JavaからのRuby on Rails入門

リソースの編集画面を作って、編集処理を実装する

calendar

reload

 Ruby on Railsで電話帳アプリを作っています。
 前回までで、電話帳リストの一覧画面新規登録画面を作りました。

 今回は、既に登録しているデータを編集する画面、及びその機能を作っていきます。

スポンサーリンク

編集画面のルーティングを確認する

 まずはルーティングを確認しましょう。
 コンソールにて、カレントディレクトリをアプリのルートにした状態で、

とコマンドを打つと、コンソールにルーティングの一覧が表示されます。
$rake routesでも可

 5行目が編集画面に割り当てられているルーティングです。
 urlは、
http://○○○○/members/:id/edit
となっています。
 :idというところに、編集したいリソースのidが入ります。例えば、http://○○○○/members/1/editで、「ID番号1のmembersリソースを編集したい」という意味のリクエストになります。

 これから、そのリクエストに応じた編集画面を表示できるように作っていきましょう。

 ルーティングを確認すれば分かるように、/members/:id/editというリクエストに対する処理を定義するのが、membersコントローラーのeditアクションです。

editアクションを定義する

 membersコントローラー内にeditアクションを追加しましょう。

 /app/controllers/members_controller.rbを開いて下さい。

 以下のように、editアクションを追加します。

 前回作った新規登録画面なら空っぽの入力フォームでいいですが、編集画面には、当然、既に登録されているデータを表示する必要があります。

 ↑こんな感じです。
 その為には、リクエストに含まれるIDを持つリソースのデータをモデルに用意させる必要があります。

 例えば、ID番号1のリソースのデータをデータベースから引っ張ってくるには、以下のようにします。

 Memberクラスのクラスメソッドであるfindメソッドの引数にそのID番号を渡すだけです。これでID番号1のリソース情報を携えたMemberオブジェクトが出来上がり、それを@memberに代入しています。

 このテンプレート変数@memberをビューから参照することで、そのデータを使って、編集画面を作るわけですが、当然ながらリクエストに含まれるIDに対して動的にデータを用意しなければなりません。

 そのためにはリクエストからidを取得して、処理する必要があります。

 とりあえず、この状態で、http://○○○○/members/1/editにアクセスしてみると、

 テストサーバーを立ち上げるコマンド。

 まだeditのビューが未定義なので、このようブラウザの表示はエラーになるのですが、

 Cloud9のコンソールを見てみると、このリクエストに付随して送られているパラメータを確認することができます。

 ↑このように、
/members/1/editへのリクエストには、
{“id”=>”1”}
というパラメータが付随しているのが分かります。

 このパラメータを、コントローラーにて取得する為のメソッドが、前回も説明したparamsメソッドです。

 paramsメソッドにより取得したidをfindメソッドの引数に渡すことで動的な処理が可能になります。

 paramsメソッドは送られてくるパラメータを、連想配列の形で取得します。

 例えば、{“id”=>”1”}というパラメータが送られている場合、paramsメソッドによって以下のように連想配列を取得します。

 その連想配列のキーを指定することで得られるのが、そのです。

 変数を用意せず、paramsメソッドによって取得した連想配列に対し、そのままキーを指定しているのが以下の記述です。同じことです。

 これで、
http://○○○○/members/:id/editというリクエストに対して、その:idによって動的にデータを用意する仕組みができました。

@memberというテンプレート変数にそのデータを代入してあるので、あとは、そのデータを使って、編集画面を作ります。

編集画面のビューを作る

 では編集画面のビューを作っていきましょう。

 /app/views/membersディレクトリ内に、edit.html.erbというファイルを新規作成して下さい。

 このedit.html.erbの中で修正画面のビューを定義するわけですが、修正画面のビューでは、登録した情報を修正する為のフォームを表示します。

 このフォーム自体は、前回作った新規登録フォームと全く同じです。ただ、入力欄が空っぽなのか、あらかじめデータが表示されているかの違いです。

 実は、こういう場合、新規登録画面のビュー(new.html.erb)をそのままを使いまわすことができます。

 このform部分(戻るリンク以外)をそのまま使いまわします。

 と言っても単にコピペするだけでは芸がありません。というかコピペでしてしまうと、今後もし、フォームに関して何らかの変更があった際などに修正するのが面倒です。

 なので、フォーム部分を1つの部品として作り、それをnew.html.erbからも、edit.html.erbからも読み込んで使うようにします。

 まずは、そのフォーム部分を、部分テンプレートとして作りましょう。

 /app/views/membersディレクトリに、_form.html.erbというファイルを新規作成して下さい。

 この_form.html.erbに、new.html.erbのform部分を全てコピペします。

 この部分テンプレート(_form.html.erb)をビューの中で読み込むには、renderメソッドを使います。

 new.html.erbの中でrenderメソッドを使って、_form.html.erbを読み込みます。
