[Rails]ItemImagesテーブルとItemsテーブルを別けた際のテーブルの紐付けから複数画像アップロードまで


Gemのインストール

Gemfile
gem 'carrierwave'
gem 'rmagick'

画像投稿機能に関わるgemのインストールをお忘れなく。
gem'rmagick'は画像のリサイズなどに使用します。

前置き

terminal
rails g uploader images

リサイズなど画像の設定をする為のファイルが生成されます。

app/uploaders/images_uploader.rb

モデルとの紐付け

imageが存在するモデルをItem_Imageモデルとしています。

ItemImagesテーブルとItemsテーブルは1対多の関係にあります。
画像は沢山のItemを持っているからです。

item.rb
class Item < ApplicationRecord
  has_many   :item_images 
  accepts_nested_attributes_for   :item_images
end

ネストさせるパラメータ(item_imageモデル)の親のモデルクラスであるitemモデルにaccepts_nested_attributes_forを記述します。引数にはネストの子のモデルをシンボルで指定してあげます。
has_manyなので複数形で記述します。
こうすることで item_images_attributesメソッドがItemモデルに追加されました。
後ほど使用します。

item_image.rb
class ItemImage < ApplicationRecord
    belongs_to :item 

    mount_uploader :image, ImageUploader
end

ItemImagesテーブルに追加したカラムの名前をmount_uploaderに指定します。

Itemsコントローラーの設定

items_controller.rb
def new
  @item = Item.new
  @item_image = @item.item_images.build
end

formで使用する@itemをnewメソッドで生成し、
buildメソッドでitemモデルに紐付くitem_imageモデルを生成しています。
buildメソッドは慣習的に、関連するモデルを生成するときは、buildを使うようです。
ここで生成したインスタンス変数@item_imageはcreateメソッドで使用します。

Formの作成

new.html.haml
= form_with(model:@item, locale: true , id: 'new_item') do |f|
  = f.fields_for :item_images do |i|
   = i.file_field :image, multiple: true, class: 'image-upload-dropfile-hidden', id:"image-label",type: 'file', name: "item_images[image][]"

1つのアップローダーで複数の画像を選択する為には、multiple: trueを記述する必要があります。
multiple: trueを記述した際、params内は配列として渡されるので、コントローラー側で配列の中身を取り出す処理が必要になります。

ストロングパラメーター

items_controller.rb
private

  def item_params
      params.require(:item).permit(item_images_attributes: [:id, :item_id, :image])
  end

今回コードが冗長になる為、itemsテーブルの他のキーのpermitの記述は抜いています。
ここで登場です、item_images_attributesメソッド。
item_images_attributesをpermitに加えると、コントローラで入れ子のitem_imagesの属性を@item.item_imagesとして扱えるようになります。
配列内にはitem_imagesテーブルのキーを記載しています。

Formに入力したデータの保存

items_controller.rb
def create
    if @item.save

      params[:item_images]['image'].each do |img|
        @item_image = @item.item_images.create(:image => img, :item_id => @item.id)
      end

      redirect_to item_path(@item.id)
    end
  end

念押しです。先ほどの@item_imageはcreateアクションで保存の処理を行うために、newアクションでインスタンス生成しました。
multiple: trueで複数の画像を渡しているので、params[:item_images]['image']の中身は配列であり、each文で一つ一つの要素に@item_image取り出す処理を行います。
これでDBにItemモデルと紐づいた画像が登録されます。