Rails 掲示板の画像アップロード機能 carrierwave 解説

Rails 掲示板の画像アップロード機能の実装　手順　（自分用）

#アップロード用のライブラリ(carrierwave)をインストール

```:Gemfile
gem 'carrierwave'
```

・carrierwaveをgemに追加して、bundle install

```:ターミナル
$ bundle install
```

#アップローダーを生成

```:ターミナル
$ bundle exec rails g uploader アップローダー名
```

```:ターミナル
$ bundle exec rails g uploader BoardImage
```
コマンドを実行すると、app/uploaders/board_image_uploader.rbファイルが作成される。

###生成されたアップローダー

```rb:
class BoardImageUploader < CarrierWave::Uploader::Base
  storage :file #アップロードファイルの保存場所を指定

  def store_dir #アップロードファイルの保存するディレクトリを指定
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  def default_url #デフォルトでっ表示する画像を指定。「app/assets/images」に保存した写真が使える。（最初は、sample.jpgが入っている）
    'board_placeholder.png'
  end

  def extension_whitelist #アップロードするファイルの許可する拡張子を指定
    %w(jpg jpeg gif png)
  end

end
```
・デフォルトでstorage :fileが指定されているので、アップロードしたファイルはpublic/配下に保存される。保存されるディレクトリは、store_dirで設定されるので、アップロードしたファイルは、「public/uploads」に保存される。


#アップロード画像のカラムを追加

・アップロード画像の情報を保存するboard_imageカラムをboardsテーブル追加する為に、マイグレーションファイルを作成

```:ターミナル
$ bundle exec rails g migration AddBoardImageToBoards board_image:string
$ bundle exec rails db:migrate
```
・マイグレーションを実行して、boardsテーブルにboard_imageカラムを追加。

#アップローダークラスとカラムの紐づけ

```rb:アップローダークラスとカラムの紐づけ
class モデル名 < ActiveRecord::Base
  mount_uploader [:カラム名], [アップローダークラス]
end
```
・投稿の際に画像も投稿する機能を追加したいので、boardモデルに先ほど作成した投稿画像用の「board_imageカラム」と「BoardImageUploaderクラス」を紐づける。

```rb:board.rb
class Board < ApplicationRecord
  mount_uploader :board_image, BoardImageUploader #追記
  belongs_to :user

  validates :title, presence: true, length: { maximum: 255 }
  validates :body, presence: true, length: { maximum: 65_535 }
end
```
・boardモデルで画像を投稿する際の「board_imageカラム」の設定を「BoardImageUploaderクラス」で行えるようになる。

#boardコントローラーに設定しているストロングパラメーターに受け取るカラムを追加

```rb:app/controllers/boards_controller.rb
   def board_params
     params.require(:board).permit(:title, :body, :board_image, :board_image_cache)
   end
```
・「board_image」「board_image_cache」を追加。

```rb:app/views/boards/_form.html.erb
<%= form_with model: board, local: true do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="form-group">
    <%= f.label :title %>
    <%= f.text_field :title, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= f.label :body %>
    <%= f.text_area :body, class: 'form-control', rows: 10 %>
  </div>
  <div class="form-group"> #ここから追記
    <%= f.label :board_image %>
    <%= f.file_field :board_image, class: 'form-control mb-3', accept: 'image/*' %>
    <%= f.hidden_field :board_image_cache %>
  </div>
  <div class='mt-3 mb-3'>
    <%= image_tag board.board_image.url,
                  id: 'preview',
                  size: '300x200' %>
  </div> 　　　#ここまで

  <%= f.submit class: 'btn btn-primary' %>
<% end %>
```

```rb:
<%= f.label :board_image %>
```
・「board_image」カラムの入力項目表示。jaファイルで名前変更可能。

```rb:
<%= f.file_field :board_image, class: 'form-control mb-3', accept: 'image/*' %>
```
・「form.htmlタグ名 :カラム名」と指定する。カラム名は保存される先のテーブルのカラム名を指定するので、「board_image」を指定。これで、boardテーブルのboard_imageカラムに投稿した画像のデータが送られる。
・acceptでファイルの種類を指定。image/*は画像ファイル全般。例えば、video/*は動画ファイル全般。

```rb:
<%= f.hidden_field :board_image_cache %>
```
・バリデーションに引っかかり、掲示板が登録できなかった時（エラーが出て入力画面に戻された時）に選んだ画像を保持しておく機能。（入力画面に戻された時に再度画像を選び直さなくていいように。）

#掲示板の部分テンプレートに、アップロードした画像のURLを指定

```rb: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 board.board_image_url, 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>
```

```rb:
<%= image_tag board.board_image.url, class:'card-img-top', size:'300x200' %>
```
・保存した画像（board_image）を表示させるには、画像が保存されている場所(パス)を取得する必要がある
・Boardモデルに、BoardImageUploaderクラスとboard_imageカラムを紐づけたことで「url」メソッドが使えるようになっている。
・「urlメソッド」　→ ファイルのURLを取得　「使い方の例：boardモデル.board_image.url」
・「board.board_image.url」でBoardモデルのboard_imageカラムからurlを取得している。
＊アップローダーでデフォルトで表示される画像を設定しているので、画像が設定されていないとういう事が起きないため、if,elseでの画像が設定されていなかったらなどの分岐は必要ない。

# .gitignoreファイルの設定

・public/uploads/配下に保存される画像は、Githubなどにアップロードする必要がないので、以下のように.gitignoreファイルに/public/uploadsを指定してGit管理下から除外

```:.gitignore
/public/uploads
```