Rails　掲示板一覧機能 解説

Rails　掲示板一覧機能 手順　（自分用）

#Bordモデルを作成する

```:ターミナル
$ bundle exec rails generate model board title:string body:text user:references
```
・モデルとマイグレーションファイルが作られる。
・掲示板を作るにあたってBordモデルを作る。その際に「user:references」をつけることで、Userモデルを参照してくれるようになる。（Bordモデルには「belongs_to :user」が追加され、マイグレーションファイルには、「t.references :user, foreign_key: true」が追加される。＊後で説明）

#BoardモデルとUserモデルの関連付けと、バリデーションの設定

```rb:app/models/user.rb
  has_many :bords, dependent: :destroy
```
・has_manyはテーブル同士を関連づけるもの。そのクラス（ここではUser）のidを外部キーとして抱える他のクラス（ここではBord）があり、UserはBordを複数登録可能であるということ。＊has_many(User)はbelongs_to(Bord)を何個も投稿できるよ！みたいなイメージ。
・「dependent: :destroy」オプションをつけることによって「boardに紐づいたuserが消されたら該当boardも削除」という作業を行なってくれる。親にあたるuserが削除された際に、子にあたるbordだけが残ると「board（子）があるのにuser（親）がいない」ということになり、整合性が取れずバグの原因になる為、「dependent: :destroy」を使う。

```rb:app/models/board.rb
class Board < ApplicationRecord
  belongs_to :user

  validates :title, presence: true, length: { maximum: 255 }#「presence: true」で入力必須。「length: { maximum:  }」は文字数制限のバリデーション。今回は、２５５字以内という制限。
  validates :body, presence: true, length: { maximum: 65_535 }#文字数制限が６５文字以上５３５文字以下。
end
```
・belongs_toはそのクラス（ここではBord）が、ある別のクラス（ここではUser）に従属しており、従属先のクラスのid(ここではuser_id)を外部キーとして抱えているということ。

#マイグレーションファイル

```rb:db/migrate/XXXXXXXXXXXXXX_create_boards.rb
class CreateBoards < ActiveRecord::Migration[5.2]
  def change
    create_table :boards do |t|
      t.string :title, null: false
      t.text :body, null: false
      t.references :user, foreign_key: true

      t.timestamps
    end
  end
end
```
・titleとbodyにnull: falseを追加
・t.references :user, foreign_key: trueは、bordテーブルに「user_id」という外部キーカラムを追加するもの。＊掲示板に投稿した際に、user_idカラムがあることで、削除や、編集を自分以外が出来ないようにする為。

```:ターミナル
$ rails db:migrate
```
・マイグレートする。

#掲示板一覧のコントローラーを実装

・bordに対応するコントローラーを作成

```:ターミナル
$ bundle exec rails generate controller boards
```
・コントローラ、view、decoratorが作成される。
#####生成されたBoardsControllerに、掲示板の一覧をDBから取得する処理を追加。

```rb:app/controllers/boards_controller.rb
class BoardController < ApplicationController
  def index
    @boards = Board.all.includes(:user).order(created_at: :desc)#includesメソッド
  end
end
```
#####includesメソッド


```rb:app/controllers/boards_controller.rb
class BoardController < ApplicationController
  def index
    @boards = Board.all.order(created_at: :desc)
  end
end
```
・includesメソッドを使わない場合、bord(投稿)の数だけuserテーブルにアクセスすることになり、データ取得に時間がかかる。その結果、アプリケーションのパフォーマンスが下がる。これを「N+1問題」という
・これを解決するためにincludesメソッドを使用する。

```rb:includesメソッド
モデル名.includes(:アソシエーションで紐付くモデル名)
```
・includesメソッドは、引数に指定された関連モデルを1度のアクセスでまとめて取得するメソッド
・bordテーブルに対して１回、Userテーブルに対しても１回のアクセスで全ての情報を取得できるようになる。

#掲示板作成のrouteを追加

```rb:config/routes.rb
Rails.application.routes.draw do
  root 'static_pages#top'

  get 'login', to: 'user_sessions#new'
  post 'login', to: 'user_sessions#create'
  delete 'logout', to: 'user_sessions#destroy'

  resources :users, only: %i[new create]
  resources :boards, only: %i[index new create]#この部分を追加
end
```

#掲示板一覧のViewを実装する

```html:app/views/boards/index.html.erb
<div class="container pt-3">
   <div class="row">
     <div class="col-lg-10 offset-lg-1">
       <!-- 検索フォーム -->
       <form>
         <div class="input-group mb-3">
           <input class="form-control" placeholder="検索ワード" type="search" />
           <div class="input-group-append">
             <input type="submit" value="検索" class="btn btn-primary" />
           </div>
         </div>
       </form>
     </div>
   </div>
 
   <!-- 掲示板一覧 -->
   <div class="row">
     <div class="col-12">
       <div class="row">
         <% if @boards.present? %>
           <%= render @boards %> #下記で説明
         <% else %>
           <p><%= t('.no_result') %></p>
         <% end %>
       </div>
     </div>
   </div>
 </div>
```
・ 「<%= render partial: 'board', locals: { board: @board }%>」の省略形は「<%= render 'board',　{ board: @board } %>」となっており、さらにインスタンス変数名（＠board）とファイル名（_board.html.erb）が同じ場合「<%= render @boards %>」のような省略形にすることができる。

```html:app/views/boards/_board.html.erb
<div class="col-sm-12 col-lg-4 mb-3">
  <div id="board-id-<%= board.id %>">
    <div class="card">
      <%= image_tag 'app/assets/images/に配置した写真のデータ名', class: 'card-img-top', size: '300x200' %>
      <div class="card-body">
        <h4 class="card-title">
          <a href="#">
            <%= board.title %>
          </a>
        </h4>
        <div class='mr10 float-right'>
          <a href="#"><%= icon 'fas', 'trash', class: 'pr-1' %></a>
          <a href="#"><%= icon 'fa', 'pen' %></a>
        </div>
        <ul class="list-inline">
          <li class="list-inline-item">
            <%= icon 'far', 'user' %>
            <%= board.user.decorate.full_name %>
          </li>
          <li class="list-inline-item">
            <%= icon 'far', 'calendar' %>
            <%= l board.created_at, format: :long %>
          </li>
        </ul>
        <p class="card-text"><%= board.body %></p>
      </div>
    </div>
  </div>
</div>
```

#ヘッダーに掲示板一覧ページへのリンクを追加

```html:_header.html.erb
<div class="dropdown-menu dropdown-menu-right">
  <%= link_to t('boards.index.title'), boards_path, class: 'dropdown-item' %>
  <%= link_to '掲示板作成', 'new_board_path', class: 'dropdown-item' %>
</div>
```
・掲示板一覧のリンクをboards_pathに変更