【Rails】furimaアプリ　商品出品機能実装　#1

# 商品出品機能実装
### 実装概要
- 商品出品機能を実装する。
- モデルの単体テストコードを書く。

### Itemモデルを作成
ターミナル
```
rails g model item
```

### マイグレーションファイルの修正
マイグレーションファイルをDB設計に基づいて下記に修正
```ruby:/db/migrate/2024~_create_items.rb
class CreateItems < ActiveRecord::Migration[7.0]
  def change
    create_table :items do |t|
      t.string        :name,             null: false
      t.text          :description,      null: false
      t.integer       :category_id,      null: false
      t.integer       :status_id,        null: false
      t.integer       :shipping_fee_id,  null: false
      t.integer       :prefecture_id,    null: false
      t.integer       :delivery_time_id, null: false
      t.integer       :price,            null: false
      t.references    :user,             null: false, foreign_key: true
      t.timestamps
    end
  end
end
```
### マイグレーションの実行
ターミナル
```
% rails db:migrate
```
### アソシエーションの記述
```app/models/user.rb
has_many :items
```
```ruby:app.models/item.rb
belongs_to :user
```

### 必要なルーティングを記述
```ruby:/config/routes.rb
resouces :items, only: [:index, :new]
```
### 画像投稿機能を実装
ImageMagickをインストール
ターミナル
```
brew install imagemagick
```
Gemをインストール
```ruby:Gemfile
# Gemfileの一番下に記述する

gem 'mini_magick'
gem 'image_processing', '~> 1.2'
```

ターミナル
```
bundle install
```

### 設定を変更
画像処理にMiniMagickを使用するため、設定ファイルに記述を行う。
```ruby:/config/application.rb
module Furima41457
  class Application < Rails::Application
    中略
    config.active_storage.variant_processor = :mini_magick
    中略
  end
end
```

### ローカルサーバーを再起動
ターミナル
```
% rails s
```

### Active Storageをインストール
ターミナル
```
% rails active_storage:install
```

rails active_storage:installコマンドを実行すると、Active Storageに関連したマイグレーションが作成される。続けてマイグレートする。

ターミナル
```
% rails db:migrate
```

### Itemsテーブルに画像ファイルを紐付ける
下記ファイルに画像ファイルのアソシエーションを追記
```ruby:app/models/item.rb
has_one_attached :image
```

### 画像の保存を許可するストロングパラメーターにする
```ruby:app/controllers/items_controller.rb
#中略

private

  def message_params
    permitted_attributes = [:name, :image, :description, :category_id, :status_id, :shipping_fee_id, :prefecture_id, :price]
    params.require(:item).permit(*permitted_attributes).merge(user_id: current_user.id)
  end
end
```
カラムが多いので、見通しを良くするために配列を使用して管理していく。

### ActiveHashを導入
ターミナル
```
#中略
gem 'active_hash'
```

ターミナル
```
% bundle install
```

