DIVE INTO CODE

プロのエンジニアになるために挑戦する人が、チャンスをつかめる場をつくる。

DIVE02

DIVE02のゴール

DIVE02は、DIVE1で作成したachieveに、ブログ編集機能、削除機能を作成していきます。
ブログ機能を作りながら引き続き、Railsの基礎を学びましょう。

DIVE02の完成形を確認しよう

Cloud9の注意点について

Cloud9は一定期間アクセスがないとDBMSを自動停止してしまいます。
毎日一回は、PosgreSQLサービスを起動してください。

sudo service postgresql start

編集機能を作成する

編集機能を作成するためには、以下の2つのアクションを作成する必要があります。

・ 編集する内容を入力させる(editアクション)
・ 編集された内容で、データを更新する(updateアクション)

それでは、早速編集機能を作成しましょう。

routingを設定する

まず、editアクションとupdateアクションに必要なroutingを生成します。

:pencil2: resourcesメソッドのonlyオプションにeditアクションとupdateアクションを加えましょう。

config/routes.rbにどのように記述するか考えてみましょう。
(ここで記述する方法が分からなければ、DIVE01に戻って復習しましょう。)

editアクションとupdateアクションを加えることができれば、rake routesコマンドでこのように表示されるはずです。

lasershow:~/workspace/achieve (master) $ rake routes
   Prefix Verb URI Pattern               Controller#Action
    blogs GET  /blogs(.:format)          blogs#index
          POST /blogs(.:format)          blogs#create
 new_blog GET  /blogs/new(.:format)      blogs#new
edit_blog GET  /blogs/:id/edit(.:format) blogs#edit
     blog PATCH /blogs/:id(.:format)      blogs#update
          PUT   /blogs/:id(.:format)      blogs#update
省略
edit_blog GET  /blogs/:id/edit(.:format) blogs#edit
     blog PATCH /blogs/:id(.:format)      blogs#update
          PUT   /blogs/:id(.:format)      blogs#update

editアクションとupdateアクションへのroutingが生成されているのが分かるかと思います。

:pencil2: コントローラにeditメソッドを定義しましょう。

メソッド内には何も記述しなくて大丈夫です。

editアクションのviewを編集する

次に、編集用のフォームを作成します。

:pencil2: editフォームを作成する。

app/views/blogs/new.html.erbと同じコードをapp/views/blogs/edit.html.erb にコーディングします。
フォームは、blogsテーブルに合わせて、titleとcontentの入力欄を作成することになります。

edit Viewに渡す、変数を生成する

form_forでフォームを作成することができたので、form_forに渡す変数を定義します。

newアクションでは、

1
2
3
4
5
6
7
8
9
class BlogsController < ApplicationController
  def index
    @blogs = Blog.all
  end

  def new
    @blog = Blog.new
  end
#省略

newアクションは、form_forに渡す変数を、newメソッドで作成しました。
しかし、editアクションでは、保存しているデータをデータベースから取得して、変数を作成します。

保存しているデータをデータベースから取得するためには、保存しているデータのidをeditアクションに渡す必要があります。
Railsでは、resourcesでroutingを生成する限り、editアクションへのリクエストが発生した場合、自動的に保存しているデータのidが送られるようになっています。

どのようにidが渡されているか確認する

ここからは実際にeditアクションを実行する流れを確認していきます。

まずeditアクションへのリクエストが発生したとすると、ターミナルに以下のようなログが表示されます。

Started GET "/blogs/1/edit" for 203.136.253.36 at 2016-04-09 08:13:11 +0000
Cannot render console from 203.136.253.36! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by BlogsController#edit as HTML
  Parameters: {"id"=>"1"}
  Rendered blogs/edit.html.erb within layouts/application (0.5ms)
Completed 200 OK in 38ms (Views: 36.6ms | ActiveRecord: 0.0ms)

Started GET "/blogs/1/edit"でeditアクションへのリクエストが発生しているのが分かります
その際、Parameters: {"id"=>"1"}パラメータでidが送信されてくるのがわかると思います。

