レビューモデルを作成します。
コマンド
1文です。
rails g model Review comment:text star:bigint room:references reservation:references guest:references host:references type
コマンド
rails g model GuestReview --parent=Review --migration=false
コマンド
rails g model HostReview --parent=Review --migration=false
記述更新 db\migrate\20200629041702_create_reviews.rb
「,default: 1」の記述追加(5行目)と「, foreign_key: { to_table: :users }」の記述追加(8,9行目)をしています。
class CreateReviews < ActiveRecord::Migration[5.0] def change create_table :reviews do |t| t.text :comment t.bigint :star, default: 1 t.references :room, foreign_key: true t.references :reservation, foreign_key: true t.references :guest, foreign_key: { to_table: :users } t.references :host, foreign_key: { to_table: :users } t.string :type t.timestamps end end end
コマンド マイグレーション
rails db:migrate
app\models\guest_review.rb
「belongs_to :guest, class_name: "User"」の追加(2行目)
class GuestReview < Review belongs_to :guest, class_name: "User" end
app\models\host_review.rb
「belongs_to :host, class_name: "User"」の追加(2行目)
class HostReview < Review belongs_to :host, class_name: "User" end
記述削除 app\models\review.rb
自動で記述された部分を削除します。
class Review < ApplicationRecord end
「app\models\room.rb」ファイルを編集します。
1.記述追加 app\models\room.rb(6行目)
has_many :guest_reviews
2.記述追加 app\models\room.rb(25行目)
def average_rating guest_reviews.count == 0 ? 0 : guest_reviews.average(:star).round(2).to_i end
記述追加 app\models\room.rb
class Room < ApplicationRecord belongs_to :user has_many :photos has_many :reservations has_many :guest_reviews geocoded_by :address after_validation :geocode, if: :address_changed? validates :home_type, presence: true validates :room_type, presence: true validates :accommodate, presence: true validates :bed_room, presence: true validates :bath_room, presence: true def cover_photo(size) if self.photos.length > 0 self.photos[0].image.url(size) else "blank.jpg" end end def average_rating guest_reviews.count == 0 ? 0 : guest_reviews.average(:star).round(2).to_i end end
「app\models\user.rb」ファイルを編集します。
記述追加 app\models\user.rb(19,20行目)
has_many :guest_reviews, class_name: "GuestReview", foreign_key: "guest_id" has_many :host_reviews, class_name: "HostReview", foreign_key: "host_id"
記述追加 app\models\user.rb
class User < ApplicationRecord # アバター画像表示用 include Gravtastic gravtastic # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :confirmable #長さ50文字以下 入力必須 validates :fullname, presence: true, length: {maximum: 50} has_many :rooms has_many :reservations has_many :guest_reviews, class_name: "GuestReview", foreign_key: "guest_id" has_many :host_reviews, class_name: "HostReview", foreign_key: "host_id" end
レビューコントローラを作成します。
「app\controllers」フォルダに「「host_reviews_controller.rb」ファイルを新規作成してください。
app\controllers\host_reviews_controller.rb(新規作成したファイル)
class HostReviewsController < ApplicationController def create # ステップ 1: 予約が存在するかどうかを確認します(room_id、guest_id、host_id) # ステップ 2: 現在のホストがこの予約のゲストをすでにレビューしているかどうかを確認します。 @reservation = Reservation.where( id: host_review_params[:reservation_id], room_id: host_review_params[:room_id], user_id: host_review_params[:guest_id] ).first if !@reservation.nil? @has_reviewed = HostReview.where( reservation_id: @reservation.id, guest_id: host_review_params[:guest_id] ).first if @has_reviewed.nil? # レビューを許可 @host_review = current_user.host_reviews.create(host_review_params) flash[:success] = "レビューを作成しました。" else # レビュー済み flash[:success] = "この予約はレビュー済みです。" end else flash[:alert] = "予約がありません。" end redirect_back(fallback_location: request.referer) end def destroy @host_review = Review.find(params[:id]) @host_review.destroy redirect_back(fallback_location: request.referer, notice: "削除しました。") end private def host_review_params params.require(:host_review).permit(:comment, :star, :room_id, :reservation_id, :guest_id) end end
「app\controllers」フォルダに「guest_reviews_controller.rb」ファイルを新規作成して下さい。
app\controllers\guest_reviews_controller.rb(新規作成したファイル)
class GuestReviewsController < ApplicationController def create # ステップ 1: 予約が存在するかどうかを確認します(room_id、host_id、host_id) # ステップ 2: 現在のホストがこの予約のゲストをすでにレビューしているかどうかを確認します。 @reservation = Reservation.where( id: guest_review_params[:reservation_id], room_id: guest_review_params[:room_id] ).first if !@reservation.nil? && @reservation.room.user.id == guest_review_params[:host_id].to_i @has_reviewed = GuestReview.where( reservation_id: @reservation.id, host_id: guest_review_params[:host_id] ).first if @has_reviewed.nil? # レビューを許可 @guest_review = current_user.guest_reviews.create(guest_review_params) flash[:success] = "レビューを作成しました。" else # レビュー済み flash[:success] = "この予約はレビュー済みです。" end else flash[:alert] = "予約がありません。" end redirect_back(fallback_location: request.referer) end def destroy @guest_review = Review.find(params[:id]) @guest_review.destroy redirect_back(fallback_location: request.referer, notice: "削除しました。") end private def guest_review_params params.require(:guest_review).permit(:comment, :star, :room_id, :reservation_id, :host_id) end end
記述追加 app\controllers\rooms_controller.rb
「show()」メソッドに「@guest_reviews = @room.guest_reviews」の記述を追加してます。(27行目)
class RoomsController < ApplicationController before_action :set_room, except: [:index, :new, :create] before_action :authenticate_user!, except: [:show] before_action :is_authorised, only: [:listing, :pricing, :description, :photo_upload, :amenities, :location, :update] def index @rooms = current_user.rooms end def new @room = current_user.rooms.build end def create @room = current_user.rooms.build(room_params) if @room.save redirect_to listing_room_path(@room), notice: "保存しました。" else flash[:alert] = "問題が発生しました。" render :new end end def show @photos = @room.photos @guest_reviews = @room.guest_reviews end def listing end def pricing end def description end def photo_upload @photos = @room.photos end def amenities end def location end def update new_params = room_params new_params = room_params.merge(active: true) if is_ready_room if @room.update(new_params) flash[:notice] = "保存しました。" else flash[:alert] = "問題が発生しました。" end redirect_back(fallback_location: request.referer) end # 予約 開始日のAJAX def preload today = Date.today reservations = @room.reservations.where("start_date >= ? OR end_date >= ?", today, today) render json: reservations end # 予約 終了日のAJAX def preview start_date = Date.parse(params[:start_date]) end_date = Date.parse(params[:end_date]) output = { conflict: is_conflict(start_date, end_date, @room) } render json: output end private # 予約 プライベートメソッド def is_conflict(start_date, end_date, room) check = room.reservations.where("? < start_date AND end_date < ?", start_date, end_date) check.size > 0? true : false end def set_room @room = Room.find(params[:id]) end def is_authorised redirect_to root_path, alert: "権限がありません。" unless current_user.id == @room.user_id end def is_ready_room !@room.active && !@room.price.blank? && !@room.listing_name.blank? && !@room.photos.blank? && !@room.address.blank? end def room_params params.require(:room).permit(:home_type, :room_type, :accommodate, :bed_room, :bath_room, :listing_name, :summary, :address, :is_tv, :is_kitchen, :is_air, :is_heating, :is_internet, :price, :active) end end
「app\controllers\users_controller.rb」ファイルを編集します。
記述追加 app\controllers\users_controller.rb(6行目)
# ユーザーがホストの場合、ホストに対するすべてのゲストレビューを表示 @guest_reviews = Review.where(type: "GuestReview", host_id: @user.id) # ユーザーがゲストの場合、ユーザに対するすべてのホストレビューを表示 @host_reviews = Review.where(type: "HostReview", guest_id: @user.id)
記述追加 app\controllers\users_controller.rb
class UsersController < ApplicationController def show @user = User.find(params[:id]) @rooms = @user.rooms # ユーザーがホストの場合、ホストに対するすべてのゲストレビューを表示 @guest_reviews = Review.where(type: "GuestReview", host_id: @user.id) # ユーザーがゲストの場合、ユーザに対するすべてのホストレビューを表示 @host_reviews = Review.where(type: "HostReview", guest_id: @user.id) end end
ルートを設定します。
記述追加 config\routes.rb(27,28行目)
resources :guest_reviews, only: [:create, :destroy] resources :host_reviews, only: [:create, :destroy]
記述追加 config\routes.rb
「resources :guest_reviews, only: [:create, :destroy]」の記述追加(27行目)と「resources :host_reviews, only: [:create, :destroy]」の記述追加(28行目)します。
Rails.application.routes.draw do #ルートをpages#homeに設定 root 'pages#home' get 'pages/home' get '/your_trips' => 'reservations#your_trips' get '/your_reservations' => 'reservations#your_reservations' resources :users, only: [:show] resources :rooms, except: [:edit] do member do get 'listing' get 'pricing' get 'description' get 'photo_upload' get 'amenities' get 'location' get 'preload' get 'preview' end resources :photos, only: [:create, :destroy] resources :reservations, only: [:create] end resources :guest_reviews, only: [:create, :destroy] resources :host_reviews, only: [:create, :destroy] devise_for :users, path: '', path_names: {sign_in: 'login', sign_out: 'logout', edit: 'profile', sign_up: 'registration'}, controllers: {registrations: 'registrations'} # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end
レビュービューを作成します。
「app\views」フォルダに「reviews」フォルダを新規作成します。
その「reviews」フォルダに「_guest_form.html.erb」ファイルを新規作成してください。
app\views\reviews\_guest_form.html.erb(新規作成したファイル)
<!-- buttonで Modalをトリガーします。 --> <button type="button" class="btn btn-normal" data-toggle="modal" data-target="#myModal_<%= reservation.id %>"> レビュー </button> <!-- Modal --> <div id="myModal_<%= reservation.id %>" class="modal fade" role="dialog"> <div class="modal-dialog"> <!-- Modal の内容--> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal">×</button> <h4 class="modal-title text-left">あなたのレビュー</h4> </div> <div class="modal-body"> <%= form_for current_user.guest_reviews.new do |f| %> <div class="form-group text-center"> <div id="stars"></div> </div> <div class="form-group"> <%= f.text_area :comment, rows: 3, class: "form-control" %> </div> <%= f.hidden_field :room_id, value: reservation.room.id %> <%= f.hidden_field :reservation_id, value: reservation.id %> <%= f.hidden_field :host_id, value: reservation.room.user.id %> <div class="text-center"> <%= f.submit "レビューを投稿", class: "btn btn-normal" %> </div> <% end %> </div> </div> </div> </div>
「app\views\reviews」フォルダに「_host_form.html.erb」ファイルを新規作成してください。
app\views\reviews\_host_form.html.erb(新規作成したファイル)
<!-- buttonで Modalをトリガーします。 --> <button type="button" class="btn btn-normal" data-toggle="modal" data-target="#myModal_<%= reservation.id %>"> レビュー </button> <!-- Modal --> <div id="myModal_<%= reservation.id %>" class="modal fade" role="dialog"> <div class="modal-dialog"> <!-- Modal の内容--> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal">×</button> <h4 class="modal-title text-left">あなたのレビュー</h4> </div> <div class="modal-body"> <%= form_for current_user.host_reviews.new do |f| %> <div class="form-group text-center"> <div id="stars"></div> </div> <div class="form-group"> <%= f.text_area :comment, rows: 3, class: "form-control" %> </div> <%= f.hidden_field :room_id, value: reservation.room.id %> <%= f.hidden_field :reservation_id, value: reservation.id %> <%= f.hidden_field :guest_id, value: reservation.user.id %> <div class="text-center"> <%= f.submit "レビューを投稿", class: "btn btn-normal" %> </div> <% end %> </div> </div> </div> </div>
記述追加 app\views\reservations\your_trips.html.erb
「<%= render partial: "reviews/guest_form", locals: {reservation: trip} %>」の記述追加(33行目)
<div class="row"> <div class="col-md-3"> <ul class="sidebar-list"> <li class="sidebar-item"><%= link_to "ご予約の内容", your_trips_path, class: "sidebar-link active" %></li> </ul> </div> <div class="col-md-9"> <div class="panel panel-default"> <div class="panel-heading"> ご予約内容 </div> <div class="panel-body"> <% @trips.each do |trip| %> <div class="row"> <div class="col-md-3", style="white-space: nowrap"> <%= I18n.l(trip.start_date, format: :full_date) %> </div> <div class="col-md-2"> <%= link_to room_path(trip.room) do %> <%= image_tag trip.room.cover_photo(:thumb) %> <% end %> </div> <div class="col-md-5"> <%= link_to trip.room.listing_name, room_path(trip.room) %><br/><br/> <span> <%= image_tag trip.room.user.gravatar_url, class: "img-circle avatar-small" %> <%= link_to user_path(trip.room.user) do %> <%= trip.room.user.fullname %> <% end %> </span> </div> <div class="col-md-3 text-right"> <%= render partial: "reviews/guest_form", locals: {reservation: trip} %> </div> </div> <hr/> <% end %> </div> </div> </div> </div>
記述追加 app\views\reservations\your_reservations.html.erb
「<%= render partial: "reviews/host_form", locals: {reservation: reservation} %>」の記述追加(37行目)
<div class="row"> <div class="col-md-3"> <ul class="sidebar-list"> <li class="sidebar-item"><%= link_to "受注予約の一覧", your_reservations_path, class: "sidebar-link active" %></li> <li class="sidebar-item"><%= link_to "登録したお部屋の管理", rooms_path, class: "sidebar-link" %></li> <li class="sidebar-item"><%= link_to "お部屋の新規登録", new_room_path, class: "sidebar-link" %></li> </ul> </div> <div class="col-md-9"> <div class="panel panel-default"> <div class="panel-heading"> 受注予約の一覧 </div> <div class="panel-body"> <% @rooms.each do |room| %> <% room.reservations.each do |reservation| %> <div class="row"> <div class="col-md-3", style="white-space: nowrap"> <%= I18n.l(reservation.start_date, format: :full_date) %> </div> <div class="col-md-2"> <%= link_to room_path(reservation.room) do %> <%= image_tag reservation.room.cover_photo(:thumb) %> <% end %> </div> <div class="col-md-5"> <%= link_to reservation.room.listing_name, room_path(reservation.room) %><br/><br/> <span> <%= link_to user_path(reservation.user) do %> <%= image_tag reservation.user.gravatar_url, class: "img-circle avatar-small" %> <%= reservation.user.fullname %> <% end %> </span> </div> <div class="col-md-3 text-right"> <%= render partial: "reviews/host_form", locals: {reservation: reservation} %> </div> </div> <hr/> <% end %> <% end %> </div> </div> </div> </div>
「app\views\reviews」フォルダに「_guest_list.html.erb」ファイルを新規作成します。
app\views\reviews\_guest_list.html.erb(新規作成したファイル)
<% if @guest_reviews.count == 0 %> <div class="text-center"> <h4>レビューはありません。</h4> </div> <% else %> <% @guest_reviews.order(id: :desc).each do |r| %> <hr/> <div class="row"> <div class="col-md-3 text-center"> <%= image_tag r.guest.gravatar_url, class: "img-circle avatar-medium" %><br/><br/> <strong><%= r.guest.fullname %></strong> </div> <div class="col-md-9"> <div <%= r.star %>"></div> <% if current_user && current_user == r.guest %> <span class="pull-right"> <%= link_to r, method: :delete, data: {confirm: "本当にいいですか?"} do %> <i class="fa fa-trash-o fa-lg"></i> <% end %> </span> <% end %> </div> <div><%= r.created_at.strftime('%Y年%-m月%-d日 %-H時%-M分') %></div> <div><%= r.comment %></div> </div> <% end %> <% end %>
「app\views\reviews」フォルダに「_host_list.html.erb」ファイルを新規作成します。
app\views\reviews\_host_list.html.erb(新規作成したファイル)
<% if @host_reviews.count == 0 %> <div class="text-center"> <h4>レビューはありません。</h4> </div> <% else %> <% @host_reviews.order(id: :desc).each do |r| %> <hr/> <div class="row"> <div class="col-md-3 text-center"> <%= image_tag r.host.gravatar_url, class: "img-circle avatar-medium" %><br/><br/> <strong><%= r.host.fullname %></strong> </div> <div class="col-md-9"> <div <%= r.star %>"></div> <% if current_user && current_user == r.host %> <span class="pull-right"> <%= link_to r, method: :delete, data: {confirm: "本当にいいですか?"} do %> <i class="fa fa-trash-o fa-lg"></i> <% end %> </span> <% end %> </div> <div><%= r.created_at.strftime('%Y年%-m月%-d日 %-H時%-M分') %></div> <div><%= r.comment %></div> </div> <% end %> <% end %>
記述追加 app\views\rooms\show.html.erb(111行目)
<!-- レビュー --> <div class="row"> <div class="col-md-12"> <h3> <%= pluralize(@guest_reviews.count, "Review") %> <span id="average_rating"></span> </h3> <div class="container"> <div><%= render "reviews/guest_list" %></div> </div> </div> </div> <br/> <hr/>
app\views\rooms\show.html.erb
<!-- 写真 --> <div class="row"> <div class="col-md-12"> <%= image_tag @room.cover_photo(nil), width: "100%" %> </div> </div> <br/> <div class="row"> <!-- 左パネル --> <div class="col-md-8"> <!-- お部屋の名前 --> <div class="row"> <div class="col-md-8"> <h1><%= @room.listing_name %></h1> <%= @room.address %> </div> <div class="col-md-4 text-center"> <%= image_tag @room.user.gravatar_url, class: "img-circle avatar-large" %><br/><br/> <%= @room.user.fullname %> </div> </div> <hr/> <!-- 部屋のインフォメーション --> <div class="row text-babu"> <div class="row text-center row-space-1"> <div class="col-md-3"> <i class="fa fa-home fa-2x"></i> </div> <div class="col-md-3"> <i class="fa fa-user-circle-o fa-2x"></i> </div> <div class="col-md-3"> <i class="fa fa-bed fa-2x"></i> </div> <div class="col-md-3"> <i class="fa fa-bath fa-2x"></i> </div> </div> <div class="row text-center"> <div class="col-md-3"><%= @room.home_type %></div> <div class="col-md-3"><%= pluralize(@room.accommodate, "人宿泊可能") %></div> <div class="col-md-3"><%= pluralize(@room.bed_room, "台") %></div> <div class="col-md-3"><%= pluralize(@room.bath_room, "部屋") %></div> </div> </div> <hr/> <!-- 詳細 --> <div class="row"> <div class="col-md-12"> <h3>お部屋の詳細</h3> <p><%= @room.summary %></p> </div> </div> <hr/> <!-- アメニティ --> <div class="row"> <div class="col-md-3"> <h4>アメニティ</h4> </div> <div class="col-md-9"> <div class="row"> <div class="col-md-6"> <ul class="amenities"> <li class="<%= 'text-line-through' if !@room.is_tv %>">テレビ</li> <li class="<%= 'text-line-through' if !@room.is_kitchen %>">キッチン</li> <li class="<%= 'text-line-through' if !@room.is_internet %>">インターネット</li> </ul> </div> <div class="col-md-6"> <ul class="amenities"> <li class="<%= 'text-line-through' if !@room.is_heating %>">暖房</li> <li class="<%= 'text-line-through' if !@room.is_air %>">エアコン</li> </ul> </div> </div> </div> </div> <hr/> <!-- カルーセル表示 --> <div class="row"> <% if @photos.length > 0 %> <div id="myCarousel" class="carousel slide" data-ride="carousel"> <!-- 表示 --> <ol class="carousel-indicators"> <% @photos.each do |photo| %> <li data-target="#myCarousel" data-slide-to="<%= photo.id %>"></li> <% end %> </ol> <!-- スライド --> <div class="carousel-inner"> <% @photos.each do |photo| %> <div class="item <%= 'active' if photo.id == @photos[0].id %>"> <%= image_tag photo.image.url() %> </div> <% end %> </div> <!-- 左右移動 --> <a class="left carousel-control" href="#myCarousel" data-slide="prev"> <span class="glyphicon glyphicon-chevron-left"></span> <span class="sr-only">戻る</span> </a> <a class="right carousel-control" href="#myCarousel" data-slide="next"> <span class="glyphicon glyphicon-chevron-right"></span> <span class="sr-only">次へ</span> </a> </div> <% end %> </div> <hr/> <!-- レビュー --> <div class="row"> <div class="col-md-12"> <h3> <%= pluralize(@guest_reviews.count, "Review") %> <span id="average_rating"></span> </h3> <div class="container"> <div><%= render "reviews/guest_list" %></div> </div> </div> </div> <br/> <hr/> <!-- GOOGLE マップ --> <div class="row"> <div id="map" style="width: 100%; height: 400px"></div> <script src="https://maps.googleapis.com/maps/api/js"></script> <script> function initialize() { var location = {lat: <%= @room.latitude %>, lng: <%= @room.longitude %>}; var map = new google.maps.Map(document.getElementById('map'), { center: location, zoom: 14 }); var marker = new google.maps.Marker({ position: location, map: map }); var infoWindow = new google.maps.InfoWindow({ content: '<div id="content"><%= image_tag @room.cover_photo(:medium) %></div>' }); infoWindow.open(map, marker); } google.maps.event.addDomListener(window, 'load', initialize); </script> <script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBojDcZmScBkIOISjoYREjgid99iZUL2Tk&callback=initMap" type="text/javascript"></script> </div> <hr/> <!-- 近くのお部屋を検索 --> <div class="row"> <h3>お近くのお部屋</h3> <% for room in @room.nearbys(10) %> <div class="col-md-4"> <div class="panel panel-default"> <div class="panel-heading preview"> <%= image_tag room.cover_photo(:medium) %> </div> <div class="panel-body"> <%= link_to room.listing_name, room %><br/> (距離:<%= room.distance.round(2) %> Km) </div> </div> </div> <% end %> </div> </div> <!-- 右パネル --> <div class="col-md-4"> <!-- 予約フォーム --> <%= render 'reservations/form' %> </div> </div>
記述追加 app\views\users\show.html.erb(45行目)
<br/> <h4>ゲストからのレビュー (<%= @guest_reviews.count %>)</h4> <div class="row"> <%= render "reviews/guest_list" %> </div> <br/> <h4>ホストからのレビュー (<%= @host_reviews.count %>)</h4> <div class="row"> <%= render "reviews/host_list" %> </div>
app\views\users\show.html.erb
<div class="row"> <div class="col-md-3"> <div class="center"> <%= image_tag @user.gravatar_url, class: "avatar-full" %> </div> <div class="panel panel-default"> <div class="panel-heading" style="text-align: center;"><%= @user.fullname %></div> <br/> <div class="center"> <%= @user.description %> </div> <hr/> <div class="panel-body"> <ul class="sidebar-list"> <% if @user.confirmation_token %> <li>Emailアドレス<span class="pull-right icon-babu"><i class="fa fa-check-circle-o"></i></span></li> <% end %> <li>電話番号<span class="pull-right icon-babu"><i class="fa fa-check-circle-o"></i></span></li> </ul> </div> </div> </div> <div class="col-md-9"> <br/> <h4><%= @user.fullname %>さんが登録しているお部屋(<%= @rooms.length %>)</h4><br/> <div class="row"> <% @rooms.each do |room| %> <div class="col-md-4"> <div class="panel panel-default"> <div class="panel-heading preview"> <%= image_tag room.cover_photo(:medium) %> </div> <div class="panel-body"> <%= link_to room.listing_name, room %> </div> </div> </div> <% end %> </div> <br/> <h4>ゲストからのレビュー (<%= @guest_reviews.count %>)</h4> <div class="row"> <%= render "reviews/guest_list" %> </div> <br/> <h4>ホストからのレビュー (<%= @host_reviews.count %>)</h4> <div class="row"> <%= render "reviews/host_list" %> </div> <br/> </div> </div>
ブラウザ確認
ホストからゲストにレビューを書いてみましょう。
http://localhost:3000/your_reservations
ゲストからホストにレビューを書いてみましょう。
http://localhost:3000/your_trips