Rails6.0で作る交流会サイトの作成ログ(4)
▼免責事項
本記事により発生した如何なる損害についても当方は責任をおいません。
会員登録機能、ログイン機能、イベント投稿機能を作ってきました。
今回は、予約機能を作って行きたいと思います。
▼予約の流れ
・ログイン
・イベントを選択
・参加ボタンをクリック
・確認ページ
・確定ボタンをクリック
・予約完了。登録メールアドレスに完了メールを送信。
・マイページから予約履歴を表示。
マイページ!!!
作るの忘れてました。。。あとで作りますw
とりあえず、予約テーブルを作りましょう!
▼reservesテーブル
・userと関連付け
・イベントと関連付け
・参加済フラグ
こんな感じかな?
$ rails g scaffold Reserve
# db/migrate/日付_create_reserves.rb
class CreateReserves < ActiveRecord::Migration[6.0]
def change
create_table :reserves do |t|
t.references :user, foreign_key: true
t.references :meeting, foreign_key: true
t.boolean :finished, default: false
t.timestamps
end
add_index :reserves, [:user_id, :meeting_id], unique: true
end
end
# app/models/reserve.rb
class Reserve < ApplicationRecord
belongs_to :user
belongs_to :meeting
end
# app/models/user.rb
class User < ApplicationRecord
before_save { email.downcase! }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }, confirmation: true, unless: :uid?, on: :create
validates :image, presence: true
has_one_attached :image
has_secure_password
has_many :meetings,dependent: :destroy
has_many :reserves,class_name: "Reserve",dependent: :destroy
end
# app/models/meeting.rb
class Meeting < ApplicationRecord
has_one_attached :image
has_rich_text :body
belongs_to :user
validates :title, presence: true, length: { maximum: 200 }
validates :body, presence: true
validates :image, presence: true
validates :category, presence: true
validates :event_place, presence: true
validates :event_address, presence: true
validates :event_date, presence: true
validates :open_time, presence: true
validates :close_time, presence: true
validates :capacity, presence: true
validates :status, presence: true
has_many :reserves,class_name: "Reserve",dependent: :destroy
end
$ rails db:migrate
テーブルの作成と関連付けはこんな感じかな?
次は、イベント詳細ページに予約ボタンをはっつけます。
# app/views/meetings/show.html.erb
~省略~
<%= link_to "このイベントに申し込む",meeting_reserve_confirmation_path(@meeting),class: "btn btn-primary" %>
# config/routes.rb
~省略 resources :reservesを次のように変更する~
resources :meetings do
resources :reserves
end
# app/controllers/reserves_controller.rb
class ReservesController < ApplicationController
before_action :set_reserf, only: [:show, :edit, :update, :destroy]
def confirmation
end
~省略~
次にapp/views/reservesフォルダの中に、confirmation.html.erbファイルを作成します。
作成したファイルに、イベントの参加確認の情報を記載して行きましょう。基本的にmeetings/show.html.erbを参考に書いておきます。
# app/views/reserves/confirmation.html.erb
<p id="notice"><%= notice %></p>
<h1>イベント参加確認</h1>
<%= link_to "参加を確定する",meeting_reserves_path(@meeting),method: :post ,class: "btn btn-primary" %>
<p>
<strong>イベント作成者:</strong>
<%= @meeting.user.name %>
</p>
<p>
<strong>タイトル:</strong>
<%= @meeting.title %>
</p>
<p>
<strong>Image:</strong>
<%= image_tag @meeting.image,style: "width:300px;" %>
</p>
<p>
<strong>category:</strong>
<%= @meeting.category %>
</p>
<p>
<strong>event_place:</strong>
<%= @meeting.event_place %>
</p>
<p>
<strong>event_address:</strong>
<%= @meeting.event_address %>
</p>
<p>
<strong>event_date:</strong>
<%= @meeting.event_date %>
</p>
<p>
<strong>open_time~close_time:</strong>
<%= @meeting.open_time %> ~ <%= @meeting.close_time %>
</p>
<p>
<strong>status(1が下書き状態、0が公開中):</strong>
<%= @meeting.status %>
</p>
<p>
<strong>イベント詳細:</strong>
<div style="border:1px solid #d2d2d2;border-radius:5px;padding:30px;">
<%= @meeting.body %>
</div>
</p>
<%= link_to "参加を確定する",meeting_reserves_path(@meeting),method: :post ,class: "btn btn-primary" %>
<%= link_to 'Back', meeting_path(@meeting) %>
次は、reserves_controller.rbを編集しましょう!
# app/controllers/reserves_controlle.rb
class ReservesController < ApplicationController
before_action :set_reserve, only: [:show, :edit, :update, :destroy]
before_action :set_meeting
def confirmation
end
# GET /reserves
# GET /reserves.json
def index
@reserves = Reserve.all
end
# GET /reserves/1
# GET /reserves/1.json
def show
end
# GET /reserves/new
def new
@reserve = Reserve.new
end
# GET /reserves/1/edit
def edit
end
# POST /reserves
# POST /reserves.json
def create
@reserve = current_user.reserves.new(reserve_params)
respond_to do |format|
if @reserve.save
format.html { redirect_to meeting_reserf_path(@meeting,@reserve), notice: 'Reserve was successfully created.' }
format.json { render :show, status: :created, location: meeting_reserf_path(@meeting,@reserve) }
else
format.html { render :new }
format.json { render json: @reserve.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /reserves/1
# PATCH/PUT /reserves/1.json
def update
respond_to do |format|
if @reserve.update(reserve_params)
format.html { redirect_to meeting_reserf_path(@meeting,@reserve), notice: 'Reserve was successfully updated.' }
format.json { render :show, status: :ok, location: meeting_reserf_path(@meeting,@reserve) }
else
format.html { render :edit }
format.json { render json: @reserve.errors, status: :unprocessable_entity }
end
end
end
# DELETE /reserves/1
# DELETE /reserves/1.json
def destroy
@reserve.destroy
respond_to do |format|
format.html { redirect_to reserves_url, notice: 'Reserve was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_reserve
@reserve = Reserve.find(params[:id])
end
def set_meeting
@meeting = Meeting.find(params[:meeting_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def reserve_params
params.permit(:meeting_id)
end
end
コントローラーの次は、予約履歴にリダイレクトするようにします。
# app/views/reserves/show.html.erb
<p id="notice"><%= notice %></p>
<h1>イベントの予約状況</h1>
<p>
<strong>イベント作成者:</strong>
<%= @reserve.meeting.user.name %>
</p>
<p>
<strong>タイトル:</strong>
<%= @reserve.meeting.title %>
</p>
<p>
<strong>Image:</strong>
<%= image_tag @reserve.meeting.image,style: "width:300px;" %>
</p>
<p>
<strong>category:</strong>
<%= @reserve.meeting.category %>
</p>
<p>
<strong>event_place:</strong>
<%= @reserve.meeting.event_place %>
</p>
<p>
<strong>event_address:</strong>
<%= @reserve.meeting.event_address %>
</p>
<p>
<strong>event_date:</strong>
<%= @reserve.meeting.event_date %>
</p>
<p>
<strong>open_time~close_time:</strong>
<%= @reserve.meeting.open_time %> ~ <%= @reserve.meeting.close_time %>
</p>
<p>
<strong>status(1が下書き状態、0が公開中):</strong>
<%= @reserve.meeting.status %>
</p>
<p>
<strong>イベント詳細:</strong>
<div style="border:1px solid #d2d2d2;border-radius:5px;padding:30px;">
<%= @reserve.meeting.body %>
</div>
</p>
では、実際にうまく予約できるか動作確認をしましょう!イベントへのリンクだけ追加しておきました!
ちょっと、gifが壊れている見たいなのでリンクも貼っておきます。
とりあえず、イベントの予約機能は作れました!!パチパチパチ!
次は、予約が完了したら申込者とイベント主催者にメールを送る機能を作って行きましょう!
$ rails g mailer ReserveMailer
あと、開発中に実際にメールの送受信をするのは面倒なので、メールの送受信を確認できる便利なgemを入れておきます。
# Gemfile
~省略~
gem "letter_opener_web"
# config/environment/development.rb
~省略~
config.action_mailer.default_url_options = { host: 'localhost:3000' }
config.action_mailer.delivery_method = :letter_opener_web
~省略~
# config/routes.rb
Rails.application.routes.draw do
mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development?
~省略~
$ bundle
これで、「/letter_opener」にアクセスすればメールの確認ができるようになりました。
では、実際に送信するメールを作って行きましょう!
# app/mailers/reserve_mailer.rb
class ReserveMailer < ApplicationMailer
def reserve_complete(user,meeting)
# イベント予約者への申し込みメール
@user = user
@meeting = meeting
mail(
to: user.email,
subject: "お申し込みありがとうございます。#{meeting.title}"
)
end
def reserve_acceptance(user,meeting)
# イベント主催者への確認メール
@user = user
@meeting = meeting
mail(
to: meeting.user.email,
subject: "イベントの参加予約がありました。"
)
end
end
メールを送信する文章のviewを「app/views/reserve_mailer」フォルダの中に「reserve_complete.html.erb」と「reserve_acceptance.html.erb」を作ります。
# app/views/reserve_mailer/reserve_complete.html.erb
<div class="main">
<p><%= @user.name %>様</p><br>
<p><br>
<p>※このメールは自動返信メールです。</p>
<br>
<p><%= @meeting.title %>への参加予約を承りました。</p><br>
<br>
<br>
<br>
ーーーーーーーーーーーーーーーーーー<br>
▼参加イベント<br>
<%= @meeting.title %><br>
<br>
▼開催場所<br>
<p><%= @meeting.event_address %><br>
<%= @meeting.event_place %><br></p>
▼開催日時
<p><%= @meeting.event_date %><br>
<%= @meeting.open_time.strftime("%H:%M") %> 〜 <%= @meeting.close_time.strftime("%H:%M") %> </p>
<br>
▼主催者連絡先
<p><%= @meeting.user.name %><br>
連絡先:<%= @meeting.user.email %></p>
ーーーーーーーーーーーーーーーーーー<br><br>
以上、何卒よろしくお願いいたします。<br>
</div>
# app/views/reserve_mailer/reserve_acceptance.html.erb
<div class="main">
<p><%= @meeting.user.name %>様</p><br>
<p><br>
<p>※このメールは自動返信メールです。</p>
<br>
<p><%= @user.name %>様から、<br>
<%= @meeting.title %>への参加予約がありました。</p><br>
<br>
<br>
<br>
ーーーーーーーーーーーーーーーーーー<br>
▼お申し込み者<br>
<%= @user.name %><br>
<br>
▼連絡先<br>
<p><%= @user.email %><br>
▼予約イベント<br>
<p><%= @meeting.title %></p>
▼開催場所<br>
<p><%= @meeting.event_address %><br>
<%= @meeting.event_place %><br></p>
▼開催日時
<p><%= @meeting.event_date %><br>
<%= @meeting.open_time.strftime("%H:%M") %> 〜 <%= @meeting.close_time.strftime("%H:%M") %> </p>
<br>
ーーーーーーーーーーーーーーーーーー<br><br>
以上、何卒よろしくお願いいたします。<br>
</div>
送信メールが作成できたので、予約完了時に送るように設定します。
# app/controllers/reserve_controller.rb
~省略~
def create
@reserve = current_user.reserves.new(reserve_params)
respond_to do |format|
if @reserve.save
ReserveMailer.reserve_complete(current_user,@meeting).deliver_now # 予約者に完了メールを送信
ReserveMailer.reserve_acceptance(current_user,@meeting).deliver_now # 主催者へ予約をメール送信
format.html { redirect_to meeting_reserf_path(@meeting,@reserve), notice: 'Reserve was successfully created.' }
format.json { render :show, status: :created, location: meeting_reserf_path(@meeting,@reserve) }
else
format.html { render :new }
format.json { render json: @reserve.errors, status: :unprocessable_entity }
end
end
end
~省略~
さぁ、なんか予約してメールの受信を確認しましょう!
うん!!いい感じですね〜♪
さぁ、最後はユーザーのマイページを作成して自分が予約した履歴を確認できるようにしますー!
では、users以下にmypageファイルを作って諸々設定しましょう!
# app/views/users/mypage.html.erb
<h1>マイページ</h1>
<p>
<strong>Name:</strong>
<%= current_user.name %>
</p>
<p>
<strong>Email:</strong>
<%= current_user.email %>
</p>
<p>
<strong>Password digest:</strong>
<%= current_user.password_digest %>
</p>
<p>
<strong>Token digest:</strong>
<%= current_user.token_digest %>
</p>
<p>
<strong>Introduction:</strong>
<%= current_user.introduction %>
</p>
<p>
<strong>Image:</strong>
<%= image_tag current_user.image,style: "width:300px;" %>
</p>
# app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def mypage
end
~省略~
# config/routes.rb
Rails.application.routes.draw do
get "/mypage" => "users#mypage",as: "mypage"
~省略~
あと、トップページにマイページへのリンクを書き加えます。
とりあえず、マイページの作成完了!
次は、マイページに自分が予約したイベント一覧を表示させましょう。
イベントの取得は、「ユーザー→予約情報の未開催のもの→イベント情報」という順番で取得してます。
# app/views/users/mypage.html.erb
<h1>マイページ</h1>
<p>
<strong>Name:</strong>
<%= current_user.name %>
</p>
<p>
<strong>Email:</strong>
<%= current_user.email %>
</p>
<p>
<strong>Password digest:</strong>
<%= current_user.password_digest %>
</p>
<p>
<strong>Token digest:</strong>
<%= current_user.token_digest %>
</p>
<p>
<strong>Introduction:</strong>
<%= current_user.introduction %>
</p>
<p>
<strong>Image:</strong>
<%= image_tag current_user.image,style: "width:300px;" %>
</p>
<h2>イベント参加予約状況</h2>
<% current_user.reserves.where(finished: false).includes(:meeting).each do |reserve| %>
<p>
<strong>イベント作成者:</strong>
<%= reserve.meeting.user.name %>
</p>
<p>
<strong>タイトル:</strong>
<%= reserve.meeting.title %>
</p>
<p>
<strong>Image:</strong>
<%= image_tag reserve.meeting.image,style: "width:300px;" %>
</p>
<p>
<strong>category:</strong>
<%= reserve.meeting.category %>
</p>
<p>
<strong>event_place:</strong>
<%= reserve.meeting.event_place %>
</p>
<p>
<strong>event_address:</strong>
<%= reserve.meeting.event_address %>
</p>
<p>
<strong>event_date:</strong>
<%= reserve.meeting.event_date %>
</p>
<p>
<strong>open_time~close_time:</strong>
<%= reserve.meeting.open_time %> ~ <%= reserve.meeting.close_time %>
</p>
<p>
<strong>status(1が下書き状態、0が公開中):</strong>
<%= reserve.meeting.status %>
</p>
<p>
<strong>イベント詳細:</strong>
<div style="border:1px solid #d2d2d2;border-radius:5px;padding:30px;">
<%= reserve.meeting.body %>
</div>
</p>
<% end %>
では、確認してみましょう!
うん!マイページにイベント参加状況を表示させることができました!
確認メールも実装できたし、マイページに予約状況を表示できたし、とりあえずの機能は実装完了ですね!
さて、現状ログインしていなくてもマイページへのアクセスができたりするので、諸々のバグを修正して行きましょう!