editアクションでは、このParametersを利用して、保存しているデータをデータベースから取得し、変数を作成します。

保存しているデータをデータベースから取得する方法

idを利用してブログを取得する場合、findメソッドを使用します。
例えば、Blog.find(1)とした場合、id = 1のブログを取得することができます。

また、createメソッドの際も学んだように、parametersから値を取り出すためには、paramsメソッドを使用します。
params[:id]とすることで、idの値(例でいうと、1)を取り出すことができます。

:pencil2: editアクションにidをもとにデータベースから該当するデータを取得する処理を記述しましょう。

app/controllers/blogs_controller.rb

1
2
3
4
5
#省略
  def edit
    @blog = Blog.find(params[:id])
  end
#省略

paramsメソッドを使用することで、parametersの値を取得することができます。
今回は取得するパラメータが一つであるため、ストロングパラメータは使用しません。

Check Point !!

`モデル名.find(キー)` キーに一致するデータを取得することができる。

edit画面とindex画面を行き来できるようにしよう

index画面からedit画面に遷移できるようにします。
link_to メソッドを使用して、edit画面に遷移させましょう。
rake routesで確認できますが、edit画面に遷移させるためには、以下の様な引数を与える必要があります。

:pencil2: rake routes editへのroutingを確認しましょう。

edit_blog GET /blogs/:id/edit(.:format) blogs#edit

この場合:idには、具体的な値(1,2,3….)を持たせる必要があります。
つまり、prefixを使用してpathを生成する場合、

1
2
3
edit_blog_path(1)   #idが1へのroutingが生成される
edit_blog_path(2)  #idが2へのroutingが生成される
edit_blog_path(13)  #idが13へのroutingが生成される

例えば、以下のようにブログIDが1である編集画面へのリンクを作成することができます。
※id = 1の場合

1
<%= link_to "ブログを編集する", edit_blog_path(id) %>

:pencil2: index.html.erbにリンクを実装しましょう。

app/views/blogs/index.html.erb

1
2
3
4
5
6
7
8
9
<h2>ブログ一覧</h2>

<% @blogs.each do |blog| %>
  <p>タイトル:<%= blog.title %></p>
  <p>本文:<%= blog.content %></p>
  <%= link_to "ブログを編集する", edit_blog_path(blog.id) %>
<% end %>

<%= link_to "ブログを作成する", new_blog_path %>

blog.idとすることで、ブログのidを取得することができます。

また、edit画面からindex画面に戻れるようにする必要もあります。

:pencil2: link_to メソッドを実装して、edit画面からindex画面にもどれるようにしましょう。

同様に作成してみましょう。

updateアクションを作成する。

続いてupdateアクションを作成します。

routingを設定する

editアクションと同様にroutingから生成します。

:pencil2: resourcesメソッドのonlyオプションにupdateアクションを加えましょう。

updateアクションを加えることができれば、rake routesコマンドでこのように表示されるはずです。

lasershow:~/workspace/achieve (DIVE1-exam) $ rake routes
     Prefix Verb  URI Pattern               Controller#Action
      blogs GET   /blogs(.:format)          blogs#index
            POST  /blogs(.:format)          blogs#create
   new_blog GET   /blogs/new(.:format)      blogs#new
  edit_blog GET   /blogs/:id/edit(.:format) blogs#edit
       blog PATCH /blogs/:id(.:format)      blogs#update
            PUT   /blogs/:id(.:format)      blogs#update
#省略

updateアクションへのroutingが生成されているのが分かるかと思います。

:pencil2: コントローラにupdateメソッドを定義しましょう。

メソッド内には何も記述しなくて大丈夫です。

updateメソッドの中に、更新する処理を実装する

値を更新するためには、updateメソッドを使用します。(アクション名のupdateとは異なり、こちらはモデルに対してのメソッドです。)

:pencil2: updateアクションに値を更新する処理を実装しましょう。

editアクションを真似して値を取得する処理を実装してください。

app/controllers/blogs_controller.rb

1
2
3
4
5
6
省略
  def update
    @blog = ここに値を取得する処理を記述する
    @blog.update(blogs_params)
  end
