Railsを使った画像の登録

##前提
- ruby on rails 6.0.0 を使用。
- ユーザー機能はdeviseにより導入されているものとする。
- viewファイルは全てhaml形式とする。
- ちなみに使っているのはMacBook Air(Retina, 13-inch, 2020)です。

##はじめに
前置きや手順などは <a href="https://qiita.com/silvercrow222/items/55aeb5d13ec2bde305db">part1</a> に記述してあるので早速実装の続きをしていこうと思います。気になったら是非読んでネ。

part2 でやることとしては手順の2番目にあたる「imageテーブルを関連づけて画像の投稿を実装」というところです。

別テーブルに画像を登録するだけなのですが、実装するのに3万年くらいかかったので正しい手順を後世に残したいと思います。

##ではさっそく、
本日もはりきってやっていきましょう。

> $ *rails g model image*
> $ *rails db:migrate*

まずはimageモデルを作ってマイグレートするところから。
前回と同じくマイグレーションファイルの記述等は省略しています。

次にモデル構造の説明をしていきます。

```ruby:app/models/product.rb
class Product < ApplicationRecord
  belongs_to :user
  has_many :images
end
```
```ruby:app/models/image.rb
class Image < ApplicationRecord
  belongs_to :product
end
```
上記を見ていただく通り、1つのproductモデルに対して、複数のimageモデルを持つことができる、1対多の関係になっています。(わかりやすいようバリデーションは省略しております)
imageテーブルでは1枚の画像に1つのレコードが対応していて、それぞれにproduct_idを紐づけているといった形です。

ざっと構造が把握できたところでローカル環境で画像をアップロードする準備を行っていきます。

```:Gemfile
~省略~
gem 'carrierwave'
gem 'mini_magick'
```
まずはgemの導入からです。画像をアップロードするために、今回は carrierwave の uploader を使っていきたいと思います。
> $ *bundle install*
> $ *rails g uploader image*

忘れずに bandle install を行ってから、uploaderファイルを生成します。これらをしっかり使うために、生成したuploaderファイルと先ほどのモデルに記述を追加していきましょう。

```ruby:app/models/image.rb
class Image < ApplicationRecord
  belongs_to :product
  mount_uploader :src, ImageUploader
end
```
このように記述することで、imageモデルのsrcカラムにおいてImageUploaderを使用することが可能になります。

```ruby:app/uploaders/image_uploader.rb
include CarrierWave::MiniMagick
process resize_to_fit: [50, 50]
```
こちらはMiniMagickの設定です。アップロードした画像のサイズを調整できる便利なgemですね。
上記の2行は新しく追加するというより、コメントアウトの中からこの記述を見つけてコメントアウトを外していただく形になります。(resizeの中身は自由に調節していただいて構いません)

ここまででアップロード自体はできるようになったので、実際に画像を投稿するフォームを作っていきます。

```ruby:app/models/product.rb
class Product < ApplicationRecord
  belongs_to :user
  has_many :images, dependent: :destroy
  accepts_nested_attributes_for :images, allow_destroy: true
end
```
```haml:app/views/products/_form.html.haml
= form_with model: @product, local: true do |f|
  = f.text_field :name, placeholder: 'name'
  = f.fields_for :images do |i|
    = i.file_field :src
  = f.submit 'SEND'
```
fields_forとやらが出てきました。こちらは1つのテーブルに対して、それに紐づいた複数のテーブルに同時にデータを保存することができるフォームヘルパーです。
使い方としては、まず親となるモデルに、accepts_nested_attributes_for という記述を追加します。因数にはそれに紐づく子モデルをもってきましょう。後ろに続く allow_destroy の部分は、trueにすると親レコードが削除された場合に、関連する子レコードも同時に削除してくれるといった便利な機能です。

```ruby:app/controllers/products_controller.rb
#~省略~
  def new
    @product = Product.new
    @product.images.new
  end
#~省略~
  def product_params
    params.require(:product).permit(:name, images_attributes: [:src]).merge(user_id: current_user.id)
  end 
#~省略~
end
```
扱うデータに伴って、コントローラーにも少し修正を加えます。
まずnewアクションの部分では、productに関連する新しいimageインスタンスを生成する記述を追加しています。これがないとimageに正しく画像が保存されないので注意です。
そしてストロングパラメータですね。こちらは少し特殊な記述をしています。_attributes: [: ] というのは関連づくモデルのカラムを指定するというものです。
フォームヘルパーのfields_forを使用した際にセットで使用するものと考えていただければ大丈夫だと思います。

これで画像の投稿機能は実装できたことになります。
ただこのままだと画像が1枚しか選ぶことができないので次回そこをアップグレードしていきたいと思います。

##最後に
今回はimageモデルを作成して実際にフォームで画像を送り、データベースに保存するところまで実装しました。

文字だけではわかりにくいと思ったので参考画像やgifなんかも付け足したいなと思いました。今は少し忙しいので余裕ができたらやろうと思います。(多分)

part3ではとうとうjQueryを導入していきます。表示をあれこれいじって画像を複数投稿できるフォームを作ろう！といった感じですね。頑張ります。

それではまた次のpartで。ありがとうございました。

<a href="https://qiita.com/silvercrow222/items/40ff5cdc28f4d7b7a0d2">商品情報編集機能を実装したい~part3~</a>