Rails carrierwave について

#はじめに
gemのnested_formを使ってますが、最新コミットが2013年で止まっているので、最近はcocoonを使ってます。
使い方はnested_formに似ています。

参考リンク↓
https://github.com/nathanvda/cocoon

#gemの追加

carrierwaveはバージョンが合わないとエラーが結構出て苦しめられたので、githubに合わせるようにした。

```
gem 'carrierwave', '>= 2.0.0.rc', '< 3.0'
gem 'mini_magick'
```
今回は　S３に画像を保存するためにgem 'fog'を追加します。

```
gem 'fog'
```

```
$ bundle
```
#画像が１枚だけの時の例(rails tutorialから引用）
###アップローダーの作成
```
$ rails generate uploader Picture
```
###画像を保存するカラムをつくる
```
$ rails generate migration add_picture_to_microposts picture:string
$ rails db:migrate
```
モデルのカラムとアップローダーを紐付けます。

```rb:models/micropost.rb
class Micropost < ApplicationRecord
  mount_uploader :picture, PictureUploader
end
```

###viewに反映させる
まずはフォーム

```erb
<%= form_for(@micropost) do |f| %>
  <%= f.file_field :picture %>
<% end %>
```
次にモデル(ストロングパラメーターにカラムを追加）

```rb
private

  def micropost_params
    params.require(:micropost).permit(:content, :picture)
  end
```

ビューに表示する

```erb
<%= image_tag micropost.picture.url if micropost.picture? %>
```

rails tutorialの内容なのでざっくりと書いてます。次が複数枚登録の場合です。




#画像が複数枚ある時の例
###アップローダーの作成

まず、Pictureという名前のアップローダーを作成します。

```
$rails generate uploader Picture
```

今回はBlogモデルにアップローダーのPictureを紐付けます。
Blogに複数画像を所持してもらいたいため、BlogとPictureを直接紐付けるのでなく、
Imageモデルを通して紐付けます。
Imageにタイトルも欲しかったのでつけました。
＊Blogモデルは前もって作成してます。

```
$rails g model Image title:string picture:string blog:references
```

```rb:マイグレーションファイル
class CreateImages < ActiveRecord::Migration[5.2]
  def change
    create_table :images do |t|
      t.string :title
      t.string :picture
      t.references :blog, foreign_key: true

      t.timestamps
    end
  end
end
```
このようなマイグレーションファイルができると思います。

```
$rails db:migrate
```

Imageモデルを以下のように変更します。

```rb:models/image.rb
class Image < ApplicationRecord
  belongs_to :blog
  mount_uploader :picture, PictureUploader #Pictureアップローダーとimageモデルの:pictureを紐付けてます。
end
```

```rb:models/blog.rb
class Blog < ApplicationRecord
  validates :title, presence: true, length: { minimum: 2, maximum: 50 }
  validates :body, presence: true, length: { minimum: 5, maxmum: 500 }
  validates :user_id, presence: true
  belongs_to :user
#------------ここから上は関係ないです。-------------------------
  has_many :image, dependent: :destroy #この一行を追加する。dependent: :deleteはblogが消えたらimageも一緒に消えるように
end
```
###１つのフォームで多数のモデルのデーターを送信する

BlogモデルとImageモデルを同じフォームで作成したいため、gem "nested_form"を追加します。
詳しくは以下サイトを見てください。

[fields_forの上手な使い方-Qiita](https://qiita.com/kouuuki/items/5daf2b5f34273d8457f7)
[Rails フォーム（form_for,nested_form,fields_for） による複数同時投稿（親子、親子孫、他人）-Qiita](https://qiita.com/NoharaMasato/items/045788fdeadd776449d5)
[ryanb / nested_form-Github](https://github.com/ryanb/nested_form)

この３つを読めばだいたいわかるはず。

```
gem "nested_form"
```

```
$bundle
```
###gem jquery-railsの追加
"ensted_form"を使うのにJqueryがいるみたいなので追加します。


```
gem 'jquery-rails'
```
```
$bundle
```
以下の３つを追加、たしか//= require rails-ujsの上に追加したほうが良かったような気がします。

```js:assets/javascripts/application.js
//= require jquery3
//= require popper
//= require bootstrap-sprockets
```

###モデル、コントローラーの実装

Blogモデルにメソッドを追加します。

```rb:models/blog.rb
class Blog < ApplicationRecord
  has_many :images, dependent: :destroy
  accepts_nested_attributes_for :images, allow_destroy: true #追加した行
end
```
nested_formを追加したら、accepts_nested_attributes_forが使えるようになっています。
allow_destroy: trueでBlogモデルのデータを削除した紐付いたimageも消えるそうです。

```rb:controllers/blogs_controller.rb

class BlogsController < ApplicationController
  before_action :set_blog, only: %i[show edit update destroy]
  skip_before_action :authenticate_user!, only: %i[index show]
  
  def index
    @blogs = Blog.all
  end

  def show
    @blog = Blog.find(params[:id])
    @user = @blog.user
    @images = @blog.images
  end

  def new
    @blog = Blog.new
    @image = @blog.images.build # 1 この行を追加
  end

  def create
    @blog = Blog.new(blog_params)
    @blog.user_id = current_user.id
    if @blog.save
      flash[:success] = "#{@blog.title} を作成しました。"
      redirect_to @blog
    else
      render :new
    end
  end

  def edit
    @image = @blog.images.build # 2 この行を追加
  end

  def update
    if @blog.update(blog_params)
      flash[:success] = "#{@blog.title} を編集しました。"
      redirect_to @blog
    else
      render :edit
    end
  end

  def destroy
    @blog.destroy
    flash[:success] = "#{@blog.title} を削除しました。"
    redirect_to blogs_path
  end

  private
#---------------------追加分---------------------------------------------------
  def blog_params
    params.require(:blog).permit(:title, :body, images_attributes: [:id, :title, :picture, :_destroy])
  end
#---------------------追加分---------------------------------------------------

  def set_blog
    @blog = Blog.find(params[:id])
  end
end
```

自分がつくったコントローラーを全部貼り付けていますが、実際追加した部分は#1, 2と追加分と書かれているストロングパラメーターの中身だけです。
create,update,destroyは普通のCRUD処理でいじってません。また、Imageのコントローラーも全く触りません。
\#1,2はフォームの処理で@imagesがほしいので作ってます。
追加分と書いてある部分はaccepts_nested_attributes_for :images, allow_destroy: trueとモデルに追加した時に、images_attributesというメソッドが出来ているみたいです。
このへんは３つの参考資料を読んでください。

次にフォーム（パーシャルでnewとedit共通のフォームです）

```erb:blogs/_form.html.erb

<%= nested_form_for(@blog) do |f| %> # 1変更部分です。
  <%= render 'shared/error', object: @blog %>
  <div class='form-group'>
    <%= f.label :title, class: 'label-title' %>
    <%= f.text_field :title, class: 'form-control', placeholder: 'タイトル', autofocus: true %>
  </div>

  <div class='form-group'>
    <%= f.label :body, class: 'label-body' %>
    <%= f.text_area :body, class: 'form-control', rows: 10, placeholder: '記事の内容' %>
  </div>

#--------------------------追加部分---------------------------------------------
  <%= f.fields_for :images, :html => { multipart: true } do |f| %> # 2 追加部分
    <div class='form-group'>
      <%= f.label :title, class: 'label-title' %>
      <%= f.text_field :title, class: 'form-control', placeholder: 'タイトル' %>
    </div>
    <div class='form-group'>
      <%= f.label :picture, class: 'label-picture' %>
      <%= f.file_field :picture, class: 'form-control' %>
    </div>
    <%= f.link_to_remove "写真リンクの削除", class: "btn btn-outline-success mb-3" %>
  <% end %>
  <%= f.link_to_add "画像の追加", :images, class: "btn btn-outline-success mb-3" %>
#--------------------------追加部分---------------------------------------------

  <div class='form-group'>
    <%= f.submit nil, class: 'btn btn-outline-success'%>
  </div>
<% end %>
```

"ensted_form"については本題でないため、説明を省きますが、# 2 追加部分の:html => { multipart: true }で複数の画像が保存できるようになっています。
適当にshowページも作ってみたので貼って置きます。

```erb:views/blogs/show.html.erb

<h1><%= @blog.title %></h1>
<p><%= link_to @user.name, user_path(@user) %></p>
<p><%= @blog.body %></p>

<% @images.each do |image| %>
  <p><%= image.title %></p>
  <p><%= image_tag image.picture.url if image.picture? %></p>
<% end %>
<%= link_to "編集", edit_blog_path(@blog), class: "btn btn-outline-success" %>
<%= link_to "削除", blog_path(@blog), 
                   method: :delete, 
                   class: "btn btn-outline-success", 
                   data: { confirm: "#{@blog.title} を削除します。よろしいですか？"} %>
```

#画像のリサイズ　MiniMagick
はじめに追加したminimagickを使います。
主なメソッドは

・resize_to_fit
・resize_to_limit
・resize_to_fill

このサイトのパクリです。↓
[CarrierWave+MiniMagickで使う、画像リサイズのメソッド-Qiita](https://qiita.com/wann/items/c6d4c3f17b97bb33936f)

minimagickをローカルで使うにはimagemagickをインストール必要があるみたいです。macを使っているのであれば、Homebrewなどでインストールしてください。ってrails tutorialに書いてありました。
[rails tutorial-13.4.3-画像のリサイズ](https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-image_resizing)

```erb:views/blogs/show.html.erb
 <p><%= image_tag image.picture.url, size: "100x100" if image.picture? %></p>
```
に書けば、画像のサイズをビューを表示する前に変更してくれるのですが、おそらく、毎回もとの画像からサイズを変更することになるので、無駄に時間がかかってしまいます。
あと、タテとヨコの比率の無視されてました。
なので、画像が保存されるタイミングでリサイズをしたいと思います。

```rb:uploaders/picture_uploader.rb

class PictureUploader < CarrierWave::Uploader::Base
  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  include CarrierWave::MiniMagick # 1 コメントを外す
#画像保存時に800x700より大きい画像はこのサイズでリサイズされます。
  process resize_to_fit: [800, 700]

#バージョン指定することで、別のサイズでリサイズすることが出来ます。
  version :thumb do
    process resize_to_fill: [300, 300, "Center"]
  end

  #以下省略してます↓↓↓
 
end
```

ビューにバージョンを適応させるには

```erb:views/blogs/show.html.erb
<p><%= image_tag image.picture.thumb.url if image.picture? %></p>
```

画像に適応させたいバージョンを付け加えるだけです。
注意が１つありましてバージョンを後から付け加えた場合、もともと保存されていた画像には適応されません。
適応したい場合は以下のサイトを参考にしてください。ちゃんとメソッドが用意されているみたいです。↓

[carrierwave で新しいリサイズのバージョンを既存の画像ファイルに適用させる(Rails)-Qiita](https://qiita.com/hirotakasasaki/items/91d017c43e0f7493d48a)

#バリデーションについて
rails tutorialのパクリです。そっちを見てください。↓

[rails tutorial-13.4.2-画像の検証](https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-image_validation)