省略

:pencil2: paramsメソッドとfindメソッドを使用して、ブログを変数に代入しよう。

updateメソッドを使用するためには、予め更新したいブログを取得しておく必要があります。
updateアクションもeditアクション同様にアクションが起動すると、parametersで(ブログの)idが送られてくるようになっています。

:pencil2: Findメソッドとストロングパラメータを使用してblogの値を設定しよう。

updateに引数(parametersで参照したもの)を与えると、その引数の値で更新することができます。引数の値には、ストロングパラメータを使用しましょう。

値を更新することができたらredirectメソッドでindexアクションにリダイレクトさせよう

:pencil2: redirect_toメソッドを使用して、indexアクションにリダイレクトさせよう。

createアクションを参考にすると、簡単に実装することができます。

以上で、編集機能は完成です。

削除機能を作成する

削除機能を実装するためには、削除したいブログを取得し、削除させるメソッドを使用して削除するという実装を行います。

destroyアクションのルーティングを作成しよう。

:pencil2: resourcesメソッドのonlyオプションにdestroyアクションを加えよう。

destroyアクションを加えることができれば、rake routesコマンドでこのように表示されるはずです。

DELETE /blogs/:id(.:format) blogs#destroyの部分がdestroyアクションへのルーティング定義です。

lasershow:~/workspace/achieve (DIVE1-exam) $ rake routes
     Prefix Verb  URI Pattern               Controller#Action
      blogs GET   /blogs(.:format)          blogs#index
            POST  /blogs(.:format)          blogs#create
   new_blog GET   /blogs/new(.:format)      blogs#new
  edit_blog GET   /blogs/:id/edit(.:format) blogs#edit
       blog PATCH /blogs/:id(.:format)      blogs#update
            PUT   /blogs/:id(.:format)      blogs#update
            DELETE /blogs/:id(.:format)      blogs#destroy
省略

:pencil2: コントローラにdestroyアクションを定義しよう。

アクション内には何も記述しなくて大丈夫です。

app/controllers/blogs_controller.rb

1
2
3
4
省略
  def destroy
  end
省略

:pencil2: paramsメソッドとfindメソッドを使用して、ブログを変数に代入しよう。

値を削除するためには、値を@blogに取得したのち、destroyメソッドを使用します。
(アクション名のdestroyとは異なり、こちらはモデルに対してのメソッドです。)

destroyメソッドを使用するためには、予め削除したいブログを取得しておく必要があります。
destroyアクションもedit/updateアクション同様にアクションが起動すると、parametersのidを利用してデータベースからデータを取得します。
edit/updateアクションを真似して値を取得する処理をプログラムしてください。

app/controllers/blogs_controller.rb

1
2
3
4
5
6
省略
  def destroy
    @blog = "ここに値を取得する処理を記述する"
    @blog.destroy
  end
省略

:pencil2: destroyアクション内でredirect_toメソッドを使用して、indexアクションにリダイレクトさせよう。

値を削除することができるようになったので、削除後redirectメソッドでindexアクションにリダイレクトさせましょう。
createアクションを参考にすると、簡単に実装することができるかと思います。

:pencil2: link_to メソッドを使用してdestroyアクションへのリンクを設定しよう。

destroyアクションを実行させるためには、リクエストを送る必要があります。
そこでlink_to メソッドを使用してリクエストを送信します。

link_to メソッドはデフォルトでHTTPメソッドをgetで送信するため、
destroyアクションへのリクエストはdeleteメソッドを指定して送信する必要があります。
そのためには以下のようにオプションでmethod: :deleteとすることでdelete(HTTP)と指定します。

<%= link_to 'ブログを削除する', blog_path(blog), method: :delete %>

それでは、実際に実装してください。
ブログのそれぞれに削除リンクが必要なため、eachメソッド内に実装します。

app/views/blogs/index.html.erb

1
2
3
4
5
6
7
8
9
10
<h2>ブログ一覧</h2>

