Railsを使ったToDoリストの作成(4.CRUDのRead機能)

#概要
本記事は、初学者がRailsを使ってToDoリストを作成する過程を記したものです。
私と同じく初学者の方で、Railsのアウトプット段階でつまづいている方に向けて基礎の基礎を押さえた解説をしております。
抜け漏れや説明不足など多々あるとは思いますが、読んでくださった方にとって少しでも役に立つ記事であれば幸いです。

#環境
* `Homebrew: 2.5.10` -> MacOSのパッケージ管理ツール
* `ruby: 2.6.5p114` -> Ruby
* `Rails: 6.0.3.4` -> Rails
* `node: 14.3.0` -> Node.js
* `yarn: 1.22.10` -> JSのパッケージ管理ツール
* `Bundler: 2.1.4` -> gemのバージョン管理ツール

```:iTerm
$ brew -v => Homebrew 2.5.10
$ ruby -v => ruby 2.6.5p114
$ rails -v => Rails 6.0.3.4
$ npm version => node: '14.3.0'
$ yarn -v => 1.22.10
$ Bundler -v => Bundler version 2.1.4
```

#第４章　CRUDのRead（index／show）
第４章では、CRUDを使ってWebアプリケーションの基本的なデータ操作を実装します。CRUDは、Create(作成)、Read(参照)、Update(更新)、Delete(削除)を表します。今回実装したい機能は以下の通りです。

* 一覧表示機能（indexアクション）
* 詳細表示機能（showアクション）
* 新規登録機能（newアクション／createアクション）
* 編集機能（editアクション／updateアクション）
* 削除機能（destroyアクション）

では、それぞれの機能実装について詳しく見ていきましょう。
まずはCRUDのReadについてです。

##1 indexアクション
まずは、indexアクションを使って一覧表示機能を実装していきます。

###1 データベースからレコードを取得する
Boardsコントローラを使ってデータベースからレコードを取ってきます。
今回は全てのレコードを取得したいのでActiveRecordのallメソッドを使い、メソッドの返り値をインスタンス変数に代入します。

```app/controllers/boards_controller.rb
class BoardsController < ApplicationController
    def index
        @boards = Board.all
    end  
end
#インスタンス変数(@xxx)に代入したものはhaml内で<%=%>で囲うことで
#Viewsで自由に表示することができます。
```
先ほど全てのデータを取ってくるとは言ったものの、現在はデータが１つしかないためダミーデータを作成します。

ダミーデータを作成するためには`db/seeds.rb`にレコードを書きます。

```db/seeds.rb
Board.create({name: 'seeds-title1', description: 'seeds-description1'})
Board.create({name: 'seeds-title2', description: 'seeds-description2'})
```

レコードを書くことができたら、ターミナルでseeds.rbを読み込むコマンドを実行し、ファイルを読み込みます。

```:iTerm
$ rails db:seed
```

これで新たに２つのレコードがデータベースに保存されているはずです。
試しにコンソールで確認してみましょう。

```ruby
irb(main):002:0> Board.all

=> #<ActiveRecord::Relation [#<Board id: 1, name: "console-name1", description: "console-description1", created_at: "2020-11-17 05:38:06", updated_at: "2020-11-17 05:38:06">, 
#<Board id: 2, name: "seeds-title1", description: "seeds-description1", created_at: "2020-11-17 11:41:05", updated_at: "2020-11-17 11:41:05">, 
#<Board id: 3, name: "seeds-title2", description: "seeds-description2", created_at: "2020-11-17 11:41:05", updated_at: "2020-11-17 11:41:05">]>
```
このようにインスタンスが３つ作成されていればOKです。

###2 一覧画面で全てのデータを表示する
次に、先ほど取ってきたデータをviewsで表示します。
全てのレコードを表示したいのでRubyのeachメソッドを使います。

```haml:app/views/boards/index.html.haml
.container
  - @boards.each do |board|
    .card
      .card_content
        .card_top
          %p.card_title 
            = board.name
          .dropdown
            = image_tag 'Combined Shape.png', class: 'card_dropdown dropbtn'
            .dropdown-content
              %a{:href => "#"} Edit
              %a{:href => "#"} Delete
        .card_description
          = board.description
        .card_detail
          = image_tag 'xxx.png'
```

ポイントは３つあります。

* `- @boards.each do |board|`という繰り返し表現を使って`@boards`の値全てををboardに渡しています。コンソールでは`Board.all`はArrayで返ってきていたため上記のような表現になっています。
* `= board.name`で繰り返し表現されたboardのnameカラムを表示しています。このようにカラム名はメソッドのように使用することができます。
* `= board.description`で繰り返し表現されたboardのdescriptionカラムを表示しています。

以上で、記事一覧機能は実装できました。

