Railsで商品情報編集機能を実装したい

##前提
- ruby on rails 6.0.0 を使用。
- ユーザー機能はdeviseにより導入されているものとする。
- viewファイルは全てhaml形式とする。
- ちなみに使っているのはMacBook Air(Retina, 13-inch, 2020)です。

##はじめに
プログラミング学習のため、某フリマサイトのコピーサイトを作っています。
商品情報編集機能を実装したが、画像の複数投稿になかなか苦戦したので参考になればと思い書くことにしました。
大した量じゃないのにpart分けしているのはあとで見たときに達成感を得るためです。許してください。(いっぱい書いたぞ！ってなりたい)
part1で特に詰まる部分はないと思いますが、part2以降の土台になってくるのでウォーミングアップ程度に書いていきます。

##仕様書
- 商品に登録された情報をひとつひとつ変更できる。
- 商品情報を編集できるのは商品を登録したユーザーのみ。
- 画像の差し替えは一枚ごとにできる。
- 商品名や画像など、すでに登録されている情報は編集画面にあらかじめ表示される。
- エラーハンドリングを行う。

######(マークアップまで完成したら参考画像を載せようと思ってます)

##手順
1, 土台となるコントローラやモデルの作成。
2, imageテーブルを関連づけて画像の投稿を実装。
3, jQueryを導入して複数画像の投稿を実装。
4, 編集画面における調整。
5, 画像のプレビューをする。

これら5つの手順をそれぞれpart分けして実装していくことにする。

それでは、

##いざ実装へ
まずは基礎的な部分を固めていく。
最初にターミナルから。
> $ *rails g contoroller products*
> $ *rails g model product*
> $ *rails db:migrate*

いつもの流れですね。データベースの作成やマイグレーションファイルの記述など細かい点は省略します。

それでは早速ルートファイルから記述していきましょう。

```ruby:app/config/routes.rb
Rails.application.routes.draw do
  root "products#index"
  resources :products
  devise_for :users, controllers: {
    registrations: 'users/registrations'
  }
end
```
deviseの部分はあらかじめ実装してあるユーザー機能に関する部分なので気にしなくて大丈夫です。
ここも細かい説明は不要ですね。resourcesで基本のアクションを作り、ルートパスにindexアクションを指定しています。(今回、destroyとshowは使わないのでexceptして頂いても大丈夫です)

次にcontrollerの記述です。

```ruby:app/controllers/products_controller.rb
class ProductsController < ApplicationController
  before_action :ensure_current_user, only[:edit, :update]
  before_action :set_product, only[:new, :create, :edit, :update]

  def index
    @products = Product.all
  end

  def new
    @prodcut = Product.new
  end
  def create
    @product = Product.new(product_params)
    if @product.save
      redirect_to products_path
    else
      render :new
    end
  end

  def edit
  end
  def update
    if @product.update(product_params)
      redirect_to products_path
    else
      render :edit
    end
  end

  private
  def product_params
    params.require(:product).permit(:name).merge(user_id: current_user.id)
  end
  def ensure_current_user
    product = Product.find(params[:id])
    if product.user_id != current_user.id
      redirect_to action: :index
    end
  end
  def set_product
    @product = Product.find(params[:id])
  end
end
```
ひとまずはこんなところでしょうか。
順を追って解説していきます。

まずは基本のアクションについて。
> *@products = Product.all*

indexは問題ないですね、productテーブルに登録された全てのレコードを取り出しています。
> *@prodcut = Product.new*

new,createでは商品の登録を行っています。モデル.newで新しいオブジェクトを生成し、そこにフォームの値を代入します。
>*def product_params*
>　*params.require(:product).permit(:name).merge(user_id: current_user.id)*
>*end*

そうして送信されたデータをproduct_paramsメソッドで受け取っています。みんな大好きストロングパラメータですね。
最低限の機能を実装するだけなのでカラムはnameだけにしています。

> *if @product.save*
> 　*redirect_to products_path*
> *else*
> 　*render :new*
> *end*

続くif文ですが、これがエラーハンドリングというやつです。
処理が成功した場合はindexへ、逆に失敗した場合はnewを再表示させています。
概念としては複雑な部分もありますが、機能としてはもうお馴染みといっていいと思います。
> @product = Product.find(params[:id])

次にedit,updateアクションとなりますが、やっていることは先ほどと同じです。createと違うところと言えば、すでにデータが存在するので新しくオブジェクトを作る必要がなく、findメソッドで選択したproductをもってきていることです。
> *def ensure_current_user*
> 　*product = Product.find(params[:id])*
> 　　*if product.user_id != current_user.id*
> 　　*redirect_to action: :index*
> 　*end*
> *end*

最後にensure_current_userメソッドです。
一見難しそうに見えますが処理はすごく簡単。要は選択したproductのユーザー情報とログインしているユーザー情報が違う場合は編集できませんよーってことです。

さて、コントローラーの記述が終わったのでそろそろviewファイルを記述します。

```haml:app/view/products/index.html.haml
- if user_signed_in?
  - @products.each do |product|
    - if product.user_id == current_user.id
      = link_to edit_product_path(product.id) do
        #{product.name}
  = link_to("出品", new_product_path)

  %h2 ログインしています
  = link_to 'ログアウト', destroy_user_session_path, method: :delete

- else
  %h2 ログインしていません
  = link_to '新規登録', new_user_registration_path
  = link_to 'ログイン', new_user_session_path
```
まずはindexからですが、着目すべきは最初の6行だけです。
ユーザーがログインしているか。
その商品はそのユーザーが登録したものか。
といった二つの条件のもと、editとnewのパスを表示させています。

```haml:app/views/products/_form.html.haml
= form_with model: @product, local: true do |f|
  = f.text_field :name, placeholder: 'name'
  = f.submit 'SEND'
```
こちらも特別に説明すべきことはないと思います。
form_withメソッドを使ってproductモデルにデータを送るというものですね。あとはこいつを部分テンプレートとしてnew.html.hamlとedit.html.hamlにrender表示させるだけです。

##最後に
ここまでで、商品のデータを作成し、それを編集する機能を実装することができました。(カラムは名前だけですが、、、)

仕様書でいうところの、

- 商品に登録された情報をひとつひとつ変更できる。
- 商品情報を編集できるのは商品を登録したユーザーのみ。
- エラーハンドリングを行う。

これら3つをクリアすることができたことになります。
次のpartでは画像(imageテーブル)を追加できるようにしていくのでよければお付き合いください。

<a href="https://qiita.com/silvercrow222/items/5487562fa349df0ceb9c">商品情報編集機能を実装したい~part2~</a>