<% @blogs.each do |blog| %>
  <p>タイトル:<%= blog.title %></p>
  <p>本文:<%= blog.content %></p>
  <%= link_to "ブログを編集する", edit_blog_path(blog.id) %>
  ここにdestroyアクションへのリンクを作成する
<% end %>

<%= link_to "ブログを作成する", new_blog_path %>

Check Point !!

`link_to の HTTPメソッド` はデフォルトが`get`である。

削除するときに、確認ダイアログを表示させる

現在の削除機能では、削除する際にリンクを押したらすぐに削除されてしまいます。
実際のアプリでは、誤って削除しないように確認ダイアログを表示させるのが一般的です。
そこで、link_to の data, confirmメソッドを使用します。

(例)

<%= link_to "ブログを編集する", edit_blog_path(blog.id), data: { confirm: '本当に編集していいですか?' } %>
例えば、このようにすることで、編集画面を表示させる前に確認画面を表示させることができます。

https://diveintocode.gyazo.com/e12ba2a2034cbbec24f68694304bba42

※ブラウザはChrome

:pencil2: deleteアクションへのリンクをクリックすると確認ダイアログがでるように、link_to メソッドにオプションを設定してください。

このように確認画面がでれば、成功です。

https://diveintocode.gyazo.com/af35340f8ba77f5757b29485d7c89882

フィードバックメッセージを作成する

現在、ブログを作成した後や、編集、削除した後に、ただ一覧画面に移動するだけです。
従って本当に、作成されたのか分かりにくくなっています。
そこで、作成/編集/削除した後は、メッセージを表示させて、ユーザにフィードバックするようにしましょう。

フィードバックメッセージを作成するためには、以下の実装を行う必要があります。

  • notice オプションでフィードバックメッセージを渡す
  • フィードバックメッセージを表示させる場所を作成する。

notice オプションでフィードバックメッセージを渡す

noticeオプションはredirect_toメソッドで使用することができます。

app/controllers/blogs_controller.rb

1
2
3
4
5
6
7
class BlogsController < ApplicationController
省略
  def create
    Blog.create(blogs_params)
    redirect_to blogs_path, notice: "ブログを作成しました!"
  end
省略

Viewでnoticeを表示させる

noticeオプションを使用するとリダイレクトした先のアクションでnotice変数を使用することができます。
つまり、<%= notice %>とすれば、notice変数を出力することができます。

1
2
3
4
5
6
7
8
9
10
11
12
<p><%= notice %></p>

<h2>ブログ一覧</h2>

<% @blogs.each do |blog| %>
  <p>タイトル:<%= blog.title %></p>
  <p>本文:<%= blog.content %></p>
  <%= link_to "ブログを編集する", edit_blog_path(blog.id) %>
  <%= link_to "ブログを削除する", blog_path(blog.id), method: :delete, data: { confirm: '本当に削除していいですか?'} %>
<% end %>

<%= link_to "ブログを作成する", new_blog_path %>

これで、ブログを作成すると、「ブログを作成しました!」とフラッシュメッセージがでるはずです。

https://diveintocode.gyazo.com/516fd6634ce6018bd4c8f31e6871d732

それでは、編集、削除機能が完了した際にもフラッシュメッセージがでるようにしましょう。
フラッシュメッセージを表示させる場所は、設定することができているので、redirect_toメソッドにnoticeオプションを与えるだけです。

:pencil2: 削除機能が完了したら、index画面で「ブログを削除しました!」と表示されるようにする。

今まで学んだことを元に実装しましょう。

バリデーションを追加する。

現在のブログでは、不正な値(文字数が多い、何も入力されていない)でも作成や更新ができるようになっています。
そこでバリデーション(値が妥当かどうかのチェックする処理)を実装しましょう。

:pencil2: Blogモデルにバリデーションを定義しましよう。

バリデーションはモデルに記述します。
railsでは、予めバリデーション用のメソッドが用意されていて、それをtitleやcontentなどのカラムに適用することでバリデーションを実行することができます。
以下のようにvalidates :titleとすることで、titleにバリデーションを設定することができます。
またpresence: trueとすることで、空の値で登録することを禁止します。

