ProgateでRuby on Railsコースを復習(その2)

Ruby
スポンサーリンク

道場コース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

ここまででデータの登録、編集、削除がひと通り完了しました。

次はユーザ情報の登録、編集、一覧表示についてです。つづく。。。

コメント