#はじめに
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'
今回は S3に画像を保存するためにgem 'fog'を追加します。
gem 'fog'
$ bundle
#画像が1枚だけの時の例(rails tutorialから引用)
###アップローダーの作成
$ rails generate uploader Picture
###画像を保存するカラムをつくる
$ rails generate migration add_picture_to_microposts picture:string
$ rails db:migrate
モデルのカラムとアップローダーを紐付けます。
class Micropost < ApplicationRecord
mount_uploader :picture, PictureUploader
end
###viewに反映させる
まずはフォーム
<%= form_for(@micropost) do |f| %>
<%= f.file_field :picture %>
<% end %>
次にモデル(ストロングパラメーターにカラムを追加)
private
def micropost_params
params.require(:micropost).permit(:content, :picture)
end
ビューに表示する
<%= 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
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モデルを以下のように変更します。
class Image < ApplicationRecord
belongs_to :blog
mount_uploader :picture, PictureUploader #Pictureアップローダーとimageモデルの:pictureを紐付けてます。
end
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
###1つのフォームで多数のモデルのデーターを送信する
BlogモデルとImageモデルを同じフォームで作成したいため、gem "nested_form"を追加します。
詳しくは以下サイトを見てください。
fields_forの上手な使い方-Qiita
Rails フォーム(form_for,nested_form,fields_for) による複数同時投稿(親子、親子孫、他人)-Qiita
ryanb / nested_form-Github
この3つを読めばだいたいわかるはず。
gem "nested_form"
$bundle
###gem jquery-railsの追加
"ensted_form"を使うのにJqueryがいるみたいなので追加します。
gem 'jquery-rails'
$bundle
以下の3つを追加、たしか//= require rails-ujsの上に追加したほうが良かったような気がします。
//= require jquery3
//= require popper
//= require bootstrap-sprockets
###モデル、コントローラーの実装
Blogモデルにメソッドを追加します。
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も消えるそうです。
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というメソッドが出来ているみたいです。
このへんは3つの参考資料を読んでください。
次にフォーム(パーシャルでnewとedit共通のフォームです)
<%= 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ページも作ってみたので貼って置きます。
<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
minimagickをローカルで使うにはimagemagickをインストール必要があるみたいです。macを使っているのであれば、Homebrewなどでインストールしてください。ってrails tutorialに書いてありました。
rails tutorial-13.4.3-画像のリサイズ
<p><%= image_tag image.picture.url, size: "100x100" if image.picture? %></p>
に書けば、画像のサイズをビューを表示する前に変更してくれるのですが、おそらく、毎回もとの画像からサイズを変更することになるので、無駄に時間がかかってしまいます。
あと、タテとヨコの比率の無視されてました。
なので、画像が保存されるタイミングでリサイズをしたいと思います。
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
ビューにバージョンを適応させるには
<p><%= image_tag image.picture.thumb.url if image.picture? %></p>
画像に適応させたいバージョンを付け加えるだけです。
注意が1つありましてバージョンを後から付け加えた場合、もともと保存されていた画像には適応されません。
適応したい場合は以下のサイトを参考にしてください。ちゃんとメソッドが用意されているみたいです。↓
carrierwave で新しいリサイズのバージョンを既存の画像ファイルに適用させる(Rails)-Qiita
#バリデーションについて
rails tutorialのパクリです。そっちを見てください。↓