Rendering microposts

我們計畫在使用者的資料頁面(show.html.erb)顯示使用者的 microposts,還要顯示使用者發佈了多少篇 microposts,你會發現很多做法會跟之前列出使用者的方法類似。(參考 Section 9.3

雖然之後才會用到 controller,但因為馬上就會用到 view,所以先建立一個 controller:

$ rails generate controller Microposts

這節的主要目的就是要渲染使用者發布的所有 microposts,之前用過這樣的程式碼(參考 Section 9.3.5):

<ul class="users">
  <%= render @users %>
</ul>

上述程式碼會自動使用 _user.html.erb partial 渲染 @user 變數中的每個使用者。我們要定義 _micropost.html.erb partial 使用相同的技巧來渲染 microposts 的集合(collection):

<ol class="microposts">
  <%= render @microposts %>
</ol>

注意,我們使用了有序清單 ol,因為 microposts 是按照一定順序排列的。

完整的 partial 如下:

app/views/microposts/_micropost.html.erb

<li id="micropost-<%= micropost.id %>" class="clearfix">
  <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
  <span class="user"><%= link_to micropost.user.name, micropost.user %></span>
  <span class="content"><%= micropost.content %></span>
  <span class="timestamp">
    Posted <%= time_ago_in_words(micropost.created_at) %> ago.
  </span>
</li>

這邊自己在 li 加了一個 class clearfix,清除 float 造成的問題

這邊使用了 time_ago_in_words 輔助方法,這個方法的作用很明顯,之後會看到效果,另外也為每篇 micropost 定義了 CSS id:

<li id="micropost-<%= micropost.id %>">

因為說不定之後你會使用例如 JavaScript 處理單篇 micropost,因此指定 id 可以方便操縱元素。

接下來要解決顯示大量 microposts 的問題。我們可以使用在 Section 9.3.3 顯示大量使用者的方法來解決這個問題,也就是使用分頁。和之前一樣,使用的是 will_paginate 方法:

<%= will_paginate @microposts %>

如果和之前的 user 列表頁面比較,會發現使用的程式碼為:

<%= will_paginate %>

在 User controller 中,will_paginate 假設有一個 @user 實例變數存在(Section 9.3.3 提過,這個變數所屬的 class 是 ActiveRecord::Relation)。

而目前我們的情況是雖然還是在 User controller 裡,但我們要分頁顯示的是 microposts(而不是 users),所以我們要傳一個 @microposts 變數給 will_paginate 方法。

這表示我們必須在 user show action 定義一個 @microposts 的變數:

app/controllers/users_controller.rb

class UsersController < ApplicationController
  .
  .
  .
  def show
    @user = User.find(params[:id])
    @microposts = @user.microposts.paginate(page: params[:page])
  end
  .
  .
  .
end

你會發現 paginate 很聰明,甚至可以在關聯上使用,從 microposts table 裡取出每一頁要顯示的 microposts。

最後,我們要顯示使用者發布多少篇 microposts,可以使用 count 方法:

user.microposts.count

paginate 一樣,count 方法也可以在關聯上使用。count 並不會從資料庫裡取出所有 microposts 然後在取出結果的陣列上調用 length 方法,因為當 microposts 數量一多,會很沒有效率。

反之,count 會直接在資料庫裡做運算,讓資料庫計算指定的 user_id 擁有多少 microposts(所有資料庫都會對這種操作做性能優化,如果計算數量還是遇到性能瓶頸,可以使用 counter cache 來提高速度)。

現在就來把 microposts 加進個人資料頁面,注意我們使用 if @user.microposts.any 來確保使用者沒有 microposts 的話就不會顯示在頁面上:

app/views/users/show.html.erb

<% provide(:title, @user.name) %>
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <h1>
        <%= gravatar_for @user %>
        <%= @user.name %>
      </h1>
    </section>
  </aside>
  <div class="col-md-8">
    <% if @user.microposts.any? %>
      <h3>Microposts (<%= @user.microposts.count %>)</h3>
      <ol class="microposts">
        <%= render @microposts %>
      </ol>
      <%= will_paginate @microposts %>
    <% end %>
  </div>
</div>

現在可以來看一下使用者的資料頁面,不過目前什麼都沒有,因為還沒建立任何 microposts: