Active Storageを利用して画像投稿機能を実装します。
今回も初心者向けにレシピ投稿アプリを例に作成していきます。
##Active Storageとは
ファイルアップロード機能を簡単に実装できるGem。Railsガイド
##事前準備
####ImageMagickの導入
ImageMagickとは
ImageMagickとは画像処理ライブラリです。
コマンドラインから画像に処理を加えることができるツールです。
Homebrewからインストールするので一度構築すれば次回以降この作業は不要になります。
brew install imagemagick
####必要なGemのインストール
MiniMagickとImageProcessingをインストールします。
MiniMagickとは
mageMagickの機能をRubyで扱えるようにするためのGem。公式ドキュメント
ImageProcessingとは
画像サイズを調整するためのGem。公式ドキュメント
gem 'mini_magick'
gem 'image_processing'
bundle install
##Active Storageの導入
Active Storageをアプリケーション内で使用するために以下のコマンドを実行します。
rails active_storage:install
インストールが完了するとActive Storageに関連したマイグレーションが作成されるので、マイグレートします。
rails db:migrate
マイグレートが完了したらactive_storage_blobsテーブルとactive_storage_attachmentsテーブルが作成されているかスキーマファイルを確認しましょう。Railsガイド
#中略
ActiveRecord::Schema.define(version: 20xx_xx_xx_xxxxxx) do
create_table "active_storage_attachments", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
t.bigint "record_id", null: false
t.bigint "blob_id", null: false
t.datetime "created_at", null: false
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
end
create_table "active_storage_blobs", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "key", null: false
t.string "filename", null: false
t.string "content_type"
t.text "metadata"
t.bigint "byte_size", null: false
t.string "checksum", null: false
t.datetime "created_at", null: false
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end
#以下略
##画像を投稿しよう
####アソシエーションの設定
レコードとファイルを1対1の関係で紐づけるために**has_one_attachedメソッド**を利用します。
今回はレシピ投稿アプリなのでrecipesテーブルに画像ファイルを紐付けます。
class Recipe < ApplicationRecord
has_one_attached :image
end
####コントローラーの編集
画像の保存を許可するストロングパラメーターにしましょう。
class RecipesController < ApplicationController
#中略
private
def recipe_params
params.require(:recipe).permit(:title, :text, :category_id, :time_required_id, :image) #「:image」を追加
end
end
####投稿画面の編集
画像を投稿するためには**file_field**というメソッドを使用します。Railsドキュメント
<%= form_with model: @recipe, local: true do |f| %>
#中略
<div class="form-group">
<label class="text-secondary">画像</label><br>
<%= f.file_field :image %>
</div>
#以下略
<% end %>
保存されているか確認してみましょう。
rails c
[1] pry(main)> Recipe.find(1).image
(0.4ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
Recipe Load (0.2ms) SELECT `recipes`.* FROM `recipes` WHERE `recipes`.`id` = 1 LIMIT 1
=> #<ActiveStorage::Attached::One:0x00007fc173c392e0
@name="image",
@record=
#<Recipe:0x00007fc177cc2780
id: 53,
title: "てりやきチキン",
text:
"1. 鶏もも肉の両面にフォークで穴を開け、味をしみやすくします。\r\n\r\n2. ジッパー付き保存袋に鶏もも肉を入れ、調味料を加えてよく揉み込みます。\r\n\r\n3. 鶏もも肉が重ならないよう平らにならして空気を抜き、半分に折って冷凍庫に入れます。※保存期間は2週間です。\r\n\r\n4. フライパンにサラダ油を引いて中火に熱し、冷蔵庫で半解凍した鶏肉を、皮目を下にして入れます。フタをして5分蒸し焼きにします。裏返し、フタをしてさらに3分焼いたら完成です!",
category_id: 5,
time_required_id: 2,
created_at: Wed, 03 Mar 2021 13:54:36 UTC +00:00,
updated_at: Wed, 03 Mar 2021 13:54:36 UTC +00:00>>
##投稿した画像を表示しよう
####コントローラーの編集
class RecipesController < ApplicationController
#中略
def show
@recipe = Recipe.find(params[:id])
end
def create
@recipe = Recipe.new(recipe_params)
if @recipe.save
redirect_to recipe_path(@recipe) #リダイレクト先をshowに変更
else
render :new
end
end
#以下略
end
####ビューファイルの作成
保存した画像を表示するには**image_tagメソッド**を使います。Railsドキュメント
基本的な使い方は以下のとおりです。
<%= image_tag 画像ファイルのパス %>
#app/assets/imagesにcooking.jpgを配置している場合
<%= image_tag 'cooking.jpg' %>
#モデルから画像ファイルを呼び出す場合
<%= image_tag @recipe.image %>
それではビューファイルを作成していきましょう。
touch app/views/recipes/show.html.erb
show.html.erbを以下のように編集します。
<div class="text-center">
<div class="card recipe-card ">
<%= image_tag @recipe.image, class: "card-img-top" %>
<div class="card-body">
<div class="recipe-name">
<%= @recipe.title %>
</div>
<div class="recipe-content">
カテゴリー: <span class="recipe-category"><%= @recipe.category.name %></span>
所要時間: <span class="recipe-time"><%= @recipe.time_required.name %></span>
</div>
<hr>
<p class="card-text">
<div class="recipe-title">作り方</div>
<div class="recipe-text d-flex justify-content-start">
<%= safe_join(@recipe.text.split("\n"),tag(:br)) %>
</div>
</p>
</div>
</div>
</div>
しかし、このままでは画像が存在しない場合でも、画像を表示する記述が読み込まれて、エラーを引き起こしてしまいます。
この問題を解決するためには**attached?
メソッド**を使います。
attached?メソッドとは
ファイルが添付されていればtrue、添付されていなければfalseを返してくれるメソッドです。Railsガイド
それでは、ビューファイルの修正をしましょう。
#中略
<div class="card recipe-card ">
<%= image_tag @recipe.image, class: "card-img-top" if @recipe.image.attached?%>
<div class="card-body">
#以下略