※新規登録画面だと分かるように、「新規登録」というヘッダーを付けておきましょう。

 edit.html.erbの中でも、_form.html.erbを読み込みます。
※こちらは「修正」というヘッダーを付けておきます。

 これで、new.html.erbにも、edit.html.erbにも、_form.html.erbに書いてあるフォームが表示されることになります。

 部分テンプレート用のerbファイルは、それと分かるようにファイル名の一文字目をアンダースコアにする必要があります。
○ _form.html.erb
×  form.html.erb

 そして、他のビューから部分テンプレートを読み込む際には、アンダースコアを省いたファイル名を指定します。
○ <%= render "form" %>
×  <%= render "_form" %>

 ただし、全く同じもの(_form.html.erb)を読み込んでいるのにも関わらず、new.html.erbでは空っぽの入力フォームが表示され、edit.html.erbではあらかじめ入力欄に然るべきデータが入った状態の入力フォームが表示されます。

 なぜそんなことになるのか?

 _form.html.erbをもう一度よく見て下さい。

 この入力フォームには、form_forメソッドの引数に渡したモデルオブジェクト(Memberオブジェクト)が持つデータがそれぞれの入力欄に表示されるようになっています。

 新規作成画面(newアクション)の場合、コントローラーにて空っぽのMemberオブジェクトを用意した上で、@memberに仕込んでいる為、それぞれの入力欄は空っぽになります。

 編集画面(editアクション)の場合は、コントローラーにて、指定されたIDのMemberオブジェクトを@memberに代入している為、それぞれの入力欄にはそのMemberオブジェクトが持つパラメータが入った状態で表示されます。

 ビューに関しては全く同じソースコードでも、コントローラーが用意する値(テンプレート変数)によって実際、表示されるHTMLは変わってくるわけです。

フォームの送信先

 さて、新規登録画面も、編集画面も_form.html.erbを読み込むことで、入力フォームを表示することができました。

 実は、それぞれの入力フォームには、入力欄のデータの初期状態だけではなく決定的に違うところがもう一箇所あります。それは登録ボタンを押した際のデータの送信先です。

 送信先はformタグのaction属性で指定しますので、それぞれの吐き出されたソースコード(HTML)を確認してみましょう。
注)formタグのaction属性と、Railsのアクションは一切関係ありませんので混乱しないように。

 ご覧のように新規登録画面のformタグのactionは、/membersとなっています。そしてmethodがpostになっています。

 一方、編集画面のformタグのaction属性は、/members/1となっています。1というのは編集するリソースのIDを表しています。methodはpostになっていますが、3行目のinputタグで_methodというキーでpatchという値を設定しているのが分かると思います。これは、PATCHによるHTTPリクエストを行うことを指定しています。

 本来なら、

とできればいいのですが、formタグのmethod属性で指定できるのはGETかPOSTのどちらのみとなっている為、PATCHによるHTTPリクエストを実現するにはこういった書き方になってしまうようです。

 この辺を深く知りたい人は、Webを支える技術 -HTTP、URI、HTML、そしてRESTを読んでみてください。

 要するに、新規登録画面からのデータ送信は、POSTメソッドによる/membersへのリクエストであり、編集画面からのデータ送信は、PATCHメソッドによる/members/:idへのリクエストということになります。

 まとめるとこういうことです。↓

action(送信先) メソッド
新規登録画面から /members POST
編集画面から /members/:id PATCH

 これをふまえてルーティングを確認してみましょう。

 3行目が新規作成画面のフォームから発信されるPOSTメソッドによる/membersへのリクエストに対する対応で、そのリクエストを受けたらmembersコントローラーのcreateアクションが呼ばれます。前回やりましたね。

 7行目が編集画面のフォームから発信されるPATCHメソッドによる/members/:idへのリクエストに対する対応で、updateアクションが呼ばれるように設定されています。
※8行目のPUTも、members#updateになっていますが、あまり気にしなくていいです。

 つまり、編集画面の登録ボタンを押すと、