app/models/blog.rb

1
2
3
class Blog < ActiveRecord::Base
  validates :title, presence: true
end

Check Point !!

`validates` により値が正しいかどうか検証、チェックすることができる。

validateが行われる流れについて

validateは、saveメソッド、createメソッド、updateメソッドなどデータを保存、更新する時に実行されます。
このとき、バリデーションが成功したか、失敗したかによって、処理を分岐させる必要があります。

例えば、ブログ機能の場合、データの保存に成功した場合と失敗した場合で以下のように処理を分けます。

  • 成功 => 一覧画面へ移動して、”ブログを作成しました!”とメッセージを表示する。
  • 失敗 => 入力画面を再度表示して、エラーメッセージを出力する。

このときのプログラムは以下のようになっており、saveメソッドを実行するとvalidateが実行されます。
@blog.saveを実行するとバリデーション成功時はtrueが返り、失敗時はfalseが返ってきます。

  • @blog.save => true のとき redirect_to blogs_path, notice: "ブログを作成しました!"を実行
  • @blog.save => false のとき render action: 'new' を実行

app/controllers/blogs_controller.rb

1
2
3
4
5
6
7
8
9
10
  def create
    @blog = Blog.new(blogs_params)
    if @blog.save
      # 一覧画面へ遷移して"ブログを作成しました!"とメッセージを表示します。
      redirect_to blogs_path, notice: "ブログを作成しました!"
    else
      # 入力フォームを再描画します。
      render action: 'new'
    end
  end

renderメソッドはViewをレンダリング(描画)する処理を行います。

(例)

1
2
  def create
  end

このようなcreateアクションが実行されると、Viewはcreate.html.erbが自動的に選択されます。
しかし、renderメソッドを使用して

1
2
3
  def create
    render action: 'new'
  end

とすることで、createアクションで呼び出すViewをnew.html.erbを指定することができます。

https://diveintocode.gyazo.com/5d4959f8ca2585bfe82786d4db167980

そのため、バリデーションが失敗して、render action: 'new'が実行された場合に変数を渡すことができるように、 @blog = Blog.new(blogs_params)というようにインスタンス変数に代入しています。

Check Point !!

`redirect_to` は指定したURLにアクセスする。

`render` は指定したViewをレンダリングする。

エラーメッセージを表示させよう。

:pencil2: バリデーションエラーのメッセージを表示しましょう。

バリデーションを実装し、処理を分岐させることができました。
しかし、バリデーションでエラーが発生したことをまだユーザに伝えることができていません。
そこで、バリデーションに失敗した場合にエラーメッセージが表示するようにします。

app/views/blogs/new.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<p id="notice"><%= notice %></p>

<%= form_for(@blog) do |f| %>
  <% if @blog.errors.any? %>
    <div id="error_explanation">
      # バリデーションエラーの件数を表示します。
      <h3><%= @blog.errors.count %>件のエラーがあります。</h3>

      <ul>
      # @blog.errorsは@blogをバリデーションした際のエラー情報です。
      # full_messagesは複数のエラーメッセージを表します。
      <% @blog.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <%= f.label :title %>
  <%= f.text_field :title %>
  <br>
  <%= f.label :content %>
  <%= f.text_field :content %>
  <br>
  <%= f.submit %>
<% end %>

<%= link_to "ブログ一覧画面にもどる", blogs_path %>

form_forの中に,エラーメッセージを表示する機能を実装しました。

1
2
3
4
5
6
7
8
9
10
11
  <% if @blog.errors.any? %>
    <div id="error_explanation">
      <h3><%= @blog.errors.count %>件のエラーがあります。</h3>

      <ul>
      <% @blog.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

<% if @blog.errors.any? %>とすることで、エラーが存在している時だけに、実行するようにしています。
@blog.savesaveメソッドなどで実行されたバリデーションでエラーが発生すると、railsはレシーバにエラーを格納します。
つまり、@blog.saveに失敗した場合、どんなエラーが発生したかは、@blogに格納されることになります。

