Edited at

基礎Ruby on Rails Chapter10 モデル間の関連付け/ブログ機能の追加(コントローラ/ビュー編)

基礎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 %>



  • 未ログインのブログ一覧

image.png



  • Hanaをクリックすると、Hanaさんのブログ一覧が見られる

image.png


  • ログイン後のブログ一覧

image.png


記事の詳細表示


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 %>



  • ブログの詳細画面が見られた。

image.png


サイドバーの修正


  • サイドバーにブログ一覧を最新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>



  • サイドバーにブログ一覧が表示された。

image.png


記事の作成、更新、削除


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


  • 自由にブログを新規作成、編集、削除ができるようになった。

image.png


  • 一対多の関連付けで、ブログを作成することができた。

参考

改訂4版 基礎 Ruby on Rails (IMPRESS KISO SERIES)