##前提
- 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
いつもの流れですね。データベースの作成やマイグレーションファイルの記述など細かい点は省略します。
それでは早速ルートファイルから記述していきましょう。
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の記述です。
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ファイルを記述します。
- 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のパスを表示させています。
= 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テーブル)を追加できるようにしていくのでよければお付き合いください。