もくじ
さて、引き続き電話帳アプリの完成に向けて邁進しましょう。
前回は電話帳の一覧画面を作りましたが、まだ誰も登録していないので当然何も表示されません。
なので電話番号や名前を登録できるように、新規登録画面を作ります。
スポンサーリンク
新規登録画面のルーティングを確認する
まず現在ルーター(routes.rb)にて定義されているルーティングを確認しましょう。
コンソールで(カレントディレクトリをアプリのルートディレクトリにした状態で)、
1 | $ rails routes |
とコマンドを叩くと、以下のように現在設定されているルーティングが一覧で表示されます。
1 2 3 4 5 6 7 8 9 | Prefix Verb URI Pattern Controller#Action members GET /members(.:format) members#index POST /members(.:format) members#create new_member GET /members/new(.:format) members#new edit_member GET /members/:id/edit(.:format) members#edit member GET /members/:id(.:format) members#show PATCH /members/:id(.:format) members#update PUT /members/:id(.:format) members#update DELETE /members/:id(.:format) members#destroy |
4行目の/members/newというのが新規登録画面に当たります。
つまりhttp://○○○○/members/newにアクセスすると、membersリソースを新規登録する為のページが表示される状態です。
とは言え、このページを表示する為の処理を何も書いていないので、まだ真っ白です。作っていきましょう。
スポンサーリンク
newアクションを定義する
ルーティングを見たら分かるように、/members/newを表示するためのController#Actionはmembers#newになっていますね。
つまり、http://○○○○/members/newにアクセスがあると、membersコントローラーのnewアクションが呼ばれるということです。
なので、membersコントローラーにnewアクション(メソッド)を追加して、その処理を書きます。
1 2 3 4 5 6 7 8 9 | class MembersController < ApplicationController def index @members = Member.all end def new @member = Member.new end end |
@memberというテンプレート変数に、Member.newを代入しています。
Member.newというのは、単にMemberオブジェクトを作っているだけです。引数なしでオブジェクトを作ると、データの入っていないMemberオブジェクトが出来上がります。
データの入っていないMemberオブジェクトというのは、要は枠だけあって中身が空っぽの状態です。
Memberモデルを作る際に、叩いたコマンドを覚えていますでしょうか?
参考Railsのモデル、マイグレーションファイル、スキーマファイルについて
こんなコマンドでした。↓
1 | $ rails g model member name:string yomi:string phone:string |
このコマンドによって作られたのが、Memberクラスです。結果的に、Memberクラスはname、yomi、phoneの3つのパラメータを持っています。
Member.newで作られるのは、それらのパラメータが全て空っぽの状態のオブジェクトだということです。
その空っぽのMemberオブジェクトを@memberというテンプレート変数に代入して、これをビューで使います。
form_forを使って、登録フォームを作る
では、/members/newで表示するビューを作りましょう。ビューのファイルは手動で作ります。
画面右のファイルツリーで、/app/views/membersを右クリックしてNew Fileをクリックすると新しいファイルが作られますので、そのファイルの名前をnew.html.erbとして下さい。
membersコントローラー内のnewアクションで呼び出されるのは、/views/members/new.html.erbです。
つまり、あるコントローラー#アクションによって表示したいビューを作るには、viewsディレクトリ下に/コントローラー名/アクション名.html.erbというファイル名のビューを用意すればOKです。
愛想も何もないですが、こんな感じの新規登録フォームを作ります。↓
もしこのフォームを普通にhtmlとして書こうと思うと、以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <form class="new_member" id="new_member" action="/members" accept-charset="UTF-8" method="post"> <p> <label for="member_name">Name</label> <input type="text" name="member[name]" id="member_name" /> </p> <p> <label for="member_yomi">Yomi</label> <input type="text" name="member[yomi]" id="member_yomi" /> </p> <p> <label for="member_phone">Phone</label> <input type="text" name="member[phone]" id="member_phone" /> </p> <input type="submit" name="commit" value="Create Member" data-disable-with="Create Member" /> </form> |
これをform_forメソッドを使って、サクっと作ります。
form_forはモデルと紐づくデータに対する入力フォームを作る為に用意されている仕組みです。違う言い方をするなら、membersテーブルにデータを格納するためのフォームです。
まず、form_forメソッドに@member(空っぽのMemberオブジェクト)とdoブロックを渡します。
1 2 3 | <%= form_for @member do |f| %> <% end %> |
注:form_forを囲うタグは、<%= %>で、endを囲う方のタグは<% %>です。
この間(doブロックの中)に、formの中身を書いていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <%= form_for @member do |f| %> <p> <%= f.label :name %> <%= f.text_field :name %> </p> <p> <%= f.label :yomi %> <%= f.text_field :yomi %> </p> <p> <%= f.label :phone %> <%= f.text_field :phone %> </p> <%= f.submit %> <% end %> |
※doブロックについてよく分からない人は、Rubyのブロック、ブロック引数、Proc、yieldをまとめてみたを読んでみてください。
変数fには空っぽのMemberオブジェクトが入った状態です。
1 | f.label :カラム名 |
で、Memberモデルが持つカラム(パラメータ)についてのlabelタグを出力しています。
例えば、
1 | <%= f.label :name %> |
とすると、
1 | <label for="member_name">Name</label> |
というタグがhtmlとして出力されます。
1 | f.text_field :カラム名 |
も同様に、そのカラムについてのinputタグ(type=text)が出力されます。
例えば、
1 | <%= f.text_field :name %> |
で、
1 | <input type="text" name="member[name]" id="member_name" /> |
が出力されます。
submitボタンは、
1 | <%= f.submit %> |
とすることで、
1 | <input type="submit" name="commit" value="Create Member" data-disable-with="Create Member" /> |
が、出力されます。
つまり、new.html.erbを以下のようにすることで、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <%= form_for @member do |f| %> <p> <%= f.label :name %> <%= f.text_field :name %> </p> <p> <%= f.label :yomi %> <%= f.text_field :yomi %> </p> <p> <%= f.label :phone %> <%= f.text_field :phone %> </p> <%= f.submit %> <% end %> |
http://○○○○/members/newには、以下のhtmlが出力されることになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <form class="new_member" id="new_member" action="/members" accept-charset="UTF-8" method="post"> <input name="utf8" type="hidden" value="✓" /> <input type="hidden" name="authenticity_token" value="VmTtcqN+1BHXRG6Nwgjqn4uen95ijo3meEWq0sMRUqmqIixNulMgqKwRzO6rq/2hB+CuRGHA6budRZQUhOicFw==" /> <p> <label for="member_name">Name</label> <input type="text" name="member[name]" id="member_name" /> </p> <p> <label for="member_yomi">Yomi</label> <input type="text" name="member[yomi]" id="member_yomi" /> </p> <p> <label for="member_phone">Phone</label> <input type="text" name="member[phone]" id="member_phone" /> </p> <input type="submit" name="commit" value="Create Member" data-disable-with="Create Member" /> |
※2行目、3行目のinputタグは自動的に出力されるようになっています。
このようにform_forを使えば、モデルに対する登録フォームを簡単に(変なスペルミスを心配すること無く)htmlとして出力することができます。
ラベルやsubmitボタンの文字を変える
普通に上記の通りやると、以下のようなラベル、submitボタンが出力されるわけですが、
これを簡単に違う文字に変えることもできます。全部日本語にして分かりやすくしましょう。
以下のように、それぞれのメソッドの引数に文字列を渡してやるだけです。
1 | <%= f.label :name, "名前" %> |
Java脳が染み付いている人は、メソッドの( )を省略する書き方に慣れていないので、なんとなく変な感じですが、( )を省略せずに書くとこうなります。↓
1 | <%= f.label(:name, "名前") %> |
f.labelの第一引数に:nameを、第二引数に“名前”を渡している状態ですね。
submitボタンの文字を変えるのも同じように引数に渡すだけです。
1 | <%= f.submit "登録" %> |
ただし、この場合第一引数なのでもちろんメソッド名(submit)の後ろにコンマは要りません。
全部、日本語にしようと思ったら、以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <%= form_for @member do |f| %> <p> <%= f.label :name, "名前" %> <%= f.text_field :name %> </p> <p> <%= f.label :yomi, "ヨミガナ" %> <%= f.text_field :yomi %> </p> <p> <%= f.label :phone, "電話番号" %> <%= f.text_field :phone %> </p> <%= f.submit "登録" %> <% end %> |
これでhttp://○○○○/members/newを開くと、こうなります。↓
ここではごく基本的な使い方しかしていませんが、form_forを上手く使うことで、非常に柔軟にそのモデルに関する登録フォームをhtmlとして出力することが可能です。
送信された情報をデータベースに書き込む
フォームをhtmlとして出力することはできましたが、まだこれだけではフォームとして機能しません。
submitボタンを押すと、フォームに入力した情報がサーバーへ送られるわけですが、その受け取った情報をデータベースに書き込むという処理を実装する必要があります。
フォームに入力された情報は一体、どこに送信されるのか?出力されたformタグをよく見てみましょう。
1 | <form class="new_member" id="new_member" action="/members" accept-charset="UTF-8" method="post"> |
actionの値(送信先)が/members、methodがpostになっているのが分かると思います。
注:formタグのaction属性は、このformをsubmitした際のリクエスト先urlを指定しています。コントローラーのactionとは無関係です。たまたま名前が同じなだけ。
つまりこのフォームのsubmitボタンを押すと、フォームに入力された値がhttp://○○○○/membersへpostメソッドで送られるということです。
ルーティングを確認してみましょう。
1 2 3 4 5 6 7 8 9 | Prefix Verb URI Pattern Controller#Action members GET /members(.:format) members#index POST /members(.:format) members#create new_member GET /members/new(.:format) members#new edit_member GET /members/:id/edit(.:format) members#edit member GET /members/:id(.:format) members#show PATCH /members/:id(.:format) members#update PUT /members/:id(.:format) members#update DELETE /members/:id(.:format) members#destroy |
3行目を見て下さい。POSTメソッドでの/membersへのリクエストというのは既にルーターにて想定済みです。右端のController#Actionを見てみると、membersコントローラーのcreateアクションが呼ばれることが確認できます。
ルーターの設定を思い出して下さい。(参考:Ruby on Railsの基本中の基本 MVC + ルーターについて)
1 2 3 4 | Rails.application.routes.draw do resources :members # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end |
このresources :membersという一行によって、membersリソースに対するリクエストに一通り応えられるルーティングを自動的に生成しているんです。
リクエストというのは例えば、
- 一覧画面を見たいというリクエスト(getメソッドで/members)
- 入力画面を表示してほしいというリクエスト(getメソッドで/members/new)
- その入力情報を登録したいというリクエスト(postメソッドで/members)
といったものです。
それらのリクエストがあった際の処理の振り分け(Controller#Action)まで、自動的に設定してくれています。これがresources :membersの威力です。
もちろん、実際にどういう処理を行うかは、コントローラーにてアクションを定義することで実装する必要があります。
ルーターがコントローラーへの取り次ぎ役というのも頷けるかと思います。
というわけで、postメソッドで/membersへリクエストがあった場合、createアクションが呼ばれるようになっています。
では実際に、その送られてきた情報をデータベースへと書き込むべく、createアクションを実装しましょう。
app/controllers/members_controller.erbを開いて下さい。
以下のようにcreateアクション(メソッド)を追加しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 | class MembersController < ApplicationController def index @members = Member.all end def new @member = Member.new end def create end end |
送られてきた情報をデータベースに書き込むために、createアクションでは以下の処理を行います。
- postメソッドで送られてきた情報を取得する
- その情報を持ったMemberオブジェクトを作る
- 保存処理をする
例えば、
1 | Member.new({"name"=>"本田圭佑", "yomi"=>"ホンダケイスケ", "phone"=>"01011112222"}) |
このようにMember.newの引数に、然るべき情報を持つ連想配列を渡すことで、その情報を持ったMemberオブジェクトが作られます。
そのオブジェクトに対して、saveメソッドを実行することでそのオブジェクトが持つ情報がmembersテーブルに追加されます。
1 2 | @member = Member.new({"name"=>"本田圭佑", "yomi"=>"ホンダケイスケ", "phone"=>"01011112222"}) @member.save |
まずはpostメソッドにより送られてきた情報を取得する必要があります。postメソッドはパラメータと値をセットにした状態で情報を送ってきます。
例えば、以下のように入力した状態でsubmitボタン(登録)をクリックすると、
formに入力された情報が、以下のような連想配列(Hash)の形でサーバーへと送られてきます。配列が入れ子になっているのが分かるかと思います。
1 2 3 4 5 6 7 8 9 10 | { "utf8"=>"✓", "authenticity_token"=>"5KKX5KxS2S+0szZeRAvnQu61LqLcgxhnNA43DXT+t9d00T3kIoW/1eO7v0660N1T9GqWQwtecQUMeGfhp5edrw==", "member"=>{ "name"=>"本田圭佑", "yomi"=>"ホンダケイスケ", "phone"=>"01011112222" }, "commit"=>"登録" } |
controllerにて、この送られてきた連想配列を丸ごと取得できるのがparamsメソッドです。
例えば、
1 2 3 | def create parameter = params end |
とやるだけで、上記のパラメータを全て連想配列として、変数parameterに代入することができます。
しかし見ての通り、この送られてきた連想配列にはMemberオブジェクトを作るためのパラメータ(member部分)以外にもいろんな情報が入っていますので、この連想配列を丸ごとMember.newの引数に入れるわけにはいきません。
なので以下のようにして、paramsメソッドで取得した連想配列の中のmember部分だけをMember.newの引数へと渡します(変数に代入せずに直接引数に入れています)。
1 2 3 | def create @member = Member.new(params[:member]) end |
こうすることで、結果的に、
1 | {"name"=>"本田圭佑", "yomi"=>"ホンダケイスケ", "phone"=>"01011112222"} |
↑この部分だけを、Member.newの引数に渡すことになります。こうして出来たMemberオブジェクトを@memberへと代入しています。
そして、@memberをsaveします。
1 2 3 4 | def create @member = Member.new(params[:member]) @member.save end |
これで、登録フォームから送られてきた情報がmembersテーブルへと書き込まれました。
と、言いたいところなのですが、Rails4.0以降では、params[:member]で取得した連想配列をモデルの引数に入れるのはご法度になってしまいました。
理由はセキュリティ上の問題です。悪意のあるコードをデータベースに放り込んでしまわないように、パラメータをチェックする処理が必要になりました。
こうやります。
1 2 3 4 | def create @member = Member.new(params.require(:member).permit(:name, :yomi, :phone)) @member.save end |
ちょっとややこしいですが、要はparamsメソッドで取得した連想配列のうち、memberモデルに関するnameパラメータ、yomiパラメータ、phoneパラメータの値のみを取得しているわけです。それ以外の変なパラメータは一切受け付けません。
以上で、送られてきた情報をデータベースへ書き込む処理が完成しました。
処理後のリダイレクト
データベースへの書き込み処理は完了しましたが、このままではデータベースに書き込んだ後、画面に何を表示するのか?が抜けている為、エラーになります。
なので処理が完了した後、どのページに遷移するのかをコントローラーにて指定する必要があります。
ここでは登録が完了したら、一覧画面(http://○○○○/members)へとリダイレクトするようにします。
redirect_toメソッドを使います。
1 2 3 4 5 | def create @member = Member.new(params.require(:member).permit(:name, :yomi, :phone)) @member.save redirect_to "/members" end |
↑このようにredirect_toメソッドの引数に、リダイレクト先のurlを文字列で渡せばいいのですが、あまりこういう書き方はしないようです。
基本的には、以下のようにリダイレクト先を指定します。
1 2 3 4 5 | def create @member = Member.new(params.require(:member).permit(:name, :yomi, :phone)) @member.save redirect_to members_path end |
members_pathというのは、何か?ルーティングを見てみましょう。
1 2 3 4 5 6 7 8 9 | Prefix Verb URI Pattern Controller#Action members GET /members(.:format) members#index POST /members(.:format) members#create new_member GET /members/new(.:format) members#new edit_member GET /members/:id/edit(.:format) members#edit member GET /members/:id(.:format) members#show PATCH /members/:id(.:format) members#update PUT /members/:id(.:format) members#update DELETE /members/:id(.:format) members#destroy |
この左端(Prefixの項目)に表示されている文字列の後ろに_pathを付けたものが、それぞれのページのurlを表すpathになります。
空欄の行がありますが、これは上の行と同じpathということです(書くのを省略しているだけ)。もちろん、リクエスト先のpathは同じでもhttpメソッドが違えば、その対応は違って然るべきです。その為に、アクションはそれぞれ違います。
※memberのPATCHとPUTは同じアクション(update)になっていますが、これはRailsの都合(仕様)でこうなっているだけで特に気にすることはないです。
つまり、members_pathというのは一覧ページ(http://○○○○/members)へのリクエストを表すpathということになります。
/membersへのリクエストは、見ての通り、
- getメソッドなら一覧ページ表示
- postメソッドなら、新規登録処理(createアクション)
ということになります。
つまり、本来ならpathを指定するだけでなくhttpメソッドも指定しなければどちらへのリダイレクトなのか分からないのですが、実はredirect_toメソッドはデフォルトでgetメソッドによるリダイレクトが行われるようになっています。
なので、
1 | redirect_to members_path |
とするだけで、/membersへのgetメソッドによるリダイレクトが行われるわけです。
※httpメソッドを指定する方法はまたの機会に。
コンソールにてrails routesコマンドでルーティングを確認することも出来ますが、ブラウザでhttp://○○○○/routesにアクセスすることで、ルーティングを確認することもできます。
ブラウザにてルーティングを確認すると、_pathも含めた形で確認することができます。
これで、新規登録画面からsubmitボタンを押すことで、データベースにその情報を書き込み、一覧画面にリダイレクトするという処理が完了しました。
実際に新規登録フォームから登録した情報が、一覧画面(http://○○○○/members)にきちんと出力されるかどうか試してみてください。
ちゃんと登録した情報が一覧画面に表示されれば、データベースへの書き込みが上手くいっている証拠です。
link_toメソッドでリンクテキストを出力する
最後に、一覧画面から新規登録画面へのリンクを付けておきましょう。
リンクテキスト(aタグ)を出力するには、link_toメソッドを使います。
link_toメソッドは、第一引数にアンカーテキスト、第二引数に遷移先urlを指定するだけです。遷移先urlの指定には上述したようにpathを使います。
例えば、
1 | <%= link_to "新規登録", new_member_path %> |
とすることで、
1 | <a href="/members/new">新規登録</a> |
というhtmlが出力されます。
では、一覧画面のビューであるapp/views/members/index.html.erbを開いて、link_toメソッドを追記しましょう。
1 2 3 4 5 6 7 | <% @members.each do |member|%> <p><%= member.yomi %></p> <p><%= member.name %></p> <p><%= member.phone %></p> <% end %> <%= link_to "新規登録", new_member_path %> |
これで、一覧表示のループの後に新規登録画面へのリンクテキストが出力されます。
↑こんな感じですね。
同様に、新規登録画面から戻るボタンも付けてみましょう。
app/views/members/new.html.erbを開いて以下のように、追記しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <%= form_for @member do |f| %> <p> <%= f.label(:name, "名前") %> <%= f.text_field :name %> </p> <p> <%= f.label :yomi, "ヨミガナ" %> <%= f.text_field :yomi %> </p> <p> <%= f.label :phone, "電話番号" %> <%= f.text_field :phone %> </p> <%= f.submit "登録" %> <% end %> <%= link_to "戻る", members_path %> |
こんな感じで、お互いのページ同士、普通に行き来できるようになりました。
次回は一度登録した情報を修正する機能を実装します。
めっちゃ分かりやすかったです!特にparamsのところなんて、スクールの教材の数億倍分かりやすくて、謎がとけて目からウロコでした。このような記事を書いてくださって有難うございます!
by Ayaka 2019年4月2日 11:03 PM
大変参考になりました!
ありがとうございます。
by rails_man 2019年10月1日 3:06 PM
Rais初学者です。
今まで見てきたどの記事よりも分かりやすかったです!
by Jerryfish3 2020年1月25日 12:59 PM
getやpostがどこで指定されているのかよく分かりました。
結果として自動的にセットされているのですが、それがどこでどう自動でセットされるのかはっきり説明していたので、頭の中がモヤモヤしていたのがスッキリしました。
by yoshi 2020年4月18日 1:35 PM
Railsチュートリアルの5000倍わかりやすい!
ありがとうございます。
by kakudaisuke 2021年2月25日 10:34 AM
めちゃくちゃ分かりやすくて感動しました。
初学者の目線で書いてくれているのが助かりました。
by TT 2021年4月24日 6:03 PM