道場コース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
ここまででデータの登録、編集、削除がひと通り完了しました。
次はユーザ情報の登録、編集、一覧表示についてです。つづく。。。

コメント