/members/:idへとPATCHメソッドによるHTTPリクエストが送られ、そのリクエストに対する処理を定義するのがupdateアクションというわけです。

updateアクションを定義する

 では、編集画面の登録ボタンを押した際の処理を実装しましょう。

 先程ルーティングを確認した通り、登録ボタンから発せられたリクエストを受けて動くのが、updateアクションです。 
 そのリクエストを受け取った際の挙動を定義する為に、membersコントローラーにupdateアクションを追加しましょう。

 編集画面の登録ボタンを押すと、その入力欄の情報(リクエスト)がサーバーへと送られますので、updateアクションでその情報を受け取って、データベースを書き換えます。

 まず編集中のMemberオブジェクトを用意します。

 あるリソースを修正するには、そのIDを持つモデル(Memberオブジェクト)が必要になります。
※空っぽのモデルを用意してしまうと新規のIDを持つモデルを用意(新規作成)することになり、修正にはなりません。

 そして、修正フォームから送られてきたデータを使って、このモデル情報(membersテーブルの一行)を更新します。

 テーブルを更新するにはモデルクラスのインスタンスメソッドであるupdateメソッドを使います。引数に

といった連想配列を渡すことで、テーブルを更新します。
※updateアクションと、モデルインスタンスのupdateメソッドは全然別物です。

 そして、最後に一覧画面へとリダイレクトします。

 paramsメソッド、redirect_toメソッドについては、前回の解説にでてきたものなので、何をしてるのかよく分からない人は見直してみてください。

 さて、これで編集処理も完成です。

重複しているコードを整理する

 members_controller.rbを見てみると、ソースコードが重複しているところがあるのが分かると思います。

 これをちょっと整理しておきましょう。

 まずは送られてくるパラメータを取得する為の

を、member_paramsというprivateメソッドとして定義して、それを各アクションにて使いまわすようにします。

 さらに、

も重複しているので、こちらもfind_member_by_idというprivateメソッドを定義して使いまわしましょう。

 可読性、保守性が高くなるように、ソースコードの重複は可能な限り避けるようにしましょう。

編集画面へのリンクを作る

 編集画面が出来上がったので、編集画面へのリンクを貼りましょう。

 一覧画面の各memberの名前をアンカーテキストにして、そこから各々の編集画面へリンクするようにします。

 /app/views/members/index.html.erbを開いて下さい。

 現状では、普通に名前、ヨミガナ、電話番号を出力しているだけですが、以下のように、link_toメソッドを使って、名前をアンカーテキストとしたリンクを出力するようにします。

 link_toメソッドの第一引数にアンカーテキストを、第二引数にリンク先urlを渡すことで、aタグを出力しています。

  出力↓↓

 アンカーテキストはmember.nameになっているので、名前が出力されます。

 リンク先は、edit_member_path(member.id)となっています。仮にmember.idが1ならば、
"/members/1/edit"
という文字列が出力されます。

 link_toやredirect_toで遷移先を指定する際に使うmembers_pathedit_member_pathなどは、定数や変数ではなくメソッドだったんですね。
 引数にIDを渡すことで、そのIDが適当な場所に挿入された状態のurlが返ってきます。

 これでそれぞれのリソースへの編集画面へのリンクも完成しました。

 次は、リソースの削除機能を実装したいと思います。

分からないことはここで質問してみてください↓

この記事をシェアする

コメント

  • いつもためになる記事をありがとうございます。
    他サイトの記事でもなかなかうまくいきませんでしたが、このシリーズはわかりやすく順調に理解が進んでいます。

    次の記事もお待ちしてます!!

    by Shun 2017年1月10日 1:03 PM

  • >Shunさん
    誰か読んでくれている人いるのかな。。と思いながらも一生懸命書いていましたが、反応をもらえて安心しました。しかも分かりやすいとのことで非常に嬉しいです。コメントありがとうございました。

    by nobuo 2017年1月10日 1:28 PM

down コメントを残す




関連記事

書いている人

Nobuo

Nobuo

一番かんたんなJava入門というサイトを運営しています。Javaやphpは少し分かりますが、Ruby on Railsについては全く何も知らないので、このサイトにアウトプットしながら覚えていこうかと思っています。 [詳細]

folder Rubyの基本文法

more...

folder 初めてのRuby on Rails入門

more...

folder Rails忘備録

more...