:pencil2: railsコンソールを使用して、バリデーションエラーを確認しましょう。

rails cコマンドでrailsコンソールモードにしてください。

(例)

lasershow:~/workspace/achieve (DIVE2) $ rails c
Running via Spring preloader in process 2812
Loading development environment (Rails 4.2.3)
2.3.0 :001 > @blog = Blog.new
 => #<Blog id: nil, title: nil, content: nil, created_at: nil, updated_at: nil>
2.3.0 :002 > @blog.save
   (0.3ms)  BEGIN
   (0.4ms)  ROLLBACK
 => false
2.3.0 :003 > @blog.errors
 => #<ActiveModel::Errors:0x000000047e5a28 @base=#<Blog id: nil, title: nil, content: nil, created_at: nil, updated_at: nil>, @messages={:title=>["can't be blank"]}>

@blog = Blog.newで、Blogのインスタンスを作成し、@blog変数にいれます。

@blog.saveで,saveメソッドを実行し、バリデーションを実行した上で値を保存します。

バリデーションが実行されると、エラーが発生した場合は、レシーバにエラーメッセージが格納され、errorsメソッドを使用することで格納できているか確認することができます。

@blog.errorsとした時に、@messages={:title=>["can't be blank"]}> とエラーメッセージが格納されているのが分かるかと思います。

それでは、先ほどform_forに実装したエラーメッセージ表示機能に戻りましょう。

@blog.errors.countとすることで、格納されたエラーメッセージの数をカウントしています。

1
2
3
<% @blog.errors.full_messages.each do |msg| %>
  <li><%= msg %></li>
<% end %>

とすることで、格納されたエラーメッセージをeach文で1つずつ取り出して、msgに格納し表示させています。

2.3.0 :004 > @blog.errors.full_messages
 => ["Title can't be blank"]

https://diveintocode.gyazo.com/10a0da00fdbc1ffd1beb0fe7c2baa993

Check Point !!

`railsコンソール` でバリデーション処理の動きを確認することができる。

:pencil2: updateメソッドでも、エラーメッセージが発生するようにしましょう。

createメソッド同様に、バリデーションエラーが発生したら、edit Viewに戻り、エラーメッセージが発生するようにしましょう。

これでエラーメッセージを作成することができましたが、エラーが発生するとデザインが崩れてしまいます。

:pencil2: エラーの元になっているエラー出力タグを表示させないようにしましょう。

config/application.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
require File.expand_path('../boot', __FILE__)

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Achieve
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.

    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
    # Run "rake -D time" for a list of tasks for finding time zone names#### :pencil2:efault is UTC.
    # config.time_zone = 'Central Time (US & Canada)'

    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
    # config.i18n.default_locale = :de

    # Do not swallow errors in after_commit/after_rollback callbacks.
    config.active_record.raise_in_transactional_callbacks = true

    config.action_view.field_error_proc = proc { |html_tag, instance| html_tag }
  end
end

config.action_view.field_error_proc = proc { |html_tag, instance| html_tag }を追加しています。
特に意味を知る必要はありませんが、この文を記述することでエラー出力タグを表示させないようにできます。

処理をまとめよう

Railsには、DRY(Don’t Repeat Yourself) 「同じことを繰り返さない」という設計理念があります。
現在のachieveのコードは無駄が多く、この理念に反しています。
そこでリファクタリング(改善という意味です)します。

リファクタリング (refactoring) とは、コンピュータプログラミングにおいて、プログラムの外部から見た動作を変えずにソースコードの内部構造を整理することである。また、いくつかのリファクタリング手法の総称としても使われる。ただし、十分に確立された技術とはいえず、また「リファクタリング」という言葉に厳密な定義があるわけではない。

blogs_controllerをリファクタリング

:pencil2: コントローラにおける共通処理をリファクタリングしましょう。

blogs_controllerを確認してください。
現在のeditとupdateとdestroyメソッドには、共通しているコードがあります。
@blog = Blog.find(params[:id])は、それぞれのアクションで記述されているので、共通化して管理しやすいようにしましょう。
メソッドを定義し、アクションメソッドが実行される前に、定義したメソッドが実行されるようにしましょう。

