道場コース2回目は投稿の作成、編集、削除をできるように機能を追加していきます。
データベースの準備
データベースのcontentカラムにバリデーション(NULL不許可 & 最大140文字)を設定します。
Postモデル(ここは単数形!)に以下の記述を追加
validates :content, {presence: true, length: {maximum: 140}}
ひとまずrails consoleからcontentの内容が空だったり、140文字を超えている場合に保存(.save)が失敗することまでを確認しておきます。
新規投稿ページの作成
ルーティングに 「posts/new」を追記します。ここでの注意点は、ルーティングは上から順に評価されるため、投稿詳細表示の”posts/:id”より上に書かないと上手くいかないこと。
get "posts/new" => "posts#new" get "posts/index" => "posts#index" get "posts/:id" => "posts#show"
共通レイアウト(application.html.erb)のヘッダー部分に「新規投稿」のリンクを追加します。
<%= link_to("新規投稿", "/posts/new") %>
個人的にエラーの原因になりがちだったのが、link_toの閉じカッコを忘れる、とか最後の”%>”を忘れるとかでした。(要注意)
新規投稿処理の作成
まずはルーティングに以下1行を追記します。ここでのポイントは、データの登録系処理なので先頭を“get”でなく“post”にすること。
post "/posts/create" => "posts#create"
次にビューを編集して投稿内容を送信できるようにします。form_tag()で指定したルートにデータをPOSTします。textareaタグのname属性(content)を指定することで、コントローラ側でparams[:content]としてアクセス可能になります。
<%= form_tag("/posts/create") do %> <textarea name="content"></textarea> <input type="submit" value="投稿"> <% end %>
最後に投稿処理です。基本的には前回コンソールで作業した内容をコントローラ側で同じように実行すればOKです。
保存完了後、投稿一覧(/posts/index)にリダイレクトするようにしておきます。
def create @post = Post.new(content: params[:content]) @post.save redirect_to("/posts/index") end
投稿成功時と失敗時それぞれのメッセージ表示を追加
Postデータにバリデーションを設定しているので、投稿失敗時のエラーメッセージを出力するようにします。
また、投稿に成功した際にもメッセージを表示するようにします。
まずはコントローラ側のcreateアクションで、@post.saveの戻り値で成功・失敗を条件分岐します。
if @post.save flash[:notice] = "投稿を作成しました" redirect_to("/posts/index") else render("posts/new") end
成功したときに一度だけメッセージをフラッシュ表示するため「flash[:notice]」にメッセージを設定し、共通レイアウト(app/views/layouts/application.html.erb)で以下のように表示します。
<% if flash[:notice] %> <div class="flash"> <%= flash[:notice] %> </div> <% end %>
失敗した際のエラーは @post の errors.full_messages に格納されるので、ビュー(new.html.erb)で次のように表示します。
<% @post.errors.full_messages.each do |error| %> <div class="form-error"> <%= error %> </div> <% end %>
なお、失敗した際の遷移先は同じ投稿画面とするので、redirect_to(“/posts/new”)としたいところですが、これだとせっかく入力した内容が消えてしまうので、アクションを実行せずに投稿画面のビューを表示するため、render(“フォルダ名/ビュー名”)を使って遷移します。
あわせて、ビュー側で @post.content を表示できるように、
<textarea name="content"><%= @post.content %></textarea>
とします。
これだけだと、(エラー時ではなく)新規投稿画面を表示した際に「@post.contentが未定義だよ」とエラーになってしまうので、newアクションの中で 「@post = Post.new」 とだけして@postを用意しておきます。
投稿編集画面の作成
ルーティングを追加します。
get "posts/:id/edit" => "posts#edit"
Postsコントローラにeditアクションを作成します。
def edit @post = Post.find_by(id: params[:id]) end
つづけてビューファイル(edit.html.erb)を作成します。(ここでtextareaタグを新規投稿ページと同じように@post.contentを初期値にセットしておきます)
投稿編集処理の作成
まずはルーティングを追加します。
post "posts/:id/update" => "posts#update"
つづけてPostsコントローラにupdateアクションを作成します。(やることは新規作成と大体同じです。newの代わり検索してデータをセットするだけ。)
def update @post = Post.find_by(id: params[:id]) @post.content = params[:content] if @post.save flash[:notice] = "投稿を編集しました" redirect_to("/posts/index") else render("posts/update") end end
ビューはこんな感じ。(新規投稿とあまり変わりませんね)
<%= form_tag("/posts/#{@post.id}/update") do %> <% @post.errors.full_messages.each do |error| %> <div class="form-error"> <%= error %> </div> <% end %> <textarea name="content"><%= @post.content %></textarea> <input type="submit" value="保存"> <% end %>
投稿削除処理の作成
まずはルーティングを作成します。
post "posts/:id/destroy" => "posts#destroy"
削除は処理だけなのでビューは不要ですが、投稿詳細画面に削除のリンクを用意します。
ポイントは link_to でのリンクはGETアクセスになってしまうので、POSTにするため、{method: “post”}と明示する点です。
<%= link_to("削除", "/posts/#{@post.id}/destroy", {method: "post"}) %>
最後にPostsコントローラにアクションを書いて完了です。ちなみに、データの削除は.saveの代わりに.destroyを呼びます。
def destroy @post = Post.find_by(id: params[:id]) @post.destroy flash[:notice] = "投稿を削除しました" redirect_to("/posts/index") end
ここまででデータの登録、編集、削除がひと通り完了しました。
次はユーザ情報の登録、編集、一覧表示についてです。つづく。。。
コメント