前 基礎Ruby on Rails Chapter10 モデル間の関連付け/ブログ機能の追加(モデル編)
次 基礎Ruby on Rails Chapter11 ストロング・パラメータ/エラーページのカスタマイズ
会員ブログ機能の実装
ネストされたリソース
ネストされたリソースのルーティング
- Taroさんのブログ記事のようなページを作成するには、ネストされたリソースを設定する。
- MemberとEntryのように、一対多で関連付けられているモデルをコントローラで扱うときは便利。
- entriesは、membersの入れ子になっている。
resources :members do
resources :entries
end
アクション | パス | HTTPメソッド | パスを返すメソッド | パスを表す配列 |
---|---|---|---|---|
index | /members/123/entries | GET | member_entries_path(@member) | [@member,:entries] |
show | /members/123/entries/123 | GET | member_entry_path(@member,@entry) | [@member,@entry] |
new | /members/123/entries/new | GET | new_member_entries_path(@member) | [:new,@member,:entry] |
edit | /members/123/entries/456/edit | GET | edit_member_entries_path(@member,@entry) | [:edit,@member,@entry] |
create | /members/123/entries | POST | member_entries_path(@member) | [@member,:entries] |
update | /members/123/entries/456 | PATCH | member_entry_path(@member,@entry) | [@member,@entry] |
destroy | /members/123/entries/456 | DELETE | member_entry_path(@member,@entry) | [@member,@entry] |
- 会員@memberの記事一覧(index)と、編集(edit)のリンクは次のように作れる。
<%= link_to "記事一覧",[@member,:entries] %>
<%= link_to "記事の編集",[:edit,@member,@entry] %>
ルーティングの設定
- 会員ごとの記事一覧と全体の一覧ページを作るので、
resources :entries, only: [:index]
、resources :entries
を追加する。 -
/members/123/entries
と/entries
が使えるようになる。
config/routes.rb(一部)
resources :members do
get "search", on: :collection
resources :entries, only: [:index]
end
#(省略)
resources :entries
ブログ記事の一覧と表示
EntriesControllerの作成
-
bin/rails g
コマンドで、EntriesControllerを作成する。4つのアクションのテンプレートも作成する。
$ bin/rails g controller entries index show new edit
create app/controllers/entries_controller.rb
invoke erb
create app/views/entries
create app/views/entries/index.html.erb
create app/views/entries/show.html.erb
create app/views/entries/new.html.erb
create app/views/entries/edit.html.erb
- indexとshowのコントローラを実装する。
app/controllers/entries_controller.rb(一部)
def index
if params[:member_id]
# 会員IDがある場合は、その会員のブログ記事一覧
@member = Member.find(params[:member_id])
@entries = @member.entries
else
# 会員IDが無い場合は、全員のブログ記事一覧
@entries = Entry.all
end
# ログイン中の会員が見られる記事に絞る。ページネーションも追加。
@entries = @entries.readable_for(current_member).order(posted_at: :desc).page(params[:page]).per(3)
end
def show
# ログイン中の会員が見られる記事のみ。idはブログ記事の主キー
@entry = Entry.readable_for(current_member).find(params[:id])
end
記事の一覧表示
- 一覧表示のビューを作成する。
- member指定かどうかで、タイトルを変える。
- 新規作成(new)、もっと読む(show)を追加する。
app/views/entries/index.html.erb
<% @page_title = @member ? @member.name + "さんのブログ" : "会員のブログ" %>
<h1><%= @page_title %></h1>
<% if current_member %>
<div class="toolbar"><%= link_to "ブログ記事の作成", :new_entry %></div>
<% end %>
<% if @entries.present? %>
<% @entries.each do |entry| %>
<h2><%= entry.title %></h2>
<p><%= truncate(entry.body, length: 80) %><%= link_to "もっと読む", entry %></p>
<%= render "footer", entry: entry %>
<% end %>
<%= paginate @entries %>
<% else %>
<p>記事がありません。</p>
<% end %>
- フッター用の部分テンプレートを作成する。
- 編集(edit)、削除(deatroy)を追加する。
app/views/entries/_footer.html.erb
<ul class="entry-footer">
<% if current_member %>
<li><%= Entry.status_text(entry.status) %></li>
<% if current_member == entry.author %>
<%= menu_link_to "編集", [:edit, entry] %>
<%= menu_link_to "削除", entry, method: :delete, data: { confirm: "本当に削除しますか?" } %>
<% end %>
<% end %>
<li>
by<%= link_to entry.author.name, [entry.author, :entries] %>
</li>
<li>
<%= entry.posted_at.strftime("%Y/%m/%d %H:%M") %>
</li>
</ul>
- ヘルパーメソッド
menu_link_to
を修正する。
# 第3引数は省略可
def menu_link_to(text, path, options={})
content_tag :li do
# methodが設定されているか、現在と同じリンクではない場合、リンクを有効にする。
condition = options[:method] || !current_page?(path)
link_to_if(condition, text, path, options) do
content_tag(:span, text)
end
end
end
- ブログ一覧へのリンクを追加する。
app/views/shared/_header.html.erb(一部)
<%= menu_link_to "ブログ", :entries %>
- 未ログインのブログ一覧
-
Hana
をクリックすると、Hanaさんのブログ一覧が見られる
- ログイン後のブログ一覧
記事の詳細表示
app/views/entries/show.html.erb
<% @page_title = @entry.title + " - " + @entry.author.name + "さんのブログ" %>
<h1><%= @entry.author.name %>さんのブログ</h1>
<h2><%= @entry.title %></h2>
<%= simple_format(@entry.body) %>
<%= render "footer", entry: @entry %>
- ブログの詳細画面が見られた。
サイドバーの修正
- サイドバーにブログ一覧を最新5件表示する。
app/views/shared/_sidebar.html.erb(一部)
<h2>会員のブログ</h2>
<%
# 現在ログインしている人がいればその人の記事と会員のみと公開記事、それ以外は公開記事を取得。
entries = Entry.readable_for(current_member).order(posted_at: :desc).limit(5)
%>
<ul>
<% entries.each do |entry| %>
<li>
<%= link_to entry.title, entry %>
by<%= link_to entry.author.name, [entry.author, :entries] %>
</li>
<% end %>
</ul>
- サイドバーにブログ一覧が表示された。
記事の作成、更新、削除
newアクションとeditアクション
- 新規は、posted_atに現在日付を初期値として渡して、画面上に入れる。
- 更新画面は、ログインメンバーのentriesを取得してfindしている。idの記事が見つからなければエラー。
app/controllers/entries_controller.rb(一部)
def new
@entry = Entry.new(posted_at: Time.current)
end
def edit
# current_memberからentriesを取得している。一対多の関係があるため。
@entry = current_member.entries.find(params[:id])
end
記事の編集フォーム
- newとedit、そして入力の部分テンプレートを作成します。
app/views/entries/index.html.erb
<% @page_title = "ブログ記事の新規作成" %>
<h1><%= @entry.title %></h1>
<%= form_for @entry do |form| %>
<%= render "form", form: form %>
<div><%= form.submit %></div>
<% end %>
app/views/entries/edit.html.erb
<% @page_title = "ブログ記事の編集" %>
<h1><%= @entry.title %></h1>
<div class="toolbar"><%= link_to "記事の表示に戻る", @entry %></div>
<%= form_for @entry do |form| %>
<%= render "form", form: form %>
<div><%= form.submit %></div>
<% end %>
app/views/entries/_form.html.erb
<%= render "shared/errors" %>
<table class="attr">
<tr>
<th width="80"><%= form.label :title %></th>
<td><%= form.text_field :title, size: 50 %></td>
</tr>
<tr>
<th><%= form.label :body %></th>
<td><%= form.text_area :body, rows: 10, cols: 45 %></td>
</tr>
<tr>
<th><%= form.label :posted_at, for: "entry_posted_at_li" %></th>
<td><%= form.datetime_select :posted_at, start_year: 2000, end_year: Time.current.year + 1, use_month_numbers: true %></td>
</tr>
<tr>
<th><%= form.label :status %></th>
<td><%= form.select :status, Entry.status_options %></td>
</tr>
</table>
記事の追加と更新、削除
- 記事の新規保存、更新保存、削除を作成する。
def create
@entry = Entry.new(params[:entry])
@entry.author = current_member
if @entry.save
redirect_to @entry, notice: "記事を作成しました。"
else
render "new"
end
end
def update
@entry = current_member.entries.find(params[:id])
@entry.assign_attributes(params[:entry])
if @entry.save
redirect_to @entry, notice: "記事を更新しました。"
else
render "edit"
end
end
def destroy
@entry = current_member.entries.find(params[:id])
@entry.destroy
redirect_to @entry, notice: "記事を削除しました。"
end
- 自由にブログを新規作成、編集、削除ができるようになった。
- 一対多の関連付けで、ブログを作成することができた。