今天要完成的項目如下:
posts#create的重新導向
posts的編輯
刪除單篇post(如果要刪除多篇會開另個篇幅來介紹)
瀏覽所有的po文
先來回顧一下我們新建的posts controller:
class PostsController < ApplicationController
def show
@posts = Post.find(params[:id])
end
def new
@post = Post.new
end
def create
@post = Post.new(post_params)
end
private
def post_params
params.require(:posts).permit(:title, :content, :note, :source)
end
end
這裡如果實際打開local host會出現錯誤,
因為我們並沒有告訴rails新建完post之後該到哪個頁面去,
所以這裡我們會加上重新導向位址。
同時,我們之後會在post上面加上一些驗證,(比如說:將標題列為必填欄位)
創立post失敗的時候將會重新render new這個頁面,並且用flash給user看到錯誤訊息。
def create
@post = Post.new(post_params)
if @post.save
redirect_to @post
#redirect_to :action => "show", :id => @post
else
render :new
end
end
這裡我們不用去指定action,只要直接傳入@post這個變數rails就能夠自己找到對應的show頁面,
如果你對原本的redirect有興趣,我也將它列在下方註釋起來。
再來就是edit和update這兩個跟更新有關的action,
把它擺在new和create後面介紹是因為它們幾乎是兩兩相對應的,
不同的是edit和update要先指定id才能進行動作,
概念上也非常直觀,因為new和create是新的物件,本來還不存在;
edit和update則是對現在已經存在的物件做更動,所以當然要先找到他們才能進行動作。
def edit
@post = Post.find(params[:id])
end
def update
@post = Post.find(params[:id])
if @post.update(post_params)
redirect_to @post
else
render :edit
end
end
這裡就不再重複一遍為什麼要render :edit了。
接著就是編輯的頁面了,你會發現edit和new兩個頁面幾乎長得是一模一樣,
所以這時候我們會把重複的部分抽出來做成partial view,
這是為了減少copy and paste,
同時以這個例子來說,如果你有新增或刪除什麼欄位的話,
就只要對partial view做更動就好。
做partial的方法很簡單,就是把重複的html code一起提出來就對了:
<%= simple_form_for @post do |f| %>
<%= f.input :title %>
<%= f.input :content %>
<%= f.input :note %>
<%= f.input :source %>
<%= f.button :submit %>
<% end %>
命名的話會用底線(_)開頭,跟一般的view做區分,(否則不知道的人可能還會以為有form這個頁面)。
做好之後使用的方法也很簡單,下面以edit為例:
<h1>編輯文章</h1>
<%= render partial: "form" %>
<h1>新增文章</h1>
<%= render partial: "form" %>
再來就是index了,
這個頁面可以檢視我們目前所有的post,
def index
@posts = Post.all
end
@posts
增加搜尋或是其他條件限定的話,可以再加入,並不是index action底下就一定是ModelName.all這樣子的形式。頁面這裡會用到each這個iterator,其實相當好理解。
我們假設現在有一個叫做items的array,
裡面有許許多多的item物件,假設我們要一一印出item的名字可以這樣做:
items.each do |item|
puts item.name
end
||
裡面可以任意命名,不過習慣上我們就會娶單數的名詞,
因為我們等於是一個一個把它拿出來做的,
從do到end中間的區塊裡面,都可以借由我們設定的名字來取用物件,
到end之後就換items中的下一個,一直到取完了為止。
做到這裡我們會發現,目前為止所有的操作都是要透過網址,
如果就這樣直接上去的話,將會是一個非常可怕的網站,
試著模擬一下:
你要看文章必須要知道文章的id,也就是當你新建完post之後還要先將id記下來,
才能連到post的show頁面以及對他做編輯。
所以我們將在這裡一步步把連結給加上去。
<h1>所有文章列表</h1>
<table>
<thead>
<th>標題</th>
<th>內容</th>
<th></th>
</thead>
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= link_to post.title, post_path(post) %></td>
<td>
<%= truncate(post.content, length: 14) %>
</td>
<td>
<%= link_to "編輯", edit_post_path(post) %>
</td>
<td></td>
</tr>
<% end %>
</tbody>
</table>
這裡值得一提的是我們現在是要瀏覽每則po文大概的樣子,
一則po文的content可能會到上百字,這樣全部load出來其實蠻有礙觀瞻的,
所以我們用了一個rails內建的helper,truncate
<%= truncate(post.content, length: 14) %>
,
在第一個參數放入要truncate的字串,後面其實應該是放入一個{},裡面有許多其他的設定,
但是ruby的語法裡面是可以省略括號的,
所以這裡直接用length: 14
,如果想要客製化truncate後接上的東西可以用omission這個key。
想知道更多可以直接去看Rails的api:
http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#method-i-truncate
最後就只差刪除啦!
destroy這個action的長相是這樣:
def destroy
@post = Post.find(params[:id])
@post.destroy
end
路徑該怎麼設定呢?
我們先回顧一下bundel exec rake routes的結果:
GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#destroy
有沒有發現他們都共用post_path這個路徑,只是http method不同而已呢?
要改變這個在rails中是很簡單的事情,
只要在link_to中加上 method: :method\_type
就可以了,
link\_to "刪除", post\_path(post), method: :delete
稍微暫停一下,我們記得Rails的設計哲學之一:DRY(Don't repeat yourself)
可是卻發現有一行程式碼重複了非常多次:
@post = Post.find(params[:id])
這裡可以把他抽出來放到find_post這個private的action中,
之後再從controller裡面的before_action去指定哪幾個action要呼叫(也可以寫before_filter)。
簡單重構後的code會長這樣子:
class PostsController < ApplicationController
before_action :find_post, only: [:show, :edit, :update, :destroy]
def index
@posts = Post.all
end
def show
end
def new
@post = Post.new
end
def create
@post = Post.new(post_params)
if @post.save
redirect_to @post
else
render :new
end
end
def edit
end
def update
if @post.update(post_params)
redirect_to @post
else
render :edit
end
end
def destroy
@post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :content, :note, :source)
end
def find_post
@post = Post.find(params[:id])
end
end
下一篇將要介紹rspec,簡單測試一下我們的CRUD到底行不行,
同時也會更認識rails的restful是怎麼一回事。