Ruby on Rails初心者の学習記録 Part4：CRUD（後編）

# はじめに
Ruby on Rails初心者の学習記録Part4です。
[Part1](https://qiita.com/Yasushi-Mo/items/d24fd4abab2c1333c522)では、Rubyの基本文法やRailsを利用したWebアプリケーションの開発環境構築手順及びルーティングについて学びました。
[Part2](https://qiita.com/Yasushi-Mo/items/5dfe81d39b5a153fe43c)では、MVCアーキテクチャのモデルについて学びました。
[Part3](https://qiita.com/Yasushi-Mo/items/931cfcef990647f1da18)とPart4では、 ["Getting Started with Rails"](https://guides.rubyonrails.org/getting_started.html) の ["7 CRUDit Where CRUDit Is Due"](https://guides.rubyonrails.org/getting_started.html#crudit-where-crudit-is-due) を教材にして、**CRUD**を学びます。[Part3](https://qiita.com/Yasushi-Mo/items/931cfcef990647f1da18)では、**CRUD**の **Create** と **Read** を学びました。このPart4では、残りの **Update** と **Delete** を学んでいきたいと思います。

# 1. 記事の更新
まずは **Update** を学んでいきます。
リソースの更新は、リソースの作成ととても似ています。両方とも複数のステップが存在します。
```mermaid
sequenceDiagram
    Client->>Server: Request Form
    Server-->>Client: Form
    Client->>Server: Submit Updated Form
    alt Valid Form
    Server->>Server: Update Resource
    Server-->>Client: Updated View    
    else Invalid Form
    Server-->>Client: Error Message
    end
```
これらのステップは、コントローラーの`edit`と`update`アクションによって制御されています。
それでは、`app/controllers/articles_controller.rb`に`create`アクションを追加します。
```articles_controller.rb
class ArticlesController < ApplicationController
  def index
    @articles = Article.all
  end

  def show
    @article = Article.find(params[:id])
  end

  def new
    @article = Article.new
  end

  def create
    @article = Article.new(article_params)

    if @article.save
      redirect_to @article
    else
      render :new, status: :unprocessable_entity
    end
  end

  def edit
    @article = Article.find(params[:id])
  end

  def update
    @article = Article.find(params[:id])

    if @article.update(article_params)
      redirect_to @article
    else
      render :edit, status: :unprocessable_entity
    end
  end

  private
    def article_params
      params.require(:article).permit(:title, :body)
    end
end
```
今回追加した`edit`と`update`のアクションは、`new`と`create`ととても似ています。
`edit`アクションでは、データベースから対象の記事を取得します。その後、`@article`内に保存し、フォームを作成するときに利用します。デフォルトでは、`edit`アクションは`app/views/articles/edit.html.erb`にレンダリングされます。
`update`アクションでは、改めてデータベースから対象の記事を取得します。その後、`article_params`でフィルタリングされた送信データを利用して更新します。エラーがなければ、対象のページにリダイレクトします。エラーがあれば、`app/views/articles/edit.html.erb`にエラーメッセージを表示します。

## ⅰ. Partialsを利用したビューのコードを共有
`edit`フォームは`new`フォームと同じ見た目です。RailsのForm BuilderとResourceful Routingのおかげで、コードも同じになっています。Form Builderは、モデルオブジェクトが過去保存されているかどうかによって、適切なリクエストを作成し、自動的にフォームを設定します。
どちらも同じコードを使うので、新たに`app/views/articles/edit.html.erb`を作成するのではなく、*Partial* という共有ビューにまとめます。
それでは、共有ビューとして`app/views/articles/_form.html.erb`を作成します。
```_form.html.erb
<%= form_with model: article do |form| %>
  <div>
    <%= form.label :title %><br>
    <%= form.text_field :title %>
    <% article.errors.full_messages_for(:title).each do |message| %>
      <div><%= message %></div>
    <% end %>
  </div>

  <div>
    <%= form.label :body %><br>
    <%= form.text_field :body %><br>
    <% article.errors.full_messages_for(:body).each do |message| %>
      <div><%= message %></div>
    <% end %>
  </div>

  <div>
    <%= form.submit %>
  </div>
<% end %>
```
このコードは、`@article`が`article`に置き換わっている以外は、`app/views/articles/new.html.erb`と同じです。Partialのコードは共有されるので、特定のインスタンス変数に依存しないようにするのがベストプラクティスです。代わりに記事をローカル変数としてPartialに渡します。
それでは、`render`でPartialを使うように`app/views/articles/new.html.erb`を変更します。
```new.html.erb
<h1>New Article</h1>

<%= render "form", article: @article %>
```

:::note warn
Partialのファイル名はプレフィックスにアンダースコアをつけなければなりません（ex. _form.html.erb）。ですが、レンダリングで参照するときはアンダースコアはつけません（ex. render "form"）。
:::
次は`app/views/articles/edit.html.erb`を作成します。同じようにPartialを参照します。
```edit.html.erb
<h1>Edit Article</h1>

<%= render "form", article: @article %>
```
これで http://localhost:3000/articles/1/edit にアクセスすると、1つ目の記事の編集画面が表示され、記事を更新することができるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/b75592f4-ed9f-993a-bd22-2c2f79db5cc4.png)

:::note
より詳しいPartialの説明は、["Layouts and Rendering in Rails § Using Partials"](https://guides.rubyonrails.org/layouts_and_rendering.html#using-partials) に記載があります。
:::

## ⅱ. 仕上げ
仕上げとして、`app/views/articles/show.html.erb`に編集画面へのリンクを追加しましょう。
```show.html.erb
<h1><%= @article.title %></h1>

<p><%= @article.body %></p>

<ul>
  <li><%= link_to "Edit", edit_article_path(@article) %></li>
</ul>
```
編集画面へのリンクが追加されました。
![ezgif-2-0ed1ddc5cd.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/8f520822-edec-1609-bca7-11ac6ca2f2d4.gif)

# 2. 記事の削除
それではいよいよ最後の **Delete** を学んでいきます。リソースの削除プロセスは、作成や更新に比べてシンプルです。必要になるのは、ルーティングとコントローラーのアクションだけです。そして、[Part3で作成したResourceful Routing（`resources :articles`）](https://qiita.com/Yasushi-Mo/items/931cfcef990647f1da18#2-resourceful-routing)によりルーティングは既に提供されています。このルーティングにより、`DELETE /articles/:id`リクエストは`ArticlesController`の`destroy`アクションにマッピングされています。
そのため、`app/controllers/articles_controller.rb`に`destroy`アクションを追加します。
```articles_controller.rb
class ArticlesController < ApplicationController
  def index
    @articles = Article.all
  end

  def show
    @article = Article.find(params[:id])
  end

  def new
    @article = Article.new
  end

  def create
    @article = Article.new(article_params)

    if @article.save
      redirect_to @article
    else
      render :new, status: :unprocessable_entity
    end
  end

  def edit
    @article = Article.find(params[:id])
  end

  def update
    @article = Article.find(params[:id])

    if @article.update(article_params)
      redirect_to @article
    else
      render :edit, status: :unprocessable_entity
    end
  end

  def destroy
    @article = Article.find(params[:id])
    @article.destroy

    redirect_to root_path, status: :see_other
  end

  private
    def article_params
      params.require(:article).permit(:title, :body)
    end
end
```
`destroy`アクションはデータベースから対象の記事を取得します。そして、`destroy`をコールします。その後、ステータスコード [303 See Other](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303) でルートパスへリダイレクトします。
それでは最後に`app/views/articles/show.html.erb`へ記事の削除リンクを追加します。
```show.html.erb
<h1><%= @article.title %></h1>

<p><%= @article.body %></p>

<ul>
  <li><%= link_to "Edit", edit_article_path(@article) %></li>
  <li><%= link_to "Destroy", article_path(@article), data: {
    turbo_method: :delete,
    turbo_confirm: "Are you sure?"
  } %></li>
</ul>
```
こちらのコードでは、"Destroy"リンクのHTML属性`data-turbo-method`と`data-turbo-confirm`を設定するために`data`オプションを利用しています。どちらの属性も [Turbo](https://turbo.hotwired.dev/) というRailsのアプリケーションにデフォルトで含まれているフレームワークにフックします。`data-turbo-method="delete"`を使うことで、`GET`リクエストの代わりに`DELETE`リクエストが送信されます。`data-turbo-confirm="Are you sure?"`を使うことで、リンククリック時に確認ダイアログが表示されます。
以上で削除機能が完成しました。これでCRUDをひと通り実装できました！
![ezgif-2-c58b6ac2f1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/ee251d45-2db8-d94b-e95c-04778db53c9e.gif)

# 最後に
今回は["Getting Started with Rails"](https://guides.rubyonrails.org/getting_started.html) の ["7 CRUDit Where CRUDit Is Due"](https://guides.rubyonrails.org/getting_started.html#crudit-where-crudit-is-due) を教材にして、**CRUD** の **Update** と **Delete** を学びました。
次回は["Getting Started with Rails"](https://guides.rubyonrails.org/getting_started.html) の次の章である ["8 Adding a Second Model"](https://guides.rubyonrails.org/getting_started.html#adding-a-second-model) を教材にコメント機能の実装を通して、今までの復習をしようと思います。
最後までお読みいただきありがとうございました！

- [Part1](https://qiita.com/Yasushi-Mo/items/d24fd4abab2c1333c522)
- [Part2：モデル](https://qiita.com/Yasushi-Mo/items/5dfe81d39b5a153fe43c)
- [Part3：CRUD（前編）](https://qiita.com/Yasushi-Mo/items/931cfcef990647f1da18)

# 参考
- [Getting Started with Rails](https://guides.rubyonrails.org/getting_started.html)
- [Layouts and Rendering in Rails § Using Partials](https://guides.rubyonrails.org/layouts_and_rendering.html#using-partials)
- [Turbo](https://turbo.hotwired.dev/)
- [303 See Other](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303)