### Activehashで使用するモデルをそれぞれ作成
app/modelsディレクトリ配下にカラム名に沿ったファイル名を命名
```ruby:app/models/category.rb
class Category < ActiveHash::Base
  self.data = [
    { id: 1, name: '---' },
    { id: 2, name: 'メンズ' },
    { id: 3, name: 'レディース' },
    { id: 4, name: 'ベビー' },
    { id: 5, name: 'キッズ' },
    { id: 6, name: 'インテリア・住まい・小物、本・音楽・ゲーム' },
    { id: 7, name: 'おもちゃ・ホビー・グッズ' },
    { id: 8, name: '家電・スマホ・カメラ' },
    { id: 9, name: 'スポーツ・レジャー' },
    { id: 10, name: 'ハンドメイド' },
    { id: 11, name: 'その他' }
  ]
end
```
```ruby:app/models/status.rb
class Status < ActiveHash::Base
  self.data = [
    { id: 1, name: '---' },
    { id: 2, name: '新品・未使用' },
    { id: 3, name: '未使用に近い' },
    { id: 4, name: '目立った傷や汚れなし' },
    { id: 5, name: 'やや傷や汚れあり' },
    { id: 6, name: '傷や汚れあり' },
    { id: 7, name: '全体的に状態が悪い' }
  ]
end
```
```ruby:app/models/shipping_fee.rb
class ShippingFee < ActiveHash::Base
  self.data = [
    { id: 1, name: '---' },
    { id: 2, name: '着払い(購入者負担)' },
    { id: 3, name: '送料込み(出品者負担)' }
  ]
end
```
```ruby:app/models/prefecture.rb
class Prefecture < ActiveHash::Base
  self.data = [
    { id: 1, name: '---' },
    { id: 2, name: '北海道' },
    { id: 3, name: '青森県' },
    { id: 4, name: '岩手県' },
    { id: 5, name: '宮城県' },
    { id: 6, name: '秋田県' },
    { id: 7, name: '山形県' },
    { id: 8, name: '福島県' },
    { id: 9, name: '茨城県' },
    { id: 10, name: '栃木県' },
    { id: 11, name: '群馬県' },
    { id: 12, name: '埼玉県' },
    { id: 13, name: '千葉県' },
    { id: 14, name: '東京都' },
    { id: 15, name: '神奈川県' },
    { id: 16, name: '新潟県' },
    { id: 17, name: '富山県' },
    { id: 18, name: '石川県' },
    { id: 19, name: '福井県' },
    { id: 20, name: '山梨県' },
    { id: 21, name: '長野県' },
    { id: 22, name: '岐阜県' },
    { id: 23, name: '静岡県' },
    { id: 24, name: '愛知県' },
    { id: 25, name: '三重県' },
    { id: 26, name: '滋賀県' },
    { id: 27, name: '京都府' },
    { id: 28, name: '大阪府' },
    { id: 29, name: '兵庫県' },
    { id: 30, name: '奈良県' },
    { id: 31, name: '和歌山県' },
    { id: 32, name: '鳥取県' },
    { id: 33, name: '島根県' },
    { id: 34, name: '岡山県' },
    { id: 35, name: '広島県' },
    { id: 36, name: '山口県' },
    { id: 37, name: '徳島県' },
    { id: 38, name: '香川県' },
    { id: 39, name: '愛媛県' },
    { id: 40, name: '高知県' },
    { id: 41, name: '福岡県' },
    { id: 42, name: '佐賀県' },
    { id: 43, name: '長崎県' },
    { id: 44, name: '熊本県' },
    { id: 45, name: '大分県' },
    { id: 46, name: '宮崎県' },
    { id: 47, name: '鹿児島県' },
    { id: 48, name: '沖縄県' }
  ]
end
```
```ruby:app/models/delivery_time.rb
class DeliveryTime < ActiveHash::Base
  self.data = [
    { id: 1, name: '---' },
    { id: 2, name: '1~2日で発送' },
    { id: 3, name: '2~3日で発送' },
    { id: 4, name: '4~7日で発送' }
  ]
end
```

### モデルとの関連付けおよびバリデーションの設定
モデルでActiveHashを使用するために、Itemモデルに以下のように関連付け・バリデーションを追記
```ruby:app/models/item.rb
#中略
extend ActiveHash::Associations::ActiveRecordExtensions

  belongs_to :category
  belongs_to :status
  belongs_to :shipping_fee
  belongs_to :prefecture
  belongs_to :delivery_time

  validates :category_id, numericality: { other_than: 1, message: "can't be blank" }
  validates :status_id, numericality: { other_than: 1, message: "can't be blank" }
  validates :shipping_fee_id, numericality: { other_than: 1, message: "can't be blank" }
  validates :prefecture_id, numericality: { other_than: 1, message: "can't be blank" }
  validates :delivery_time_id, numericality: { other_than: 1, message: "can't be blank" }
```

### 各Activehashモデルにアソシエーションを追記
```ruby:app/models/Activehashモデル名
include ActiveHash::Associations
  has_many :items
```

### それぞれ使用したいActivehashモデルに対応した表示がされるようにビューファイル修正
```ruby:app/views/items/new.html.erb
#中略

<%= f.collection_select(:category_id, Category.all, :id, :name, {}, {class:"select-box", id:"item-category"}) %>

#中略

```

Itemモデル作成からActiveStorageを使用した画像投稿機能の実装とActivHashを使用したカテゴリーの選択ができる機能の実装までをまとめてみました。
次回はJavascriptを使用して実装を行なっていきます。