【Ruby on Rails】CRUD操作を行うアプリケーションの作成手順

#【Ruby on Rails】CRUD操作を行うアプリケーションの作成手順

前回、[Ruby on Railsを全くゼロの状態からプロジェクトを作成し、DBから取得したデータをブラウザに表示](https://qiita.com/yuta-38/items/e7bb2584d3421ce11e5a)したので、

その続きで**アプリケーションにCRUD（作成、読み取り、更新、削除）を実装する方法**について。


##目次

1. [ルーティングの追加(show)](#1-ルーティングの追加show)
2. [アクションの追加(show)](#2-アクションの追加show)
3. [ビューの編集(show)](#3-ビューの編集show)
4. [ブラウザに表示](#4-ブラウザに表示)
5. [リソースフルルーティング](#5-リソースフルルーティング)
6. [pathヘルパーを使ったリンク設定](#6-pathヘルパーを使ったリンク設定)
7. [link_toヘルパーへの置き換え](#7-link_toヘルパーへの置き換え)
7. [newとcreateアクションの作成](#8-newとcreateアクションの作成)
8. [フォームの作成](#9-フォームの作成)
9. [ストロングパラメータの設定](#10-ストロングパラメータの設定)
10. [articlesトップにフォームへのリンクを追加](#11-articlesトップにフォームへのリンクを追加)
11. [editとupdateアクションの作成](#12-editとupdateアクションの作成)
12. [パーシャル（共有ビュー）の作成](#13-パーシャル共有ビューの作成)
13. [編集ページへのリンク追加](#14-編集ページへのリンク追加)
14. [削除処理の作成](#15-削除処理の作成)
15. [ボタンリンクのレイアウト調整](#16-ボタンリンクのレイアウト調整)


<br>
##1. ルーティングの追加(show)
まずは、ルーティングを追加する。
config/routes.rbに以下を記述。

```rb:routes.rb
Rails.application.routes.draw do
  root "articles#index"

  get "/articles", to: "articles#index"
  get "/articles/:id", to: "articles#show"
end
```

・`get "/articles/:id", to: "articles#show"`
/articles/整数 のURIのアクセスしたら、articlesコントローラのshowアクションを実行する。

・`:id`
URIに`:パラメータ名`とすることでデータを渡すことができる。

**▼パラメータの受け取り**
params([:パラメータ名])　でデータを受け取ることができる。

```:パラメータ受け渡しの例
ルーティング： /articles/:id
　↓
URL: articles/5
 ↓
コントローラ： params([:id])  #イコール５になる。
```

`params`があったら、getメソッドでデータが渡されているんだな。と理解しておけばOK。


<br>
##2. アクションの追加(show)
"articles#show"の処理を作成する。
`app/controllers/articles_controller.rb`に以下を追記。

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

テーブルarticlesの中の、URIの末尾で渡された整数番目のデータを取得し、`@article`（単数形）に代入する。

たくさんあるテーブルのデータのうち1行のみを指定して抜き出しているため、変数名は単数形にしている。

・**@ の意味**
コントローラの中の`@変数`をインスタンス変数とよび、インスタンス毎に異なる変数を定義できる。

`@@変数`の場合はクラス変数とよび、クラスで固有（どのインスタンスでも同じ）な変数となる。

（参考）[【Ruby】クラスの中のアットマークの意味。@と@@の違いは？](https://qiita.com/yuta-38/items/5f52daece2df7e6b0680)


<br>
##3. ビューの編集(show)
"articles#show"実行後に表示するビューを作成する。
views > articles > show.html.erb　を作成する。

```erb:show.html.erb
<h1><%= @article.title %></h1>

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

コントローラのshowアクションで、articlesテーブルから取得したデータ`@article`の中の`title`と`body`を表示している。


##4. ブラウザに表示
http://localhost:3000/articles/1 
にアクセスして、ページが正しく表示されるか確認する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/8541d312-e86e-c001-e342-6a383aa0ee1e.png)

サーバーが停止している場合は以下で起動。

```
$ bin/rails server
```


<br>
##5. リソースフルルーティング
CRUD操作のR（読み取り）のためにshowアクションを設定した。
Ruby on Railsには便利なCRUDのための超便利なリソースフルルーティングという機能がある。

routes.rbに以下を記述すると、CRUDのルーティングを自動生成してくれる。

・`resources :ルート名`

なお、ルーティングの確認は`bin/rails routes`コマンドでできる。


<br>
**▼ルーティングの確認**

```rb:routes.rb（indexとshow）
Rails.application.routes.draw do
  root "articles#index"
  get "/articles", to: "articles#index"
  get "/articles/:id", to: "articles#show"
end
```

```:
$ bin/rails routes
                                  Prefix Verb   URI Pattern                                                                                       Controller#Action
                                    root GET    /                                                                                                 articles#index
                                articles GET    /articles(.:format)                                                                               articles#index
                                         GET    /articles/:id(.:format)                                                                           articles#show
```


 ↓ リソースフルルーティングに変更

```rb:routes.rb（indexとshow）
Rails.application.routes.draw do
  root "articles#index"
  resources :articles
end
```

```:
$ bin/rails routes
                                  Prefix Verb   URI Pattern                                                                                       Controller#Action
                                    root GET    /                                                                                                 articles#index
                                articles GET    /articles(.:format)                                                                               articles#index
                                         POST   /articles(.:format)                                                                               articles#create
                             new_article GET    /articles/new(.:format)                                                                           articles#new
                            edit_article GET    /articles/:id/edit(.:format)                                                                      articles#edit
                                 article GET    /articles/:id(.:format)                                                                           articles#show
                                         PATCH  /articles/:id(.:format)                                                                           articles#update
                                         PUT    /articles/:id(.:format)                                                                           articles#update
                                         DELETE /articles/:id(.:format)                                                                           articles#destroy
```

HTTP動詞にPOSTやDELETE、ルートにnew, :id/editが追加されている。


右の方にスクロールしていくと、index, create, new, edit, show, update, destroyアクションと対応していることがわかる。


<br>
##6. pathヘルパーを使ったリンク設定
リソースフルルーティングを使うと、pathヘルパーも使えるようになる。

・`ルート名_path(対応するテーブルの行データ)`

この記述で、対象のページへのURIを表示することができる。


###articlesトップページに各ページへのリンクを追加
`resources :articles`でCRUDのルーティングを作成した場合、`articles_path`ヘルパーが作成される。引数でArticleモデル(articlesテーブル)の1行のデータを渡すと、そのページへのリンクを自動生成してくれる。

これを利用して、index.html.erbページに各個別ページへのリンクを設置する。

```erb:index.html.erb
<h1>Articles</h1>

<ul>
  <% @articles.each do |article| %>
    <li>
      <a href="<%= article_path(article) %>">
        <%= article.title %>
      </a>
    </li>
  <% end %>
</ul>
```

articles#indexコントローラから受け取った`@articles`のデータを一つづつ抜き出し、`article_path(article)`で各個別ページへのリンクを生成している。


<br>
**▼ブラウザでの表示**
http://localhost:3000/articles/ 

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/c3be6a69-c177-c5ba-c05a-1a500b11faac.png)|
|:-|

article.title名でリンクが表示され、クリックすると個別ページに移動することができる。


<br>
##7. link_toヘルパーへの置き換え
aタグとpathヘルパー、アンカーテキストの組み合わせは、`link_to`を使うと簡単に記述できる。

・`link_to "アンカーテキスト", ルート名`

```erb:index.html.erb
<h1>Articles</h1>

<ul>
  <% @articles.each do |article| %>
    <li>
      <%= link_to article.title, article %>
    </li>
  <% end %>
</ul>
```

<br>
**▼ブラウザでの表示**
http://localhost:3000/articles/ 

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/c3be6a69-c177-c5ba-c05a-1a500b11faac.png)|
|:-|

pathヘルパーを使った場合と同じ内容が表示される。


<br>
##8. newとcreateアクションの作成
CRUD操作のC（作成）に当たるアクションを作成する。作成するアクションは２つ。（１）データ入力のためのform用にnewアクション。（２）データをDBに保存するためのcreateアクション。

**▼newアクション**

- モデルから新しいオブジェクトを作成。
- formを表示するために使用する（saveではない）
- 対象のビューは、new.html.erb

**▼createアクション**

- 作成した新しいオブジェクトにデータを追加
- saveでテーブルに保存する目的。
- save処理がうまくいけば、その記事ページにリダイレクト。`/articles/#{@article.id}`
- save処理がうまくいかない場合はnewアクションに飛ばす

app/controllers/articles_controller.rb

```rb:articles_controller.rb
  def new
    @article = Article.new
  end

  def create
    @article = Article.new(title: "...", body: "...")

    if @article.save
      redirect_to @article
    else
      render :new
    end
```

・`if @article.save`
`変数.save`はSQLで INSERT INTO~を実行した後、最後にbooleanを返すため、if文の条件式として使うことができる。

・`render :アクション名`
renderでアクション名を指定すると、そのアクションを実行することができる。

####redirect_toとrenderの違い
saveが成功したときのページ繊維は`redirect_to`、失敗したときは`render`ヘルパーを使うのには意味がある。

**`redirect_to`は新しいリクエストを作成してページ遷移する**。対して、**`render`は現在のリクエストのまま指定のページを開く**。

このため、DBのデータに変更があった場合は、新しいリクエストで通信し直す必要があるため、`redirect_to`を使う。


<br>
##9. フォームの作成
newアクションで開くフォームを作成する。
app/views/articles/new.html.erb

```erb:new.html.erb
<h1>New Article</h1>

<%= form_with model: @article do |form| %>
  <div>
    <%= form.label :title %><br>
    <%= form.text_field :title %>
  </div>

  <div>
    <%= form.label :body %><br>
    <%= form.text_area :body %>
  </div>

  <div>
    <%= form.submit %>
  </div>
<% end %>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/5dea037b-2a36-061a-f265-57a4e38c5726.png)



rubyでformを簡単に作るためにヘルパーが用意されている。（form builderと呼ぶ）。

form builderで使うメソッドは、label, text_field（またはtext_area）, submitの３つだけ。

####・`form_with`
form builderインスタンスを生成する。

```:フォームビルダー
<%= form_with model: モデルのインスタンス do |form| %>
```


####・`form.label`
入力フォームにラベル（テキスト）をつける。指定は文字列でも、プロパティ名でも可。

```:プロパティ名で指定
<%= form.label :プロパティ名 %>
```

```:文字列で指定
<%= form.label "テキスト" %>
```

```:文字列とプロパティ名の組み合わせ
<%= form.label "テスト#{:プロパティ名}テスト" %>
```

<br>
**▼コンパイル例**

```
<%= form.label :title %>
```

 ↓ htmlにコンパイル

```
<label for="article_title">Title</label>
```


<br>
####・`form.text_field`
入力ボックス（input type="text"）を作成する。

```:
<%= form.text_field :プロパティ名 %>
```

コンパイルすると対応するidとname属性が生成される。


<br>
**▼コンパイル例**

```:
<%= form.text_field :title %>
```

 ↓ htmlにコンパイル

```:
<input type="text" name="article[title]" id="article_title">
```


<br>
####・`form.submit`
送信ボタンを作成する。

```
<%= form.submit %>
```

 ↓ htmlにコンパイル

```
<input type="submit" name="commit" value="Save " data-disable-with="Save ">
```

デフォルトではSaveの文字が表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/c85dd5d8-1132-7600-df47-d4d37a1770d3.png)


**▼文字列の追加とクラスの追加**
文字列は `" "`で記述。
クラス名は`,class:'クラス名'`　※カンマ必須。（他のヘルパーも同様）


```
<%= form.submit "送信", class:"submit-button" %>
```

 ↓ htmlにコンパイル

```
<input type="submit" name="commit" value="送信" class="submit-button" data-disable-with="送信">
```



<br>
##10. ストロングパラメータの設定
悪意のあるユーザーによるDBのデータ追加を防ぐため、コントローラにプライベートメソッドを追加する。

app/controllers/articles_controller.rb

###10-1. プライベートメソッドの追加

```rb:articles_controller.rb
  private
    def article_params
      params.require(:article).permit(:title, :body)
    end
```

articleオブジェクトで変更・追加できるカラムは、`title`と`body`のみという制限を付与。


<br>
####（補足）エラー undefined method `errors'
次のようなエラーが発生した場合は、コントローラにプライベートメソッドが追加されていない可能性がある。

>undefined method `errors' for nil:NilClass


<br>
###10-2. バリデーションの追加
モデルにバリデーションを追加する。

・`validates :カラム名, バリデーションルール`

app/models/article.rb

```rb:article.rb
class Article < ApplicationRecord
  validates :title, presence: true
  validates :body, presence: true, length: { minimum: 10 }
end
```


<br>
###10-3. エラーメッセージの表示設定
バリデーションでfalseになったときのエラーメッセージはビューの中に記述する。

```erb:エラーメッセージ
    <% @モデルのインスタンス名.errors.full_messages_for(:プロパティ).each do |message| %>
      <div><%= message %></div>
    <% end %>
```


app/views/articles/new.html.erb

```erb:new.html.erb
<h1>New Article</h1>

<%= 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_area :body %><br>
    <% @article.errors.full_messages_for(:body).each do |message| %>
      <div><%= message %></div>
    <% end %>
  </div>

  <div>
    <%= form.submit %>
  </div>
<% end %>
```

この状態では、エラーメッセージが設定されていないため表示されない。



<br>
##11. articlesトップにフォームへのリンクを追加
`link_to`ヘルパーを使う。パスはpathヘルパーを使う

```
<%= link_to "アンカーテキスト", アクション名_インスタンス名_path %>
```

<br>
app/views/articles/index.html.erb

```erb:index.html.erb
<h1>Articles</h1>

<ul>
  <% @articles.each do |article| %>
    <li>
      <%= link_to article.title, article %>
    </li>
  <% end %>
</ul>

<%= link_to "New Article", new_article_path %>
```


<br>
##12. editとupdateアクションの作成
CRUDの「U」（更新）を行うためのアクションを作成する。必要なアクションは２つ。

（１）editアクションで、DBから指定したidのデータを取得して表示する。
(2)updateアクションで、変更内容をDBに反映する。

app/controllers/articles_controller.rb に以下を追記。


```rb:articles_controller.rb
  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
    end
  end
```

<br>
・`モデルのインスタンス.update（更新データ）`
更新データとして、article_params関数が渡されている。これは、先ほどストロングパラメータとして定義したもの。

データ更新時にバリデーションを実行して、その結果がupdateの引数（更新データ）になる。

>updateの引数にストロングパラメータを指定するのが一般的。

```rb:articles_controller.rb（コントローラ）
  private
    def article_params
      params.require(:article).permit(:title, :body)
    end
```

```rb:article.rb（モデル）
class Article < ApplicationRecord
  validates :title, presence: true
  validates :body, presence: true, length: { minimum: 10 }
end
```


<br>
##13. パーシャル（共有ビュー）の作成
newとeditで使うビューは全く同じものになる。このため、共有用のビュー（パーシャルや部分テンプレートと呼ぶ）を作成して、newとeditそれぞれのビューでパーシャルを読み込むようにする。

###13-1. パーシャルの作成
パーシャルのファイル名は冒頭にアンダースコアをつけ、共有用であることがわかるようにする。

呼び出すビューと同じディレクトリに作成する。

app/views/articles/_form.html.erb

```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_area :body %><br>
    <% article.errors.full_messages_for(:body).each do |message| %>
      <div><%= message %></div>
    <% end %>
  </div>

  <div>
    <%= form.submit %>
  </div>
<% end %>
```

**▼注意点**
パーシャルを使わず直接表示した場合、受け取るデータを@をつけて`@モデルのインスタンス名`で記述したが、パーシャルは@ が不要になる。

これは、パーシャルを呼び出す時に、@ のつかないプロパティ名で渡す記述をするため（次で作成）


<br>
###13-2. パーシャルの呼び出し
renderヘルパーを使って、パーシャルを呼び出す。

・`<%= render "form", 渡すプロパティ名: @モデルのインスタンス名 %>`


以下２ファイルでパーシャルを呼び出す記述を追記。

app/views/articles/new.html.erb
app/views/articles/edit.html.erb

```erb:new.html.erb
<h1>New Article</h1>

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

```erb:edit.html.erb
<h1>Edit Article</h1>

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

どちらのビューも呼び出し方は同じ。h1が異なるのみ。


<br>
###13-3. ブラウザで確認
http://localhost:3000/articles/1/edit
にアクセスする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/dd1c12c5-c568-c87a-c608-57f870447d5e.png)

DBのデータが表示されていればOK。


<br>
##14. 編集ページへのリンク追加
`link_to`を使って、記事詳細ページ（show）と一覧ページ(index)に編集ページ(edit)へのリンクを追加する。

ページの指定はpathヘルパーを使う。**引数でデータを渡す**。

app/views/articles/show.html.erb

```erb:show.html.erb
<h1><%= @article.title %></h1>

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

<ul>
  <li><%= link_to "Edit", edit_article_path(@article) %></li>
</ul>
```

<br>
app/views/articles/index.html.erb


```erb:index.html.erb
<h1>Articles</h1>

<ul>
  <% @articles.each do |article| %>
    <li>
      <%= link_to article.title, article %> | <%= link_to "Edit", edit_article_path(article) %>
    </li>
  <% end %>
</ul>

<%= link_to "New Article", new_article_path %>
```


<br>
##15. 削除処理の作成
###15-1. コントローラにdestroyアクションを追加
CRUDの「D」（削除）処理をコントローラに追加する。

app/controllers/articles_controller.rb

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

    redirect_to root_path
  end
```


<br>
###15-2. 削除リンクの追加
`button_to`ヘルパーを使って、詳細ページ（show）と一覧ページ(index)に削除リンクを追加する。

削除処理のURLは`article`でshowやupdateアクションと同じ。HTTP動詞でdeleteを選択することで振り分けを行う。

```erb:
<li><%= button_to "Destroy", article_path(@article), method: :delete} %>
```

・`method: :メソッド名`
アクセスするメソッドを指定する。デフォルトはGET。


**▼注意点**
公式のチュートリアルでは`link_to`が使われているが、GETメソッドで飛んでしまい削除できないため、`button_to`を使う。


>confirmはbutton_toでは機能しない（rails v6）
・`data: {confirm: 文字列}`
リンク先に飛ぶ前に確認ダイアログを表示する。


<br>
app/views/articles/show.html.erb

```erb:show.html.erb
<h1><%= @article.title %></h1>

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

<ul>
  <li><%= link_to "Edit", edit_article_path(@article) %></li>
  <li><%= button_to "Delete", article_path(@article),　method: :delete　%></li>
</ul>
```

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/a507fb30-f105-f1b0-0b02-282ac271e0be.png)|
|:--|

ボタンとリンクが組み合わさって不自然。。


<br>
app/views/articles/index.html.erb


```erb:index.html.erb
<h1>Articles</h1>

<ul>
  <% @articles.each do |article| %>
    <li>
      <%= link_to article.title, article %> | 
      <%= link_to "Edit", edit_article_path(article) %> | 
      <%= button_to "Delete", article_path(article),　method: :delete　%>

    </li>
  <% end %>
</ul>

<%= link_to "New Article", new_article_path %>
```


<br>
##16. ボタンリンクのレイアウト調整
`button_to`で作成したボタンを、アンカーテキストと同じになるようCSSで調整する。


|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/a507fb30-f105-f1b0-0b02-282ac271e0be.png)|
|:--|

コントローラを作成した時に同時に生成されるscssファイルを編集する。

app/assets/stylesheets/articles.scss

button_toをコンパイルしたタグには`class="button_to"`がついているのでそれを利用。

```scss:articles.scss
.button_to > input{
  //ボタンのcssをキャンセル
  border: none;
  outline: none;
  background: transparent;

  //リンクのcssに合わせる
  font-size: 16px;
  text-decoration: underline;
  color: #640764;
  padding: 0;
  font-family: initial;
}
```

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/089af93a-5975-968a-59e1-53d4ae8eecff.png)|
|:--|

だいたい似た感じになった。（ざっくりなので、カラーコードやhoverの設定など適宜調べてください）


<br>
今回はここまで。次回はCommentモデルの生成と、モデルの関連づけ。