0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

さあ、アップロードしよ?

Posted at

はじめに

SPA開発でBackend側をRails apiモードを使って画像をアップロードする機能を実装していたのですがそもそも画像を登録するという点に知識が浅く理解をしていなかったのでアウトプットすることを目的で記事にしました

ER図

image.png

当初のテーブルの関係性はER図のように1つの商品には複数の画像を登録できるように設計していました

商品の登録

products_controller.rb
class Api::V1::ProductsController < ApplicationController
  def create
    product = Product.new(prudct_params)

    if product.save
      if params[:product][:images].present?
        params[:product][:images].each do |image|
          product.product_images.create(image: image)
        end
      end

      render json: puroduct, include: [:product_images], status: :created
    else
      render json: { status: "error", errors: product.errors.full_messages }, status: :unprocessable_entity
    end
  end

  private

  def product_params
    params.require(:product).permit(:name, :price, :description)
  end
end

商品を登録するコードを書き終えたのでいざ、データを注入だ!と思ったのですが画像は登録できませんでした(厳密に言えば登録はできるがアップロードはできない)

なぁぜなぁぜ?

そもそも、product_imagesのimageカラムには画像のファイルパスやURLが保存される仕様になっているからでした...ズコーッ!

Active Storage

Active Storageとは、Railsに標準で組み込まれている画像・ファイル管理システムである

実装方法

1. インストール
bash
# Active Storageをインストール
rails active_storage:install
rails db:migrate

このコマンドを実行することでactive_storage_blobsactive_storage_attachmentsというテーブルが作成される

2. モデルにhas_one_attachedを追加

ProductImageモデルにhas_one_attachedを設定

product_image.rb
class ProductImage < ApplicationRecord
  belongs_to :product
  has_one_attached :image
end
3. コントローラーの修正

Postmanやフロントエンドからmultipart/form-data形式で画像を送信できるようにする

products_controller.rb
class Api::V1::ProductsController < ApplicationController
  def create
    product = Product.new(product_params)

    if product.save
      if params[:product][:images].present?
        params[:product][:images].each do |image|
          product_image = product.product_images.create
          product_image.image.attach(image)
        end
      end

      render json: product, include: [:product_images => { methods: :image_url }], status: :created
    else
      render json: { errors: product.errors.full_messages }, status: :unprocessable_entity
    end
  end

  private

  def product_params
    params.require(:product).permit(:name, :price, :description, images: [])
  end
end
4. 画像のURLを取得

モデルにimage_urlメソッドを追加し、APIレスポンスで画像URLを返す

product_image.rb
class ProductImage < ApplicationRecord
  belongs_to :product
  has_one_attached :image

  def image_url
    Rails.application.routes.url_helpers.url_for(image) if image.attached?
  end
end
5. Postmanでテスト

Postmanにてmultipart/form-data形式で画像を送信

  1. POST http://localhost:8000/api/v1/products
  2. Bodyタブを開く
  3. form-dataを選択
  4. product[images][]に画像ファイルを追加

以上が画像をアップロードをする流れです

まだ、終わりません!もっと詳しく!

ここで、疑問に思ったのだがactive_storage_blobsテーブルに保存されるならそもそもpruduct_imagesテーブルは要らなかったのか?

画像はactive_storage_blobsにアップロードされるのでproduct_imagesimageカラムは「画像のファイルパスを文字列として保存する想定の設計」だったため不要になります。しかし、Active Storageを使うならproduct_imagesは単なる中間テーブルとして機能する形になる
なぜなら、1つの商品に複数の画像を登録するためである

  • Active Storageを使ってproduct_imagesテーブルに画像をアップロードする
  • active_stotage_attachmentsproduct_imagesactive_storage_blobsを紐付ける

Active Storageのデータの流れ

Active Storageは以下の3つのテーブルを使って、画像を管理する

  1. product_images → どの商品に紐づく画像なのかを管理(商品IDなど)
  2. active_storage_blobs → 画像の実体データを管理
  3. active_atorage_attachments → product_imagesとactive_storage_blobsを紐づける
  4. この仕組みと使うことでproduct_imagesにhas_one_attached :imageを設定するだけで画像を管理できる
データの流れ図
               ┌────────────────────────┐
               │  product_images        │  ← 商品ごとの画像管理
               │ (id, commodity_crop_id)│
               └───────────┬────────────┘
                           │ (紐づけ)
                           ▼
               ┌────────────────────────────┐
               │ active_storage_attachments │  ← 関連付けテーブル
               │ (record_id, blob_id)       │
               └───────────┬────────────────┘
                           │ (紐づけ)
                           ▼
               ┌────────────────────────┐
               │  active_storage_blobs  │  ← 画像の実体データ
               │ (id, filename, data)   │
               └────────────────────────┘

データの関係性

テーブル 役割 具体例
product_images 商品ごとの画像情報 商品ID: 1, 画像ID: 10
active_storage_attachments products_images と active_storage_blobs を関連付ける record_id: 10, blob_id: 5
active_storage_blobs 実際の画像データ blob_id: 5, ファイル名: apple.jpg

まとめ

画像をアップロードするのにあたり、単にカラム持たせて画像を登録できるものだと思っていたので今回、いい勉強になったなーって思いました!
まだまだ、学習不足だけど新しいことに気づくと楽しいな!!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?