app/controllers/blogs_controller.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  def edit
    # edit, update, destroyで共通コード
    @blog = Blog.find(params[:id])
  end

  def update
    # edit, update, destroyで共通コード
    @blog = Blog.find(params[:id])

    if @blog.update(blogs_params)
      redirect_to blogs_path, notice: "ブログを更新しました!"
    else
      render action: 'edit'
    end
  end

  def destroy
    # edit, update, destroyで共通コード
    @blog = Blog.find(params[:id])
    @blog.destroy
    redirect_to blogs_path, notice: "ブログを削除しました!"
  end

まず、@blog = Blog.find(params[:id])をメソッドで定義します。

1
2
3
4
5
6
7
8
9
10
11
省略
  private
    def blogs_params
      params.require(:blog).permit(:title, :content)
    end

    # idをキーとして値を取得するメソッド
    def set_blog
      @blog = Blog.find(params[:id])
    end
end

メソッドはアクション(routing)で呼ばれるわけではないため、private句の中に定義します。
後はこのメソッドをアクションが実行される前に、実行します。

アクションのメソッドが実行されるまえに実行するためには、before_actionメソッドを実行します。

Check Point !!

`複数のアクションで共通している処理` はメソッドにまとめることができる。

1
2
3
4
5
6
7
class BlogsController < ApplicationController
  before_action :set_blog, only: [:edit, :update, :destroy]

  def index
    @blogs = Blog.all
  end
  省略

before_action :set_blogとすることで、アクションのメソッドが実行される前に、指定したメソッドを実行することができます。
また、onlyオプションを使用することで、指定されたアクションにのみbefore_action が実行されます。

Viewを共通化する

現在blogs/newとblogs/editのViewは違うファイルながらも同じコードで書かれています。
そこでパーシャル(Viewの部分テンプレート)を用いて共通のViewをリファクタリングします。

:pencil2: パーシャルを用いてリファクタリングしましょう。

Viewを共通化するためには、共通化するViewをパーシャル化し、renderメソッドで呼び出します。

まずパーシャルを作成しましょう。

views/blogsフォルダを右クリックして、_form.html.erbを作成します。

パーシャルであることを宣言するためには、ファイル名の先頭に_をつけます。

app/views/blogs/_form.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<%= form_for(@blog) do |f| %>
  <% if @blog.errors.any? %>
    <div id="error_explanation">
      <h3><%= @blog.errors.count %>件のエラーがあります。</h3>

      <ul>
      <% @blog.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <%= f.label :title %>
  <%= f.text_field :title %>
  <br>
  <%= f.label :content %>
  <%= f.text_field :content %>
  <br>
  <%= f.submit %>
<% end %>

<%= link_to "ブログ一覧画面にもどる", blogs_path %>

次は、renderメソッドを使用してパーシャルを呼び出しましょう。

app/views/new.html.erb

1
2
3
<h3>ブログを作成する</h3>

<%= render 'form' %>

これで、Viewを共通化することができます。

:pencil2: app/views/blogs/edit.html.erbでもrenderメソッドを使用してViewを共通化させましょう。

Check Point !!

`パーシャル` とはViewの共通的なコードをまとめる、部分テンプレートのことである。

Check Point !!

`リファクタリング` と冗長なコードや無駄な処理を改善することである。

DIVE02課題

・お問い合わせ機能にバリデーションを追加し、エラーが発生した場合、Viewを再描画してエラーメッセージも表示されるようにすること。

最低満たすべき、バリデーションは以下の通りです。

・ name 空白を禁止
・ email 空白を禁止
・ content 空白を禁止

・お問い合わせが投稿されたら、お問い合わせ投稿画面でフィードバックメッセージを表示させること

例
"お問い合わせありがとうございました!"

課題に取り組んでみましょう。ご質問等ありましたら無料説明会で承ります。

DIVE INTO CODEの無料説明会を開催中

プロのエンジニアになるために挑戦する人を応援します.

無料説明会はこちら