予約機能の作成
この記事で行うこと
ユーザーがチケットを予約できるようにする。
使うもの
Ruby 2.4.2 Rails 5.1.6
前提
ユーザーの作成 - さがえもんを完了していること。
実装手順
① チケットの表示
チケット一覧をトップページに表示します。 チケットはログインしているユーザーもログインしていないユーザーも見ることができます。
welcome_controller.rb
class WelcomeController < ApplicationController def index @tickets = Ticket.all end end
app/views/welcome/index.html.haml
.col-md-12 %h3.text-center チケット一覧 %table.table %thead %tr %th %th チケット名 %th 内容 %th.text-center チケット販売可能枚数 %th.text-center 締切日 %tbody - @tickets.each.with_index(1) do |ticket, index| %tr %td= index %td= ticket.title %td= ticket.body %td.text-center= ticket.number %td.text-center= ticket.expired_at
② 予約とユーザーの関連付け
ユーザーはチケットを複数予約することができます。 ユーザーとチケットの中間モデルを予約が行うのでモデルの関係性は以下のようになります。
ER図の書き方
まず予約(Delivery)を作成します。
$ rails g model Delivery Running via Spring preloader in process 35173 invoke active_record create db/migrate/20180727113318_create_deliveries.rb create app/models/delivery.rb invoke test_unit create test/models/delivery_test.rb create test/fixtures/deliveries.yml
ユーザー・予約・チケットを関連付けします。
app/models/user.rb
class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable # ------------------------------------------------------------------------------- # Relations # ------------------------------------------------------------------------------- has_many :deliveries, dependent: :destroy end
app/models/ticket.rb
class Ticket < ApplicationRecord # ------------------------------------------------------------------------------- # Relations # ------------------------------------------------------------------------------- has_many :deliveries, dependent: :destroy end
app/models/delivery.rb
class Delivery < ApplicationRecord # ------------------------------------------------------------------------------- # Relations # ------------------------------------------------------------------------------- belongs_to :user belongs_to :ticket end
db/migrate/20180727113318_create_deliveries.rb
class CreateDeliveries < ActiveRecord::Migration[5.1] def change create_table :deliveries do |t| t.belongs_to :user, foreign_key: true t.belongs_to :ticket, foreign_key: true t.timestamps end end end
マイグレートコマンドを実行しましょう。
$ rails db:migrate == 20180727113318 CreateDeliveries: migrating ================================= -- create_table(:deliveries) -> 0.0059s == 20180727113318 CreateDeliveries: migrated (0.0059s) ========================
これで関連付けは終了です。
③ 確認画面
ユーザーがチケットを予約ボタンまで辿りつけるようにします。
チケット一覧画面に「予約する」ボタンを設置しましょう。 この「予約する」ボタンを押すと確認画面に遷移します。
Deliveryコントローラーを作成します。 Userスコープで作成しましょう。
$ rails g controller Users::Deliveries Running via Spring preloader in process 35321 create app/controllers/users/deliveries_controller.rb invoke haml create app/views/users/deliveries invoke test_unit create test/controllers/users/deliveries_controller_test.rb invoke helper create app/helpers/users/deliveries_helper.rb invoke test_unit invoke assets invoke coffee create app/assets/javascripts/users/deliveries.coffee invoke scss create app/assets/stylesheets/users/deliveries.scss
予約はログインユーザーしかできない仕様ですので、未登録・未ログインユーザーにはログインを強制します。 ログインリダイレクトのコールバックを作成しますが、今後予約以外でもログインしてほしい場面が出てくると思うので共通化しておきます。
BaseコントローラーをUserスコープに作成します。
$ rails g controller Users::Base Running via Spring preloader in process 35374 create app/controllers/users/base_controller.rb invoke haml create app/views/users/base invoke test_unit create test/controllers/users/base_controller_test.rb invoke helper create app/helpers/users/base_helper.rb invoke test_unit invoke assets invoke coffee create app/assets/javascripts/users/base.coffee invoke scss create app/assets/stylesheets/users/base.scss
baseコントローラーのコールバックにdeviseのauthenticate_user!を置きます。 app/controllers/users/base_controller.rb
class Users::BaseController < ApplicationController before_action :authenticate_user! end
DeliveryコントローラーはこのBaseコントローラーを継承することで、
before_action :authenticate_user!
を使用できるようになります。(AppricationController
-> Users::BaseController
)
app/controllers/users/deliveries_controller.rb
class Users::DeliveriesController < Users::BaseController end
これで、未登録・未ログインユーザーにはログインを強制することができるようになりました。
続いて、確認画面を作成していきましょう。
app/controllers/users/deliveries_controller.rb
class Users::DeliveriesController < Users::BaseController def new @ticket = Ticket.find(params[:ticket_id]) #チケット情報を確認画面で表示するため end end
app/views/users/deliveries/new.html.haml
.col-md-12 %h3.text-center 予約確認画面 %table.table %thead %tr %th チケット名 %th 内容 %th.text-center チケット販売可能枚数 %th.text-center 締切日 %tbody %tr %td= @ticket.title %td= @ticket.body %td.text-center= @ticket.number %td.text-center= @ticket.expired_at
次にルーティングですが、リソースをネストさせます。
config/routes.rb
Rails.application.routes.draw do root 'welcome#index' devise_for :users, path: 'users', controllers: { registrations: 'users/registrations', # 登録 confirmations: 'users/confirmations', # 確認 sessions: 'users/sessions' # ログイン } resources :tickets, only: %i(new create index) # # ユーザー # namespace :users do resources :tickets do resources :deliveries, only: %i(new) do get :confirm, to: 'deliveries#new', on: :collection end end end end
詳しく知りたい方は routing(ルーティング)まとめ - さがえもん をご覧ください。
最後にトップページから、確認画面に遷移できるようにリンクを貼りましょう。
app/views/welcome/index.html.haml
.col-md-12 %h3.text-center チケット一覧 %table.table %thead %tr %th %th チケット名 %th 内容 %th.text-center チケット販売可能枚数 %th.text-center 締切日 %th ## 追加 %tbody - @tickets.each.with_index(1) do |ticket, index| %tr %td= index %td= ticket.title %td= ticket.body %td.text-center= ticket.number %td.text-center= ticket.expired_at %td.text-center ## 追加 = link_to '予約する', confirm_users_ticket_deliveries_path(ticket), class: 'btn btn-primary' ## 追加
動作確認
トップページ
確認画面
④ 予約作成
予約作成の処理を実装します。 確認画面に「予約する」ボタンを置きます。その「予約する」ボタンをクリックすると、予約が作成されて予約完了画面に遷移します。
Deliveryコントローラーに予約を作成するcreateアクションを定義し、処理をかきます。 app/controllers/users/deliveries_controller.rb
class Users::DeliveriesController < Users::BaseController before_action :set_ticket, only: %i(new create) def new @delivery = Delivery.new end def create @delivery = current_user.deliveries.new(ticket: @ticket) if @delivery.save redirect_to # 予約完了画面のパス, notice: '予約しました' else render :new end end private def set_ticket @ticket = Ticket.find(params[:ticket_id]) end end
@ticket
はnewとcreateの両方に使用しますのでコールバックでまとめます。
Deliveryを初期化しますが、current_user.deliveries
と書くことで Delivery.new(user: current_user, ticket: @ticket)
よりも短く書くことができます。
create
をルーティングに追加します。
config/routes.rb
・・・ resources :deliveries, only: %i(new create) do ・・・
確認画面に「予約する」リンクを追加します。
app/views/users/deliveries/new.html.haml
.col-md-12 %h3.text-center 予約確認画面 %table.table %thead %tr ・・・・ %th.text-center 締切日 %th # 追加 %tbody %tr ・・・・ %td.text-center= @ticket.expired_at %td.text-center= link_to '予約する', [:users, @ticket, @delivery], method: :post, class: 'btn btn-primary' # 追加
そのまま予約完了画面を実装しましょう。
app/views/users/deliveries/complete.html.haml
.col-md-12 .text-center= link_to 'ホームに戻る', :root
app/controllers/users/deliveries_controller.rb
・・・ def create ・・・・ if @delivery.save redirect_to :users_complete, notice: '予約しました' ・・・ end end def complete end ・・・
config/routes.rb
# # ユーザー # namespace :users do resources :tickets do ・・・ end get :complete, to: 'deliveries#complete' #追加 end
以上で予約作成の実装は完了です。 最後に動作確認をしましょう。
動作確認
トップページトップページの「予約する」ボタンをクリックすると、ログイン画面にリダイレクトされる。
トップページからナビゲーションバーのサインアップリンクをクリックしてサインアップする。
トップページから「予約する」ボタンを押すと、予約確認画面に遷移する。
「予約する」ボタンを押すと、予約が作成され予約が作成される。
ソースコード
GitHub - sagaekeiga/ticket-reservation
質疑応答
コメントしてください。