はじめに
本記事は第二章 マークアップ作業のつづきです。
Railsでフリマアプリの根幹機能となる商品出品・購入に必要なユーザ登録/ログイン機能を実装します。
gem"devise"
を使って実装します。また、ウィザード形式
という手法を用います。
制作するアプリ
ユーザ登録、商品出品・購入の機能を備えたフリマアプリです。
メルカリ風、というかほぼメルカリです。メルカリを作ります。
ちなみに、この規模のアプリは単価50万円ほどで取引されることが多いそうです。
通常納期は30日ほどだと思いますが、慣れてくれば1週間くらいで作れたりします。
この記事を使って、Web開発を極める一助にしていただければ幸いです。
OS : macOS Catalina 10.15.3
DB : MySQL
Ruby : 2.5.1
Rails: 5.2.4.2
フリマアプリ開発完全ロードマップ
章 | 内容 | 難易度 | 所要時間 | 主要技術 |
---|---|---|---|---|
序 章 | AWS自動デプロイ | ★☆☆☆☆ | ★★★☆☆ | capistrano |
第一章 | データベース設計 | ★☆☆☆☆ | ★☆☆☆☆ | |
第二章 | マークアップ作業 | ★★☆☆☆ | ★★★★★ | |
第三章 | ユーザ登録/ログイン機能 | ★★★★☆ | ★★☆☆☆ | devise |
第四章 | 商品出品/編集/詳細表示/削除 | ★★★★☆ | ★★★☆☆ | carrierwave |
第五章 | 商品カテゴリ機能 | ★★★★★ | ★★★☆☆ | ancestry |
第六章 | 商品購入機能 | ★★★★★ | ★★★★☆ | Payjp |
記事を読むにあたっての前提条件
この記事には私が作成した大量のコードが載っていますが、
コードレビューのための記事ではありませんので、「コードが汚い!」などのコメントはご遠慮願います。
リファクタリングについては、編集リクエストで私に直接お知らせ頂ければ幸いです。
ご理解のほど、何卒よろしくお願いいたします。
1.devise関連のビューを作成
以下のコマンドで一気に作成可能です。早速実行しましょう。
初期では全てerbファイルなのでhamlへの変換もついでにやっておきます。
rails g devise:views
rails haml:erb2haml
2.deviseコントローラの作成
まずはdeviseコントローラの作成です。以下のコマンドをターミナルで実行。
rails g devise:controllers users
このコマンドでapp/controllers/users
というディレクトリに、何個かコントローラが作成されます。
このコントローラを使用する目的は、ウィザード形式の実装に必要だからです。
住所登録の機能をユーザ登録と並行して行うために、
new_addressやcreate_addressといった自作アクションをこのコントローラに定義する必要が出てきます。
早速、作成したregistrations_controller.rb
のnewアクションを編集しましょう。
# GET /resource/sign_up
def new
@user = User.new
end
元々書いてあるsuper
という記述でも同じ動作をしますが、
理解を深めるために上記のように@user
を使うよ、と明記しましょう。
続いて、routes.rbを編集して上記のコントローラを使用することを明示します。
また、先ほどビューの作成を行った事で、user関連の仮ルーティングが必要なくなったので、ついでに削除します。
Rails.application.routes.draw do
# ↓編集箇所ここから
devise_for :users, controllers: {
registrations: 'users/registrations',
}
# ↑編集箇所ここまで
$date = Time.now.in_time_zone('Tokyo').to_s
root "products#index"
resources :products do
collection do
get "product_create" ,to: 'products#create'
get "product_update" ,to: 'products#update'
get "product_destroy",to: 'products#destroy'
end
end
# ↓編集箇所ここから
resources :users, only: :show
# ↑編集箇所ここまで
end
以上がコントローラ作成です。続いて、ビューの設定をしていきます。
3.deviseビューの設定
上記で仮ルーティングを修正したので、変更をビューに反映します。
修正するのは、_header.html.haml
とnew_session.html.haml
です。
#request{action: request.path}
.header
.header__top
= link_to root_path do
%i{class: "fab fa-pagelines"}
%span{class: "header__top__logo"}
Frema
%input{class: "header__top__input", id: "serch-box", placeholder: "何かお探しですか?"}
.header__bottom
%ul
%li
%i{class: "fas fa-list"}
カテゴリーから探す
%li
%i{class: "fas fa-tags"}
ブランドから探す
%li
%i{class: "fas fa-bell"}
お知らせ
%li
%i{class: "fas fa-check"}
やることリスト
%li
// ↓修正箇所ここから
= link_to new_user_session_path do
%i{class: "fas fa-smile"}
ログイン/新規会員登録
// ↑修正箇所ここまで
.new_session-wrapper
.new_session-wrapper__logo
= link_to root_path do
%i{class: "fab fa-pagelines"}
%span{class: "new_session-wrapper__logo__text"}
Frema
.new_session-wrapper__main
.new_session-wrapper__main__title.text-align-center
アカウントをお持ちでない方はこちら
// ↓修正箇所ここから
= link_to new_user_registration_path, class: "new_session-wrapper__main__btn" do
新規会員登録
// ↑修正箇所ここまで
.new_session-wrapper__main
%input{class: "new_session-wrapper__main__text",placeholder: "メールアドレス"}
%input{class: "new_session-wrapper__main__text",placeholder: "パスワード"}
%input{type:"submit", class: "new_session-wrapper__main__btn", value: "ログイン"}
次に、deviseビューの中身を自作のビューに置き換えます。
自作ビュー(views/users/new_session.html.haml
)のコードを全てコピーし、
deviseビュー(views/devise/sessions/new.html.haml
)に貼り付けます。
上記の作業が終わったら、自作ビューのファイルは不要になるので、削除してOK。
上記と同様に、以下のファイルも中身の入れ替えをしてください。
コピー元ファイル | 貼り付け先ファイル | 備考 |
---|---|---|
users/new_session.html.haml | devise/sessions/new.html.haml | 上記で作業済み |
users/new_user.html.haml | devise/registrations/new.html.haml | |
users/new_address.html.haml | devise/registrations/new_address.html.haml | フォルダ移動でOK |
users/create_address.html.haml | devise/registrations/create_address.html.haml | フォルダ移動でOK |
ビューの設定はここまで。続けて、form_with
などを使って機能の実装に移ります。
4.とりあえずユーザ登録できるようにする
ウィザード形式の前に、まずは普通にユーザ登録をできるようにしておきます。
前章で以下のような画面を作りましたが、まずはこの画面だけでユーザ登録を成立させます。
ちょっと変ですが、必須事項を入力して次へ進む
をクリックしたらユーザ登録が完了するようにしましょう。
何を実装するにしても、ひとつひとつ段階を踏むのがオススメです。
以下のようにregistrations/new.html.haml
をform_withで書き換えます。
= form_with model: @user,url: user_registration_path do |f|
.new_session-wrapper
.new_session-wrapper__logo
= link_to root_path do
%i{class: "fab fa-pagelines"}
%span{class: "new_session-wrapper__logo__text"}
Frema
%span{class: "new_session-wrapper__logo__title title-active after-active"}
会員情報
%span{class: "new_session-wrapper__logo__title"}
住所情報
%span{class: "new_session-wrapper__logo__title-done"}
登録完了
.new_session-wrapper__main
.new_session-wrapper__main__head
会員情報
.new_session-wrapper__main
= render "devise/shared/error_messages", resource: @user
.new_session-wrapper__main__title
ニックネーム
%span{class: "require"} 必須
= f.text_field :nickname, {class: "new_session-wrapper__main__text", placeholder: "例)フリマ太郎"}
.new_session-wrapper__main__title
メールアドレス
%span{class: "require"} 必須
= f.text_field :email, {class: "new_session-wrapper__main__text", placeholder: "PC・携帯どちらでも可"}
.new_session-wrapper__main__title
パスワード
%span{class: "require"} 必須
= f.password_field :password, {class: "new_session-wrapper__main__text", placeholder: "7文字以上、英字数字両方を含むこと"}
.new_session-wrapper__main__title
パスワード(確認)
%span{class: "require"} 必須
= f.password_field :password_confirmation, {class: "new_session-wrapper__main__text", placeholder: "7文字以上、英字数字両方を含むこと"}
.new_session-wrapper__main__title
お名前(全角)
%span{class: "require"} 必須
.flexbox
= f.text_field :firstname, {class: "new_session-wrapper__main__text text-half", placeholder: "例)山田"}
= f.text_field :lastname , {class: "new_session-wrapper__main__text text-half", placeholder: "例)彩"}
.new_session-wrapper__main__title
お名前カナ(全角)
%span{class: "require"} 必須
.flexbox
= f.text_field :firstname_kana, {class: "new_session-wrapper__main__text text-half", placeholder: "例)ヤマダ"}
= f.text_field :lastname_kana , {class: "new_session-wrapper__main__text text-half", placeholder: "例)アヤ"}
.new_session-wrapper__main__title
生年月日
%span{class: "require"} 必須
.flexbox
- year = ["--"]
- for num in 0..120
- year << 2020-num
= f.select :birth_year, year, {}, class: "new_session-wrapper__main__select three-select"
年
- month = ["--"]
- for num in 1..12
- month << num
= f.select :birth_month, month, {}, class: "new_session-wrapper__main__select three-select"
月
- day = ["--"]
- for num in 1..31
- day << num
= f.select :birth_day, day, {}, class: "new_session-wrapper__main__select three-select"
日
.new_session-wrapper__main__title
電話番号
%span{class: "require"} 必須
= f.text_field :tel_number, {class: "new_session-wrapper__main__text", placeholder: "ハイフンなし10~11桁"}
.new_session-wrapper__main__title
.new_session-wrapper__main__caution
※本人情報は正しく入力してください。会員登録後、お時間を頂く場合があります。
= f.submit "次へ進む", class: "new_session-wrapper__main__btn"
select
の中身は、配列を定義して記述してみました。
ビューでやるのはちょっと...という方はコントローラで@year
や@month
といった
配列オブジェクトを定義し、ビューに渡すのもアリだと思います。お好みでどうぞ。
続いてapplication_controller.rb
を以下のように編集します。
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:firstname, :lastname, :firstname_kana, :lastname_kana, :nickname, :birth_year, :birth_month, :birth_day, :tel_number])
end
end
deviseが受け取るパラメータは初期状態でname
とemail
だけなので、
今回追加したnickname
やfirstname
などを受け取れるようにするための編集です。
これでユーザを新規登録できるようになります。
適当なダミーデータを入力して、新規登録してみてください。
ログイン後、ヘッダーのログイン/新規登録
ボタンがマイページへ
ボタンに変わっていればログインできてます。
この後もユーザ登録機能を引き続きデバッグするので、
マイページ左下のログアウトボタンで一旦ログアウトしておきます。
5.ウィザード形式の実装
お待ちかね、ウィザード形式の実装です。
以下のような手順で進めます。
1. 次へ進む
をクリックした後にnew_address
に遷移させる
2. 遷移しても@user
の中身を保持できるようにする
3. 遷移先で@address
を新たに生成する
4. バリデーションをチェックし、問題がなければ完了画面に遷移させる
それでは、実際に手を動かして実装作業を進めます。
1. 次へ進む
をクリックした後にnew_address
に遷移させる
new_address
へのルーティングが必要になります。
routes.rb
を以下のように編集しましょう。
Rails.application.routes.draw do
devise_for :users, controllers: {
registrations: 'users/registrations',
}
# 編集箇所ここから
devise_scope :user do
get 'addresses', to: 'users/registrations#new_address'
post 'addresses', to: 'users/registrations#create_address'
end
# 編集箇所ここまで
$date = Time.now.in_time_zone('Tokyo').to_s
root "products#index"
resources :products do
collection do
get "product_create" ,to: 'products#create'
get "product_update" ,to: 'products#update'
get "product_destroy",to: 'products#destroy'
end
end
resources :users, only: :show
end
続けて、registrations_controller.rb
のcreateアクションを編集します。
# POST /resource
def create
@user = User.new(sign_up_params)
if @user.valid?
redirect_to addresses_path
else
render :new
end
end
これで遷移自体は可能になりますが、このままだと遷移後に@user
の情報が消滅します。
なので、sessionという機能を使って@user
を保持できるようにします。
2. 遷移しても@user
の中身を保持できるようにする
registrations_controller.rb
のcreateアクションを編集します。
# POST /resource
def create
@user = User.new(sign_up_params)
if @user.valid?
session["devise.regist_data"] = {user: @user.attributes}
session["devise.regist_data"][:user]["password"] = params[:user][:password]
redirect_to addresses_path
else
render :new
end
end
session["devise.regist_data"]
という記述を用いる事で、遷移後も@user
の中身を保持できます。
{user: @user.attributes}
という記述については、少々複雑な話になるので、あまり気にしなくてもOKです。
一応要点だけ説明すると、パスワードの情報を保持するのに必要な記述です。
単にsession["devise.regist_data"] = @user
と記述してしまうと、
パスワードの情報の保持が面倒になります。これを回避しています。
また、2つめのsessionの記述も同様にパスワードの保持に関わる記述です。
3. 遷移先で@address
を新たに生成する
registrations_controller.rb
にnew_address
という自作アクションを定義します。
def new_address
@user = User.new(session["devise.regist_data"]["user"])
@address = Address.new
end
これで遷移先のnew_addressビューでform_with
が使えるようになったので、書き換えていきます。
= form_with model: @address, local: true do |f|
.new_session-wrapper
.new_session-wrapper__logo
= link_to root_path do
%i{class: "fab fa-pagelines"}
%span{class: "new_session-wrapper__logo__text"}
Frema
%span{class: "new_session-wrapper__logo__title before-active after-active"}
会員情報
%span{class: "new_session-wrapper__logo__title title-active after-active"}
住所情報
%span{class: "new_session-wrapper__logo__title-done"}
登録完了
.new_session-wrapper__main
.new_session-wrapper__main__head
住所情報
.new_session-wrapper__main
.new_session-wrapper__main__title
郵便番号
%span{class: "require"} 必須
= f.text_field :post_number, {class: "new_session-wrapper__main__text", placeholder: "例)123-4567"}
.new_session-wrapper__main__title
都道府県
%span{class: "require"} 必須
= f.select :prefecture, ["北海道","青森県","岩手県","宮城県","秋田県","山形県","福島県","茨城県","栃木県","群馬県","埼玉県","千葉県","東京都","神奈川県","新潟県","富山県","石川県","福井県","山梨県","長野県","岐阜県","静岡県","愛知県","三重県","滋賀県","京都府","大阪府","兵庫県","奈良県","和歌山県","鳥取県","島根県","岡山県","広島県","山口県","徳島県","香川県","愛媛県","高知県","福岡県","佐賀県","長崎県","熊本県","大分県","宮崎県","鹿児島県","沖縄県"], {}, class: "new_session-wrapper__main__select"
.new_session-wrapper__main__title
市区町村
%span{class: "require"} 必須
= f.text_field :city, {class: "new_session-wrapper__main__text", placeholder: "例)渋谷区"}
.new_session-wrapper__main__title
番地
%span{class: "require"} 必須
= f.text_field :address, {class: "new_session-wrapper__main__text", placeholder: "例)道玄坂3-2"}
.new_session-wrapper__main__title
建物名
%span{class: "any"} 任意
= f.text_field :apartment, {class: "new_session-wrapper__main__text", placeholder: "例)フォンティスビル7F"}
.new_session-wrapper__main__caution
※本人情報は正しく入力してください。会員登録後、お時間を頂く場合があります。
%input{type: "submit",class: "new_session-wrapper__main__btn", value: "次へ進む"}
4. バリデーションをチェックし、問題がなければ完了画面に遷移させる
registrations_controller.rb
にcreate_address
という自作アクションを定義します。
また、前述のcreateアクション(userの情報を保存するほう)のストロングパラメータは、
deviseに用意されているものを使いましたが、自作アクションにおいては
自分でストロングパラメータを定義する必要があります。
def create_address
@user = User.new(session["devise.regist_data"]["user"])
@address = Address.new(address_params)
if @address.valid?
@user.save
@address = Address.new(address_params.merge(user_id: @user.id))
@address.save
session["devise.regist_data"]["user"].clear
sign_in(:user, @user)
else
render :new_address
end
end
protected
def address_params
params.require(:address).permit(:post_number, :prefecture, :city, :address, :apartment)
end
これでウィザード形式を使ったユーザ登録が可能になりました!!
6.ログイン機能の実装
メールアドレスとパスワードの入力でログインを可能にします。すぐ終わります。
sessions/new.html.haml
を以下のように編集します。
.new_session-wrapper
.new_session-wrapper__logo
= link_to root_path do
%i{class: "fab fa-pagelines"}
%span{class: "new_session-wrapper__logo__text"}
Frema
.new_session-wrapper__main
.new_session-wrapper__main__title.text-align-center
アカウントをお持ちでない方はこちら
= link_to new_user_registration_path, class: "new_session-wrapper__main__btn" do
新規会員登録
.new_session-wrapper__main
= form_with model:resource, url: session_path(resource_name) do |f|
= f.text_field :email, {class: "new_session-wrapper__main__text",placeholder: "メールアドレス"}
= f.password_field :password, {class: "new_session-wrapper__main__text",placeholder: "パスワード"}
= f.submit "ログイン", {class: "new_session-wrapper__main__btn"}
form_with
を使用してmodel
をresource
、
url
をsession_path(resource_name)
とすればログイン機能になります。
これは暗記でいいかなと思います。もしくはメモ帳にメモです。
お疲れ様でした!!
以上がdeviseによるウィザード形式を用いたユーザ登録/ログイン機能の実装でした。
第四章 商品出品/編集/詳細表示/削除に続きます。
章 | 内容 | 難易度 | 所要時間 | 主要技術 |
---|---|---|---|---|
序 章 | AWS自動デプロイ | ★☆☆☆☆ | ★★★☆☆ | capistrano |
第一章 | データベース設計 | ★☆☆☆☆ | ★☆☆☆☆ | |
第二章 | マークアップ作業 | ★★☆☆☆ | ★★★★★ | |
第三章 | ユーザ登録/ログイン機能 | ★★★★☆ | ★★☆☆☆ | devise |
第四章 | 商品出品/編集/詳細表示/削除 | ★★★★☆ | ★★★☆☆ | carrierwave |
第五章 | 商品カテゴリ機能 | ★★★★★ | ★★★☆☆ | ancestry |
第六章 | 商品購入機能 | ★★★★★ | ★★★★☆ | Payjp |
あとがき
この記事のここをもっと解説してほしい!などありましたら、コメント欄でお伝えください。
また、コメント欄での暴言や誹謗中傷の書き込み等はご遠慮ください。マジで凹むので。
いつも記事を読んで頂いている皆さま、LGTMを押していただいている皆さま、本当にありがとうございます!!
有益な記事を執筆するモチベーションに繋がりますので、この記事が役に立ったなと思って頂けたら、
是非LGTMボタンのクリックやSNSでのシェアをよろしくお願いします。