###ダミーデータについて
先ほどはseeds.rbを使って手動でダミーデータの作成を行いましたが、ダミーデータを自動的に作成できる[faker](https://github.com/faker-ruby/faker)という便利なgemがあるのでインストールして使っていきましょう。

```:Gemfile
gem 'faker'
```

Gemfileに`gem 'faker'`と書いたら、`bundle install`をします。
これでfakerのインストールは完了です。

では、実際にseeds.rbでfakerを使用してみましょう。
今回はfakerの中でも文章を作成してくれる`Faker::Lorem`を使用しています。
また、より多くのレコードを作成したいので10回Board.createを繰り返しています。

```db/seeds.rb
10.times do
    Board.create(
        name: Faker::Lorem.sentence(word_count: 3), 
        description: Faker::Lorem.sentence(word_count: 20)
    )
end
```

レコードを書くことができたら、ターミナルでseeds.rbを読み込むコマンドを実行し、ファイルを読み込みます。

```:iTerm
$ rails db:seed
```

これで新たに10個のダミーデータの入ったレコードがデータベースに保存されました。

##2 showアクション
次に、showアクションを使って詳細表示機能を実装していきます。

###1 ルーティング
まずは、詳細表示ページに遷移するURLが必要なのでroutes.rbでURLを作成します。

```config/routes.rb
Rails.application.routes.draw do
  resources :boards
end
```

`resources`はCRUDの基本となる以下７つのアクションへのルーティングを定義する時に使います。

| CRUD | アクション | 　　 |
|:-:|:-:|:-:|
| Create(作成)  |  index |  show |
| Read(参照)  | new  | create  |
|  Update(更新) |  edit |  update |
|  Delete(削除) |  delete |   |

また、以下のようにonlyオプションをつけると、ルーティングを限定することができます。

```ruby
resources :boards, only: [:show, :edit, :update]
#resources :コントローラ名, only: [アクション名]
```

今回は、boardsコントローラにresourcesを指定したので、[rails/info](http://localhost:3000/rails/info/routes)で確認すると全てのアクションのルーティングが定義されていることがわかると思います。
![スクリーンショット 2020-11-18 12.19.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864210/caeb41cb-b10b-117a-bfda-9534d791d9a9.png)

###2 コントローラ
では、次にコントローラでデータを取得します。
データを取得するためにActiveRecordのfindメソッドを使用します。
findメソッドはテーブルから引数に入っているidのレコードを取ってきます。

```app/controllers/boards_controller.rb
class BoardsController < ApplicationController
    def show
        @board = Board.find(params[:id])
    end
end
```
ポイントは２つあります。

* ビューで表示したいので取得したデータをインスタンス変数に代入しています。今回は１つのレコードを取ってくるので単数形になっています。
* 引数の値を`params[:id]`としています。'params'とは送られてきた値を受け取るためのメソッドで[:カラム名]で値を受け取ることができます。

以上で、データの取得は完了です。

###3 Views

取得したデータをViewsで表示していきます。
先ほど取得した値を`@board.name`で表示しています

```app/views/boards/show.html.haml
.container
  .board_title
    = @board.name
  .card
    .card_image
      = image_tag 'ToDoPhoto.png'
    .card_content
      .card_top
        %p.card_title 
          card_name
      .card_description
        card_description
      .card_detail
        = image_tag 'default-avatar.png'
        .card_detail_img
          %div
            %span 23
            %span 
              = image_tag 'Shape.png'
```
これでshowアクションを使った詳細表示機能の実装は完了です。

###4 一覧表示画面にリンクを貼る

最後に、一覧表示ページから詳細表示ページに遷移できるようにリンクを貼っていきましょう。

```app/views/boards/index.html.haml
.container
  - @boards.each do |board|
    = link_to board_path(id: board.id) do
      .card
        .card_content
          .card_top
            %p.card_title 
              = board.name
            .dropdown
              = image_tag 'Combined Shape.png', class: 'card_dropdown dropbtn'
              .dropdown-content
                %p Edit
                %p Delete
          .card_description
            = board.description
          .card_detail
            = image_tag 'default-avatar.png'
```
![スクリーンショット 2020-11-18 17.10.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864210/eaa5723c-3550-c778-0dc9-e19d2519f45d.png)

ここではカード全体をリンクにしています。
では具体的にコードを見ていきましょう。

重要なのは以下の一文です。ここでリンクを作成し、ネストすることでカード全体をリンクにしています。

```ruby
= link_to board_path(id: board.id) do
# = link_to 'リンクの名前' Helperのpath 
```

* `link_to`はaタグを生み出すためのメソッドです。
* pathの引数ではidを指定しています。idは@boardsからeachメソッドによって作成されたboardのidカラムなので`board.id`としています。しかしRailsでは引数にインスタンスを代入するだけでidを勝手に取得してくれるため`id: board:id`の部分を`board`と書くこともできます。

##戻るボタンの実装
ついでにheaderに戻るボタンを実装しましょう。
戻るボタンを押したら、前の画面に戻れるようにしてロゴを押したらトップページに戻れるようにします。

```haml:app/layouts/application.html.haml
.header_logos
  - unless current_page?(root_path)
    = link_to :back do
      = image_tag 'xxx.png', class: 'header_back'
  %p.header_pageTitle
    = link_to 'Boards', root_path
```

ここでのポイントは２つあります・

* `current_page?(root_path)`とすることで、現在いるページを判定することができます。今回はunless文を使って一覧表示画面にいない場合に戻るボタンを表示するようにしています。
* `link_to :back`は一個前のページのURLを指しています。