A proto-feed
雖然 micropost 表單現在可以運作了,但是使用者無法立即看到成功建立的 micropost,因為在 Home page 並不會顯示任何 microposts。雖然其實可以透過「view my profile」看到所有 microposts,不過還是不太直覺。
如果能在 Home page 直接顯示使用者的所有 microposts 列表(a feed of microposts)就好了,mockup 如下:

在 Chapter 12 我們會建立一個包含被使用者關注的其他使用者的 microposts feed。
因為每個使用者都會有 feed,所以可以在 User model 定義一個 feed 方法,尋找目前使用者發表的所有 microposts。我們會透過在 Micropost model 調用 where 方法來實現:
app/models/user.rb
class User < ActiveRecord::Base
.
.
.
# Defines a proto-feed.
# See "Following users" for the full implementation.
def feed
Micropost.where("user_id = ?", id)
end
private
.
.
.
end
其中的「?」:
Micropost.where("user_id = ?", id)
是要確保 id 的值在傳入底層的 SQL query 之前做了適當的轉義(escaped),可以避免 SQL injection 這種嚴重的安全漏洞。這裡用到的 id 屬性只是一個整數(例如,self.id 是使用者的唯一 ID),所以沒什麼危險。不過在 SQL 述句中引入變數之前做轉義是個好習慣。
細心的讀者這時候可能會注意上述程式碼的寫法實際上可以寫成:
def feed
microposts
end
我們會沿用之前的寫法,因為它涵蓋更完善的在 Chapter 12 所需的完整 feed。
為了在 sample apllication 使用 feed,我們要增加一個 @feed_items 實例變數:
@feed_items = current_user.feed.paginate(page: params[:page])
然後在 Home page 加上 feed partial。
注意,現在必須執行兩行程式碼,來判斷使用者是否登入,也就是說原本在 StaticPages controller 的 home action:
@micropost = current_user.microposts.build if logged_in?
改成:
if logged_in?
@micropost = current_user.microposts.build
@feed_items = current_user.feed.paginate(page: params[:page])
end
當使用者登入時,這兩行程式碼才會被執行。
因此我們必須在 StaticPages controller 的 home action 加上 if-end 述句:
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
if logged_in?
@micropost = current_user.microposts.build
@feed_items = current_user.feed.paginate(page: params[:page])
end
end
def help
end
def about
end
def contact
end
end
接著建立 feed partial:
app/views/shared/_feed.html.erb
<% if @feed_items.any? %>
<ol class="microposts">
<%= render @feed_items %>
</ol>
<%= will_paginate @feed_items %>
<% end %>
feed partial 使用以下的程式碼,把單篇 micropost 交給 app/views/microposts/_micropost.html.erb 的 partial 渲染:
<%= render @feed_items %>
Rails 會知道要去渲染 micropost partail,是因為 @feed_items 中的元素都是 Micropost class 的實例。所以 Rails 會在對應 resource 的 views 資料夾中尋找正確的 partial:
app/views/microposts/_micropost.html.erb
最後就把 feed partial 加進 Home page 裡:
app/views/static_pages/home.html.erb
<% if logged_in? %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<%= render 'shared/user_info' %>
</section>
<section class="micropost_form">
<%= render 'shared/micropost_form' %>
</section>
</aside>
<div class="col-md-8">
<h3>Micropost Feed</h3>
<%= render 'shared/feed' %>
</div>
</div>
<% else %>
.
.
.
<% end %>
最後 Home page 畫面如下:

現在,發布 micropost 可以按照預想的方式使用了,如下圖:

不過還有個不足,如果發布失敗,Home page 還需要一個 @feed_items 實例變數,所以提交失敗時網站無法正常執行:

最簡單的解決方式是,如果提交失敗就把 @feed_items 設為空陣列:
app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
def create
@micropost = current_user.microposts.build(micropost_params)
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
@feed_items = []
render 'static_pages/home'
end
end
def destroy
end
private
def micropost_params
params.require(:micropost).permit(:content)
end
end
但是這麼做,分頁連結就失效了,可以點擊分頁連結,看是什麼原因。