掲示板画像アップロード機能を作る

アップローダーの作成

$ bundle exec rails g uploader BoardImage

デフォルトの画像ファイルとアップロード可能なファイルの種類を指定する

class BoardImageUploader < CarrierWave::Uploader::Base

# carrierwaveを通じた画像のアップロード先をどこにするのかを指定して、指定されたディレクトリに、アップロードされたファイルが保存されていく
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

# 画像が投稿されていない場合でもデフォルト画像を表示させる(uploaders/board_image_uploder.rb内の画像をデフォルトにする)
  def default_url
    'board_placeholder.png'
  end

# アップロード可能なファイル種別を指定
  def extension_whitelist
    %w(jpg jpeg gif png)
  end

end

アップロード先のフォルダを、.gitignoreに指定

/public/uploads

ローカルでアップした画像ファイルをリモートにアップロードしないようにgitignoreを追記

Boardテーブルに画像のカラムを追加する

Boardにboard_imageを追加

$ bundle exec rails g migration AddBoardImageToBoards board_image:string
$ bundle exec rails db:migrate

Boardモデルに、アップローダーの仕様を宣言

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

ControllerとViewに、画像ファイルのフィールドを追加する

コントローラで、画像ファイルの入力を受け付ける

app/controllers/boards_controller.rb

# reqireでデータのオブジェクト名を指定して、permitで保存処理のできるキーを追加する
def board_params
     params.require(:board).permit(:title, :body, :board_image, :board_image_cache)
   end

掲示板のフォームに、画像ファイルの入力フィールドを追加

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 %>

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

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>

メッセージを追加

config/locales/activerecord/ja.yml

ja:
  activerecord:
    attributes:
      board:
        title: 'タイトル'
        body: '本文'
        board_image: 'サムネイル'
config/locales/carrierwave/ja.yml

ja:
  errors:
    messages:
      carrierwave_processing_error: '処理できませんでした'
      carrierwave_integrity_error: 'は許可されていないファイルタイプです'
      carrierwave_download_error: 'はダウンロードできません'
      extension_whitelist_error: "は %{allowed_types}の形式でアップロードしてください"
      extension_blacklist_error: "%{extension}ファイルのアップロードは許可されていません。アップロードできないファイルタイプ: %{prohibited_types}"
      content_type_whitelist_error: "%{content_type}ファイルのアップロードは許可されていません。アップロードできるファイルタイプ: %{allowed_types}"
      content_type_blacklist_error: "%{content_type}ファイルのアップロードは許可されていません"
      rmagick_processing_error: "rmagickがファイルを処理できませんでした。画像を確認してください。エラーメッセージ: %{e}"
      mini_magick_processing_error: "MiniMagickがファイルを処理できませんでした。画像を確認してください。エラーメッセージ: %{e}"
      min_size_error: "を%{min_size}以上のサイズにしてください"
      max_size_error: "を%{max_size}以下のサイズにしてください"