[Rails] アイキャッチ画像のサイズ変更と位置指定

学習記録

はじめに

現在、記事にアイキャッチ画像を設定することはできますが、表示する際に画像の大きさと表示位置が選べません。 入力フォームによってアイキャッチ画像の横幅を設定できるようにし、ラジオボタンで選択した位置に画像が表示されるように変更しましょう。
今回、画像アップロード機能はActive Storageというgemを使っています。
まずはActive Storageについて抑えておきましょう。

Active Storage

Rails5.2からActive Storageというファイル管理gemが導入され、Amazon S3などのクラウドストレージサービスへファイルをアップロードして、DB上でActiveRecordモデルに添付することが簡単になりました。添付はどんな種類でもできますが、画像はサイズや形式の変換、ビデオ・PDFはプレビュー機能を提供してくれます。

デコレーターをコードリーディング

画像がどのように実装されているのか見る中で、eye_catch_urlメソッドがデコレータの中で改造されている事に気が付きました。 こちらのデコレータはDraperではなく、ActiveDecoratorというgemを使っています。

module ArticleDecorator
  def eye_catch_url(version = :origin)                     # 引数を渡す。 デフォルト値は origin 
    if !eye_catch.attached? || eye_catch.metadata.blank?   # 画像がない場合、もしくは画像のメタデータがからの場合
      return '/images/eye_catch.jpg'                       # '/images/eye_catch.jpg' を返す
    end

    command = case version                  # version が、
              when :thumb                   # :thumb の場合
                { resize: '640x480' }
              when :lg                      # :lg の場合
                { resize: '1024x768' }
              when :ogp                     # :ogp の場合
                { resize: '120x630' }
              else
                false
              end

    command ? eye_catch.variant(command).processed : eye_catch        # command がtrue の場合、条件分岐された結果に画像がリサイズされる。 falseの場合、リサイズされず、eye_catch が返される
  end
end

attached?: レシーバのオブジェクトに指定のファイルが紐付いていればtrueを返す。紐付いていなければ、falseを戻り値として返すメソッド。
variant: 画像を変換する際に使われるメソッド。このメソッドを使うためには、image_processing gemGemfileに追加する必要がある。
processed: これを付けることで、すでにそのサイズで保存されて画像があれば、変換処理は行われず、即時にURLが返される。

Active Storageの導入

gemのインストールはRailsインストール時に行われているので不要です。

$ rails active_storage:install   

コマンド実行時に、active_storage_blobsactive_storage_attachmentsという2つのテーブルを作成するマイグレーションファイルが生成されます。

$ rails db:migrate    #migrationファイルをDBに反映させる。

active_storage_blobs : 実際にアップロードしたファイルが保存されるテーブル(実際はファイル名、ファイルの種類、バイト数、誤り検出符号などのファイルデータを保持するテーブル)

active_storage_attachments: 実際に操作を行うモデルとactive_storage_blobsを紐づける中間テーブル

ファイルの保存先の設定

ファイルの保存先は、各環境の設定ファイルに記載します。
config/environments/development.rb の中身を見てみます。

#デフォルトではdevelopment環境のファイルの管理場所はlocalとなっている
config.active_storage.service = :local                 # この local とは、config/storage.yml で定義された保存先の名前

設定はconfig.active_storage.serviceにファイルを保存する場所を名前で指定し、そのファイルの保存場所の名前に対応する設定をconfig/storage.ymlに定義することで行います。 例えば、:local のところを :amazon:google:microsoft などに置き換え、config/storage.ymlの方に、必要な認証情報などの値を入力します。

config/storage.ymlの中身を見てみます。

local:   #localにおいてあるファイルの設定方法(デフォルト)。ここで、localを定義している。
  service: Disk    #localの時は使用するサービスをローカルディスクに設定
  root: <%= Rails.root.join('tmp', 'storage') %>    #ファイルを格納する場所

test:  #testにおいてあるファイルの設定方法。ここで、testを定義している。
  service: Disk   #testの時は使用するサービスをローカルディスクに設定
  root: <%= Rails.root.join('tmp', 'storage') %>    #ファイルを格納する場所

ここのファイルに書いてある情報(ルート、サービスなど)をもとに、モデルが実際のファイルを取得しに行きます。

カラム追加

class AddColumnToArticles < ActiveRecord::Migration[5.2]
  def change
    add_column :articles, :eyecatch_width, :integer
    add_column :articles, :eyecatch_align, :integer, default: 0, null: false
  end
end

defalt: 0: デフォルト値が設定されない場合、0になるよう設定します。

null: false: NULLを許可しないよう設定します。

eyecatch_with にNOT NULL制約やdefaltの設定をしてしまうと、記事作成時にバリデーションエラーで引っかかってしまいます。
default: 100などの機能個別の設定値は、DB側に設定してしまうと要件が変更された場合に修正が大変です。

enumの設定

enum align: { left: 0, center: 1, right: 2 }

バリデーションの設定

validates :eyecatch_width, numericality: { less_than_or_equal_to: 700, greater_than_or_equal_to: 100 }, allow_blank: true

numericality: このヘルパーは、属性に数値のみ使われていることを検証します。デフォルトでは、整数または浮動小数点にマッチします。

オプション
less_than_or_equal_to: 指定された値と等しいか、それよりも小さくなければならないことを指定します。
greater_than_or_equal_to: 指定された値と等しいか、それよりも大きくなければならないことを指定します。

コントローラ設定

追加した2つのカラムがストロングパラメータで受け取れる値として追加しましょう。

def article_params
   params.require(:article).permit(
     :title, :description, :slug, :state, :published_at, :eye_catch, :eyecatch_width, :eyecatch_align, :category_id, :author_id, tag_ids: []
   )
end

入力フォームの作成

= simple_form_for @article, url: admin_article_path(@article.uuid) do |f|
        .box-body
          - if @article.eye_catch.attached?
            = image_tag @article.eye_catch_url(:thumb), class: 'img-thumbnail'
            br
            br
          = f.input :eyecatch_width, placeholder: '100'        # 追加 プレースホルダーを100に設定
          = f.input_field :eyecatch_align, as: :radio_buttons       # 追加 f.input だと正しく出力されない。ラジオボタンを使うときはf.input_fieldを使う
          = f.input :description, input_html: { class: 'js-autosize' }

simple_formについて

今回の使っている入力フォームは、最低限の記述で簡単に入力フォームを生成できるsimple_formというgemを使っています。 Railsでフォームを作成するにはform_withを使えば実装できますが、simple_formを使えばより簡単にフォームを作成することができ、カスタマイズも容易です。

ビューファイル

画像表示部分のclassを動的に出力する

- if article.eye_catch.attached?
    section.eye_catch class="text-#{article.eyecatch_align}"
      = image_tag article.eye_catch_url(:lg), class: 'img-fluid', width: article.eyecatch_width

難しく考えて判定ロジックなどをヘルパーメソッドとして作らなくても、eyecatch_alignのenumを利用して class="text-right", class="text-center", class="text-left" を出力するようにすれば、簡単に画像の位置を変更することができます。

enum eyecatch_align: { left: 0, center: 1, right: 2 }
[2] pry(main)> article.eyecatch_align
=> "center"

widthの値も同じ要領で設定します。

参考記事

Active Storage の概要 - Railsガイド
Active Record のモデルにファイルを添付する方法について解説します。
Active Record バリデーション - Railsガイド
Active Recordのバリデーション機能について解説します。
GitHub - heartcombo/simple_form: Forms made easy for Rails! It's tied to a simple DSL, with no opinion on markup.
Forms made easy for Rails! It's tied to a simple DSL, with no opinion on markup. - GitHub - heartcombo/simple_form: Forms made easy for Rails! It's tied...

コメント

タイトルとURLをコピーしました