学生向けプログラミング入門 | 無料

学生向けにプログラミングを無料で解説。Java、C++、Ruby、PHP、データベース、Ruby on Rails, Python, Django

Rails7.1 | 民泊予約アプリ作成 | 32 | raty-js

↓↓クリックして頂けると励みになります。



31 | 予約確認】 << 【ホーム】 >> 【33 | Ransack



raty-jsは、スター評価を作成するためのJavaScriptプラグインです。
Rails6.1でraty-jsを利用するには、以下のステップに従って設定を行う必要があります。


「star-on.png」「star-off.png」「star-half.png」の3ファイルを「app/assets/images」フォルダにコピーしておいてください。


画像は下記のリンクにあります。
github.com


「raty-js」をインストールします。

Rails6.1ではyarnを利用していましたが、Rails7.1ではimportmapを利用しまし。
しかしながら、使用するJQueryがimportmapでうまく動作しないため、raty-jsも「app/views/layouts/application.html.erb」ファイルのheadタグ内で直接CDNのスクリプトを読み込ませます。


記述追加 「app/views/layouts/application.html.erb」43行目

   <!-- ratyjs 3.1.1 -->
   <script src="https://cdnjs.cloudflare.com/ajax/libs/raty/3.1.1/jquery.raty.min.js" integrity="sha512-Isj3SyFm+B8u/cErwzYj2iEgBorGyWqdFVb934Y+jajNg9kiYQQc9pbmiIgq/bDcar9ijmw4W+bd72UK/tzcsA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  



記述追加 【app/views/layouts/application.html.erb】43行目

<!DOCTYPE html>
<html>
  <head>
    <title>VacationRental7</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>

    <!-- noty3.1.4 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/noty/3.1.4/noty.min.js" integrity="sha512-lOrm9FgT1LKOJRUXF3tp6QaMorJftUjowOWiDcG5GFZ/q7ukof19V0HKx/GWzXCdt9zYju3/KhBNdCLzK8b90Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/noty/3.1.4/noty.min.css" integrity="sha512-0p3K0H3S6Q4bEWZ/WmC94Tgit2ular2/n0ESdfEX8l172YyQj8re1Wu9s/HT9T/T2osUw5Gx/6pAZNk3UKbESw==" crossorigin="anonymous" referrerpolicy="no-referrer" />

    <!-- Google Fonts -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Kaisei+Opti&family=Kosugi+Maru&family=Rampart+One&display=swap" rel="stylesheet">

    <!-- Dropzone5.5.1 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.1/min/dropzone.min.js" integrity="sha512-jytq61HY3/eCNwWirBhRofDxujTCMFEiQeTe+kHR4eYLNTXrUq7kY2qQDKOUnsVAKN5XGBJjQ3TvNkIkW/itGw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.1/min/dropzone.min.css" integrity="sha512-zoIoZAaHj0iHEOwZZeQnGqpU8Ph4ki9ptyHZFPe+BmILwqAksvwm27hR9dYH4WXjYY/4/mz8YDBCgVqzc2+BJA==" crossorigin="anonymous" referrerpolicy="no-referrer" />

    <!-- JQuery 3.7.1 -->
    <script
    src="https://code.jquery.com/jquery-3.7.1.min.js"
    integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
    crossorigin="anonymous"></script>

    <!-- JQuery-UI 1.13.2 -->
    <script
    src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"
    integrity="sha256-lSjKY0/srUM9BE3dPm+c4fBo1dky2v27Gdjm2uoZaL0="
    crossorigin="anonymous"></script>

    <!-- 日付ピッカー デザインsunny-->
    <link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/sunny/jquery-ui.css">
    
    <!-- 日付ピッカーの日本語化-->
    <script src="https://rawgit.com/jquery/jquery-ui/master/ui/i18n/datepicker-ja.js"></script>

    <!-- ratyjs 3.1.1 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/raty/3.1.1/jquery.raty.min.js" integrity="sha512-Isj3SyFm+B8u/cErwzYj2iEgBorGyWqdFVb934Y+jajNg9kiYQQc9pbmiIgq/bDcar9ijmw4W+bd72UK/tzcsA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  
    </head>

  <body>

    <!-- ナビゲーションバー -->
    <%= render  "shared/navbar" %>

    <!-- noty -->
    <%= render 'shared/notification' %>

    <%= yield %>
  </body>
</html>


レビューモデルを作成します。



まず、ゲスト用レビューモデルを作成します。


コマンド
rails g model GuestReview --parent=Review --migration=false


次に、ホスト用レビューモデルを作成します。


コマンド
rails g model HostReview --parent=Review --migration=false


レビューモデルを作成します。


コマンド
一文です。
rails g model Review review:text stars:bigint room:references reservation:references guest:references host:references type


記述更新 db\migrate\20200727021130_create_reviews.rb
「,default: 1」の記述追加(5行目)をしています。
また、8,9行目に「, foreign_key: { to_table: :users }」の記述を追加しています。

class CreateReviews < ActiveRecord::Migration[7.1]
  def change
    create_table :reviews do |t|
      t.text :review
      t.bigint :stars,default: 1
      t.references :room, null: false, foreign_key: true
      t.references :reservation, null: false, foreign_key: true
      t.references :guest, null: false, foreign_key: true, foreign_key: { to_table: :users }
      t.references :host, null: false, foreign_key: true, 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」ファイルを以下のように編集します。


記述追加 app\models\review.rb
4行目と5行目に「, class_name: "User"」の記述を追加しています。

class Review < ApplicationRecord
  belongs_to :room
  belongs_to :reservation
  belongs_to :guest, class_name: "User"
  belongs_to :host, class_name: "User"
end



「app\models\room.rb」ファイルを編集していきます。


1.6行目に「has_many :guest_reviews」の記述追加


2.7行目に「has_many :reviews」の記述追加


3.21行目に「average_rating()」メソッドの追加

 def average_rating
    guest_reviews.count == 0 ? 0 : guest_reviews.average(:stars).round(2).to_i
  end  



記述追加 app\models\room.rb

class Room < ApplicationRecord

  belongs_to :user
  has_many :reservations

  has_many :guest_reviews
  has_many :reviews

  has_many_attached :photos
  has_rich_text :description

  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 average_rating
    guest_reviews.count == 0 ? 0 : guest_reviews.average(:stars).round(2).to_i
  end  
  
end



「app\models\reservation.rb」ファイルを編集します。


記述追加 app\models\reservation.rb
5行目に「has_many :reviews」の記述追加

class Reservation < ApplicationRecord
  belongs_to :user
  belongs_to :room

  has_many :reviews
end



「app\models\user.rb」ファイルを編集していきます。


5, 6行目に以下の記述を追加します。

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

  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"

  has_one_attached :avatar

  validates :full_name, presence: true, length: {maximum: 50}
  
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :confirmable, :omniauthable

  def self.from_omniauth(auth)
    user = User.where(email: auth.info.email).first

    if user
      return user
    else
      where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
        user.email = auth.info.email
        user.password = Devise.friendly_token[0, 20]
        user.full_name = auth.info.name   # ユーザーモデルに名前があると仮定
        user.image = auth.info.image # ユーザーモデルに画像があると仮定

        user.uid = auth.uid
        user.provider = auth.provider

      end
    end
  end

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[:alert] = "この予約はレビュー済みです。"
        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(:review, :stars, :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[:alert] = "この予約はレビュー済みです。"
        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(:review, :stars, :room_id, :reservation_id, :host_id)
    end
end



記述追加 app\controllers\rooms_controller.rb
「show()」メソッドに「@guest_reviews = @room.guest_reviews」の記述を追加してます。(30行目)

class RoomsController < ApplicationController

  protect_from_forgery except: [:upload_photo]

  before_action :set_room, except: [:index, :new, :create]
  before_action :authenticate_user!, except: [:show]
  before_action :is_authorised, only: [:listing, :pricing, :photo_upload, :delete_photo, :amenities, :location, :update]

  def index
     @rooms = Room.all
  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
    @i = 0
    @guest_reviews = @room.guest_reviews

  end

  def listing
  end

  def pricing
  end

  def description
  end

  def photo_upload
  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

  def upload_photo
    @room.photos.attach(params[:file])
    render json: { success: true }
  end

  def delete_photo
    @image = ActiveStorage::Attachment.find(params[:photo_id])
    @image.purge
    redirect_to photo_upload_room_path(@room)
  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 set_room
    @room = Room.find(params[:id])
  end

  def room_params
    params.require(:room).permit(:home_type, :room_type, :accommodate, :bed_room, :bath_room, :listing_name, :summary, :address, :latitude, :longitude, :is_tv, :is_kitchen, :is_air, :is_heating, :is_internet, :price, :active, :description)
  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 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

end



「app\controllers\users_controller.rb」ファイルを編集します。


show()メソッドの11行目に以下の記述を追加します。

# ユーザーがホストの場合、ホストに対するすべてのゲストレビューを表示
@guest_reviews = Review.where(type: "GuestReview", host_id: @user.id)
# ユーザーがゲストの場合、ユーザに対するすべてのホストレビューを表示
@host_reviews = Review.where(type: "HostReview", guest_id: @user.id)



dashboad()メソッドの6行目にも以下の記述を追加しています。

    # ユーザーがホストの場合、ホストに対するすべてのゲストレビューを表示
    @guest_reviews = Review.where(type: "GuestReview", host_id: current_user.id)
    # ユーザーがゲストの場合、ユーザに対するすべてのホストレビューを表示
    @host_reviews = Review.where(type: "HostReview", guest_id: current_user.id)



app\controllers\users_controller.rb

class UsersController < ApplicationController

  before_action :authenticate_user!

  def dashboard
    # ユーザーがホストの場合、ホストに対するすべてのゲストレビューを表示
    @guest_reviews = Review.where(type: "GuestReview", host_id: current_user.id)
    # ユーザーがゲストの場合、ユーザに対するすべてのホストレビューを表示
    @host_reviews = Review.where(type: "HostReview", guest_id: current_user.id)
  end

  def show
    @user = User.find(params[:id])

    # ユーザーがホストの場合、ホストに対するすべてのゲストレビューを表示
    @guest_reviews = Review.where(type: "GuestReview", host_id: @user.id)
    # ユーザーがゲストの場合、ユーザに対するすべてのホストレビューを表示
    @host_reviews = Review.where(type: "HostReview", guest_id: @user.id)
  end  

  def update
    @user = current_user
    if @user.update(current_user_params)
      flash[:notice] = "保存しました"
    else
      flash[:alert] = "更新できません"
    end
    redirect_to dashboard_path
  end

  private
  def current_user_params
    params.require(:user).permit(:about, :status, :avatar)
  end

end


ルートを設定します。



32,33行目に以下の記述を追加します。

  resources :guest_reviews, only: [:create, :destroy]
  resources :host_reviews, only: [:create, :destroy]



記述追加 config\routes.rb

Rails.application.routes.draw do

  # ルートを app\views\pages\home.html.erb に設定
  root 'pages#home'

  # get
  get '/dashboard', to: 'users#dashboard'
  get 'pages/home'
  get '/users/:id', to: 'users#show', as: 'user'
  get '/your_trips' => 'reservations#your_trips'
  get '/your_reservations' => 'reservations#your_reservations'

  # post
  post '/users/edit', to: 'users#update'

  resources :rooms, except: [:edit] do
    member do
      get 'listing'
      get 'pricing'
      get 'description'
      get 'photo_upload'
      get 'amenities'
      get 'location'
      get 'preload'
      get 'preview'
      delete :delete_photo
      post :upload_photo
    end
    resources :reservations, only: [:create]
  end

  resources :guest_reviews, only: [:create, :destroy]
  resources :host_reviews, only: [:create, :destroy]

  # device
  devise_for :users, 
              path: '', 
              path_names: {sign_up: 'register', sign_in: 'login', edit: 'profile', sign_out: 'logout'},
              controllers: {omniauth_callbacks: 'omniauth_callbacks', registrations: 'registrations'}
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end


ビューを作成します。



レビューフォームのレンダーファイルを作成します。
「app\views」フォルダに「reviews」フォルダを新規作成します。
作成した「reviews」フォルダに「_guest_form.html.erb」ファイルを新規作成してください。



app\views\reviews\_guest_form.html.erb(新規作成したファイル)

<!-- Button trigger modal -->
<button type="button" class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#exampleModal<%= reservation.id %>">
<font style="font-size: 0.9rem;">レビュー</font>
</button>

<!-- Modal -->
<div class="modal fade" id="exampleModal<%= reservation.id %>" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
    <%= form_for GuestReview.new do |f| %>
      <div class="modal-header">
        <h1 class="modal-title fs-5" id="exampleModalLabel"><div id="stars<%= reservation.id %>"></div></h1>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">

        <%= f.text_area :review, class: "textarea", required: true, style: "width: 20rem; height: 10rem; margin-top: -1rem;" %>
        <%= f.hidden_field :reservation_id, value: reservation.id %>
        <%= 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>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">閉じる</button>
        <%= f.submit "レビューを投稿する", class: "btn btn-warning" %>
      </div>
    <% end %>
    </div>
  </div>
</div>

<script>
  $('#stars<%= reservation.id %>').raty({
    path: '/assets',
    scoreName: 'guest_review[stars]',
    score: 1
  });
</script>



「app\views\reviews」フォルダに「_host_form.html.erb」ファイルを新規作成してください。


app\views\reviews\_host_form.html.erb(新規作成したファイル)

<!-- Button trigger modal -->
<button type="button" class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#exampleModal<%= reservation.id %>">
<font style="font-size: 0.9rem;">レビュー</font>
</button>

<!-- Modal -->
<div class="modal fade" id="exampleModal<%= reservation.id %>" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
    <%= form_for HostReview.new do |f| %>
      <div class="modal-header">
        <h1 class="modal-title fs-5" id="exampleModalLabel"><div id="stars<%= reservation.id %>"></div></h1>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">

        <%= f.text_area :review, class: "textarea", required: true, style: "width: 20rem; height: 10rem; margin-top: -1rem;" %>
        <%= f.hidden_field :reservation_id, value: reservation.id %>
        <%= 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>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">閉じる</button>
        <%= f.submit "レビューを投稿する", class: "btn btn-warning" %>
      </div>
    <% end %>
    </div>
  </div>
</div>

<script>
  $('#stars<%= reservation.id %>').raty({
    path: '/assets',
    scoreName: 'host_review[stars]',
    score: 1
  });
</script>



「app\views\reservations\your_trips.html.erb」ファイルを編集します。


記述追加 app\views\reservations\your_trips.html.erb(41行目)

      <li class="list-group-item mt-4" style="border: none;">
               <% if trip.end_date < Date.today %>
                   <%= render partial: "reviews/guest_form", locals: {reservation: trip} %> 
               <% else %>
                    <span class="alert alert-danger" style="font-size: 0.7rem;">チェックアウト後にレビューできます</span>
               <% end %>
       </li>



記述追加 app\views\reservations\your_trips.html.erb(41行目)

<div class="container mt-4">
    <div class="card">
        <div class="card-body">

            <h5 class="card-title text-danger h3 font1">予約内容(ゲスト)</h5>
            <% if @trips.blank? %>
                <h5 class="font1">表示できる予約はありません。</h5>
            <% end %>
            <% @trips.each do |trip| %>
                <div class="card mt-4">
                    <div class="card-body">

                        <ul class="list-group">
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">申込日:</span><%= I18n.l(trip.created_at, format: :full_date) %>
                            </li>
                            <li class="list-group-item" style="border: none;">
                            <span class="font1">部屋名:</span>
                                <%= link_to room_path(trip.room), style: "text-decoration: none;", data: { turbolinks: false} do %>                                
                                    <span class="btn btn-light"><%= trip.room.listing_name %></span>
                                <% end %>                            
                            </li>
                            <li class="list-group-item" style="border: none;">
                            <span class="font1">ホスト:</span>
                                <%= link_to user_path(trip.room.user), class: "tootip", style: "text-decoration: none;" do %>
                                        <span class="btn btn-light"><%= trip.room.user.full_name %></span>
                                <% end %>
                            </li>
                            <li class="list-group-item" style="border: none;">
                            <span class="font1">宿泊日:</span>   <%= I18n.l(trip.start_date, format: :full_date) %>                     
                            </li>
                            <li class="list-group-item" style="border: none;">
                            <span class="font1">ご出発:</span>   <%= I18n.l(trip.end_date, format: :full_date) %>                       
                            </li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">泊数:</span><%=trip.total/trip.price %></li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">料金:</span><%= number_to_currency(trip.total) %>
                            </li>
                            <li class="list-group-item mt-4" style="border: none;">
                                <% if trip.end_date < Date.today %>
                                    <%= render partial: "reviews/guest_form", locals: {reservation: trip} %> 
                                <% else %>
                                    <span class="alert alert-danger" style="font-size: 0.7rem;">チェックアウト後にレビューできます</span>
                                <% end %>
                            </li>
                        </ul>

                    </div>
                </div>
            <% end %>

        </div>
    </div>
</div>



レビューができるか確認します。
現在の日付よりチェックアウト日が過ぎていないとレビューボタンが出ないようになっています。
確認するには、Posticoでチェックアウト日を編集してください。
ブラウザ確認
http://localhost:3000/your_trips

PCレイアウト
PCレイアウト


モバイルレイアウト
モバイルレイアウト



「app\views\reservations\your_reservations.html.erb」ファイルを編集します。


記述追加(42行目)

<li class="list-group-item mt-4" style="border: none;">
      <% if reservation.end_date < Date.today %>
               <%= render partial: "reviews/host_form", locals: {reservation: reservation} %> 
      <% else %>
               <span class="alert alert-danger" style="font-size: 0.7rem;">チェックアウト後にレビューできます</span>
      <% end %>
</li>    



記述追加 【app\views\reservations\your_reservations.html.erb】42行目

<div class="container mt-4">
    <div class="card">
        <div class="card-body">

            <h5 class="card-title text-danger h3 font1">予約内容(ホスト)</h5>
            <% if @rooms.blank? %>
                <h5 class="font1">表示できる予約はありません。</h5>
            <% end %>
            <% @rooms.each do |room| %>
                <% room.reservations.each do |reservation| %>
                <div class="card mt-4">
                    <div class="card-body">

                        <ul class="list-group">
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">申込日:</span><%= I18n.l(reservation.created_at, format: :full_date) %>
                            </li>
                            <li class="list-group-item" style="border: none;">
                            <span class="font1">部屋名:</span>
                                <%= link_to room_path(reservation.room), style: "text-decoration: none;", data: { turbolinks: false} do %>                                
                                    <span class="btn btn-light"><%= reservation.room.listing_name %></span>
                                <% end %>                            
                            </li>
                            <li class="list-group-item" style="border: none;">
                            <span class="font1">ゲスト:</span>
                                <%= link_to user_path(reservation.room.user), class: "tootip", style: "text-decoration: none;" do %>
                                        <span class="btn btn-light"><%= reservation.user.full_name %></span>
                                <% end %>
                            </li>
                            <li class="list-group-item" style="border: none;">
                            <span class="font1">宿泊日:</span>   <%= I18n.l(reservation.start_date, format: :full_date) %>                     
                            </li>
                            <li class="list-group-item" style="border: none;">
                            <span class="font1">ご出発:</span>   <%= I18n.l(reservation.end_date, format: :full_date) %>                       
                            </li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">泊数:</span><%=reservation.total/reservation.price %></li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">料金:</span><%= number_to_currency(reservation.total) %>
                            </li>
                            <li class="list-group-item mt-4" style="border: none;">
                                <% if reservation.end_date < Date.today %>
                                    <%= render partial: "reviews/host_form", locals: {reservation: reservation} %> 
                                <% else %>
                                    <span class="alert alert-danger" style="font-size: 0.7rem;">チェックアウト後にレビューできます</span>
                                <% end %>
                            </li>                            
                        </ul>

                    </div>
                </div>
            <% end %>
            <% end %>

        </div>
    </div>
</div>



ホストでログインし、動作を確認してください。
ブラウザ確認
http://localhost:3000/your_reservations

PCレイアウト
PCレイアウト


モバイルレイアウト
モバイルレイアウト



レビューの内容を表示するレンダーファイルを作成します。
ゲストからのレビューとホストからのレビューを分けて作成します。


「app\views\reviews」フォルダに「_guest_list.html.erb」ファイルを新規作成します。


app\views\reviews\_guest_list.html.erb(新規作成したファイル)

<% @guest_reviews.each do |r| %>
    <div style="border: solid 1px #c0c0c0; margin-bottom: 0.3rem; border-radius: 5px;">
        <ul class="list-group list-group-horizontal">
            <li class="list-group-item" style="border: none;">
                <div class="font1"><%= r.guest.full_name %></div>
                <figure class="figure">
                <%= link_to user_path(r.guest), style: "text-decoration:none;" do %>
                    <%= image_tag avatar_url(r.guest), style: "width: 40px;", class: "figure-img img-fluid rounded-pill" %>
                <% end %>
                    <div><i class="fa fa-star" style="color: gold;"></i></span><span><%= r.stars %></div>                
                </figure>
            </li>
            <li class="list-group-item bn" style="width: 100%; border: none;">
                <span><%= r.review %></span>
                <span class="text-secondary" style="font-size: 0.7rem;"><%= time_ago_in_words(r.created_at) %></span>
            </li>
            <% if (user_signed_in?) %>
                <% if current_user && current_user == r.guest %>
                <li class="list-group-item bn">
                    <div>
                        <%= button_to r, method: :delete, form: { onSubmit: "return check()" }, class: "btn btn-light" do %>
                            <i class="fas fa-trash-alt" style="color: red;"></i>
                        <% end %>
                    </div>
                </li>
                <% end %>
            <% end %>
                    
        </ul>
    </div>

<% end %>

<script>
    function check(){
        if(window.confirm('本当に削除してよろしいですか?')){ 
        return true;
        }
        else{
        window.alert('キャンセルされました'); 
        return false; 
        }
    }    
</script>



「app\views\reviews」フォルダに「_host_list.html.erb」ファイルを新規作成します。


app\views\reviews\_host_list.html.erb(新規作成したファイル)

<% @host_reviews.each do |r| %>
    <div style="border: solid 1px #c0c0c0; margin-bottom: 0.3rem; border-radius: 5px;">
        <ul class="list-group list-group-horizontal">
            <li class="list-group-item" style="border: none;">
                <div class="font1"><%= r.host.full_name %></div>
                <figure class="figure">
                <%= link_to user_path(r.host), style: "text-decoration:none;" do %>
                    <%= image_tag avatar_url(r.host), style: "width: 40px;", class: "figure-img img-fluid rounded-pill" %>
                <% end %>
                    <div><i class="fa fa-star" style="color: gold;"></i></span><span><%= r.stars %></div>
                </figure>
            </li>
            <li class="list-group-item bn" style="width: 100%; border: none;">
                <span><%= r.review %></span>
                <span class="text-secondary" style="font-size: 0.7rem;"><%= time_ago_in_words(r.created_at) %></span>
            </li>
            <% if (user_signed_in?) %>
                <% if current_user && current_user == r.host %>
                <li class="list-group-item bn">
                    <div>
                        <%= button_to r, method: :delete, form: { onSubmit: "return check()" }, class: "btn btn-light" do %>
                            <i class="fas fa-trash-alt" style="color: red;"></i>
                        <% end %>
                    </div>
                </li>
                <% end %>
            <% end %>
                    
        </ul>
    </div>

<% end %>

<script>
    function check(){
        if(window.confirm('本当に削除してよろしいですか?')){ 
        return true;
        }
        else{
        window.alert('キャンセルされました'); 
        return false; 
        }
    }    
</script>



作成したレンダーファイルを実際に表示させていきます。
45行目にスターの平均値を表示する記述も追加しています。


記述追加 app\views\rooms\show.html.erb45, 112行目

<div class="container mt-4">
    <div class="row">
        <!-- 右側 -->
        <div class="col-md-3">

            <!-- 予約フォーム -->
            <%= render 'reservations/form' %>

        </div>
        <!-- 左側 -->
        <div class="col-md-9 mb-4">

            <!-- カルーセル表示 -->
            <div class="card">
                <div class="card-body">

                    <div id="carouselExampleIndicators" class="carousel slide" data-bs-ride="carousel">
                        <div class="carousel-indicators">
                            <% @photos.each do |photo| %>
                                <button type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide-to="<%= @i %>" class="<%= 'active' if photo.id == @photos[0].id %>" aria-current="true" aria-label="Slide <%= @i+1 %>"></button>
                                <% @i = @i +1 %>
                            <% end %>
                        </div>
                        <div class="carousel-inner">
                            <% @photos.each do |photo| %>
                                <div class="carousel-item <%= 'active' if photo.id == @photos[0].id %>">
                                    <%= image_tag url_for(photo), class: "d-block w-100", style: "border-radius: 10px;" %>
                                </div>
                            <% end %>
                        </div>
                        <button class="carousel-control-prev" type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide="prev">
                            <span class="carousel-control-prev-icon" aria-hidden="true"></span>
                            <span class="visually-hidden">Previous</span>
                        </button>
                        <button class="carousel-control-next" type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide="next">
                            <span class="carousel-control-next-icon" aria-hidden="true"></span>
                            <span class="visually-hidden">Next</span>
                        </button>
                    </div>
                </div>
            </div>

            <div class="card mb-2 mt-2">
                <div class="card-body">
                    <i class="fa fa-star fa-2x" style="color: gold;"></i><span style="font-size: 1.6rem;"><%= pluralize(@room.average_rating, "") %></span>
                    <h2 class="mb-4 mt-4"><%= @room.listing_name %></h2>

                    <div class="fs-5"><%= @room.address %></div>
                    <div class="mt-4">
                    <%= link_to user_path(@room.user), style: "text-decoration:none;" do %>
                        <%= image_tag avatar_url(@room.user), class: "bd-placeholder-img figure-img img-fluid rounded-pill", style: "width: 40px; height: 30px;" %>
                        <span class="badge bg-light text-dark"><%= @room.user.full_name %></span>
                    <% end %>
                    </div>

                </div>
            </div>

            <!-- 部屋のインフォメーション -->
            <div class="card mb-2">
                <div class="card-body">    
                    <div class="row">
                        <div class="col-3">
                            <i class="fas fa-home fa-2x" style="color: cadetblue"></i><br/>
                            <%= @room.home_type %>
                        </div>
                        <div class="col-3">
                            <i class="fas fa-user fa-2x" style="color: cadetblue"></i><br/>
                            <%= pluralize(@room.accommodate, "人宿泊可能") %>
                        </div>
                        <div class="col-3">
                            <i class="fas fa-bed fa-2x" style="color: cadetblue"></i><br/>
                            <%= pluralize(@room.bed_room, "台") %>
                        </div>
                        <div class="col-3">
                            <i class="fas fa-door-closed fa-2x" style="color: cadetblue"></i><br/>
                            <%= pluralize(@room.bath_room, "部屋") %>
                        </div>
                    </div>
                </div>
            </div>

            <div class="card">
                <div class="card-body">        
                    <div class="badge bg-secondary mb-4">部屋の詳細</div>
                    <p><%= @room.summary %></p>
                </div>
            </div>            

            <!-- アメニティ -->
            <div class="card mt-2">
                <div class="card-body">        
                    <div class="badge bg-secondary mb-4">アメニティ</div>

                    <div class="row">
                        <div class="col-6">
                            <ul style="list-style:none;">
                                <li class="<%= 'text-line-through' if !@room.is_tv %>"><span style="color: black;"><i class="fas fa-tv"></i>&nbsp;テレビ</span></li>
                                <li class="<%= 'text-line-through' if !@room.is_kitchen %>"><span style="color: black;"><i class="fas fa-blender"></i>&nbsp;キッチン</span></li>
                                <li class="<%= 'text-line-through' if !@room.is_internet %>"><span style="color: black;"><i class="fas fa-wifi"></i>&nbsp;Wi-Fi</span></li>
                            </ul>
                        </div>
                        <div class="col-6">
                            <ul style="list-style:none;">
                                <li class="<%= 'text-line-through' if !@room.is_heating %>"><span style="color: black;"><i class="fab fa-hotjar"></i>&nbsp;暖房</span></li>
                                <li class="<%= 'text-line-through' if !@room.is_air %>"><span style="color: black;"><i class="fas fa-temperature-low"></i>&nbsp;エアコン</span></li>
                            </ul>
                        </div>
                    </div>
                </div>           
            </div>

            <!-- レビュー -->
            <div class="card mt-2">
                <div class="card-body">        
                    <div class="badge bg-warning mb-4">レビュー(<%= @guest_reviews.count %>)</div>
                    <!-- レンダー -->
                    <%= render "reviews/guest_list" %>
 
                </div>
            </div>

            <!-- GOOGLE マップ -->
            <div class="card mt-4">
                <div class="card-body">                    
                    <div id="map" style="width: 100%; height: 400px"></div>
                    <script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDoAh6S8kEpArgzGFEvjT_0xK_VeUL5SEs&callback=initMap" type="text/javascript"></script>           
                    <script>
                        function initMap() {
                        const uluru = { lat: <%= @room.latitude %>, lng: <%= @room.longitude %> };
                        const map = new google.maps.Map(document.getElementById("map"), {
                            zoom: 14,
                            center: uluru,
                        });
                        const contentString =
                            '<div id="content">' +
                            '<%= image_tag room_cover(@room), style: "width: 200px;" %>' +
                            "</div>" +
                            "<h5>" +
                            '<%= @room.listing_name %>' +
                            "</h5>" +
                            "<div>" +
                            '<%= @room.address %>' +
                            "</div>";
                        const infowindow = new google.maps.InfoWindow({
                            content: contentString,
                            ariaLabel: "<%= @room.listing_name %>",
                        });
                        const marker = new google.maps.Marker({
                            position: uluru,
                            map,
                            title: "<%= @room.listing_name %>",
                        });
                        
                        marker.addListener("click", () => {
                            infowindow.open({
                            anchor: marker,
                            map,
                            });
                        });
                        }
                        window.initMap = initMap;
                    </script>
                </div>
            </div>

            <!-- 近くのお部屋を検索 -->
            <div class="card mt-4 mb-4">
                <div class="card-body">
                    <h3 class="badge bg-secondary">近くの部屋</h3>
                    <div class="row">
                    <% for room in @room.nearbys(10) %>                    
                        <div class="col-md-4">
                        <div class="card mb-2">
                        <%= link_to room_path(room), data: { turbo: false} do %>
                            <%= image_tag room_cover(room), class: "card-image-top", style: "width: 100%;" %>   
                        <% end %>                           
                        <div class="card-body">             
                            <div>
                                <%= link_to room.listing_name, room, class: "btn btn-light", data: { turbo: false} %>
                            </div>
                            <div class="badge bg-danger">距離:<%= room.distance.round(2) %> Km</div>

                        </div>
                        </div>

                        </div>
                    <% end %>
                    </div>
                </div>
            </div>

        </div>
    </div>
</div>



ブラウザを確認します。
コメントを投稿した人のみ、削除することができます。
動作を確認して下さい。
http://localhost:3000/rooms/1

PCレイアウト
PCレイアウト


モバイルレイアウト
モバイルレイアウト



ホストからのレビューをユーザー詳細ページに表示させます。
「app\views\users\show.html.erb」ファイルを編集します。


記述編集 【app\views\users\show.html.erb】

<div class="container mt-4 mb-4">
    <div class="row">
        <!-- 左側 -->
        <div class="col-md-4 mb-4">
            <div class="card">
                <div class="card-body">

                    <!-- ステータス -->
                    <div>
                        <% if @user.status %>
                            <span class="badge bg-success"><i class="fa-regular fa-bell"></i>オンライン</span>
                        <% else %>
                            <span class="badge bg-secondary"><i class="fa-regular fa-bell-slash"></i>オフライン</span>
                        <% end %>                                   
                    </div>                
                    <!-- アバター -->
                    <%= image_tag avatar_url(@user), class: "img-fluid img-thumbnail rounded-pill" %>
                    <h4 class="text-center"><%= @user.full_name %></h4>   
                    
                    <!-- 自己紹介 -->
                    <div class="h5 text-center"><%= @user.about %></div>                           
                </div>
            </div>       

        </div>
        <!-- 右側 -->
        <div class="col-md-8">

            <!-- 登録している部屋 -->
            <div class="card mb-4">           
                <div class="card-body">
                    <h5 class="card-title"><%= @user.full_name %>さんが登録している部屋</h5>

                    <div class="container mt-4">
                    <div class="row">
                        <% @user.rooms.each do |room| %>
                            <% if room.active? %>
                                <div class="col-md-4">
                                    <div class="card">
                                        <div class="card-body">
                                            <span><i class="fa fa-star fa-1x" style="color: gold;"></i><%= pluralize(room.average_rating, "") %></span>
                                            <%= link_to room_path(room), data: { turbolinks: false} do %>
                                                <%= image_tag room_cover(room), style: "width: 100%;" %>
                                                <h5 class="card-title">
                                                    <span class="btn btn-light"><%= room.listing_name %></span>
                                                </h5>
                                            <% end %>
                                            <div class="card-text" style="margin-left: 0.5rem;">
                                                <p style="font-size: 0.8rem; margin-bottom: -0.3rem;">Address</p>
                                                <p style="margin-bottom: 2rem;"><%= room.address %></p>
                               
                                            </div>
                                
                                            <h5 class="badge rounded-pill bg-danger text-light" style="font-size: 1rem;">1泊<%= number_to_currency(room.price) %></h5>
                                        </div>
                                    </div>
                                </div>
                            <% end %>
                        <% end %>
                    </div>
                </div>

                </div>
            </div>

            <!-- レビュー -->
            <div class="card">           
                <div class="card-body">
                    <h5 class="card-title"><%= @user.full_name %>さんへのレビュー</h5>
                        <%= render "reviews/guest_list" %>
                        <%= render "reviews/host_list" %>

                </div>
            </div>
        </div>     
    </div>
</div>



ページを確認します。
ブラウザ確認
http://localhost:3000/users/2

PCレイアウト
PCレイアウト


モバイルレイアウト
モバイルレイアウト



同じくダッシュボードページにもレビューを表示させます。


記述追加 「app/views/users/dashboard.html.erb」

<div class="container mt-4 mb-4">
    <div class="row">
        <!-- 左側 -->
        <div class="col-md-4">
            <div class="card mb-4">
                <div class="card-body">
                    <!-- アバター -->
                    <%= image_tag avatar_url(current_user), class: "img-fluid img-thumbnail rounded-pill" %>
                    <h4 class="text-center"><%= current_user.full_name %></h4>
                    <!-- 画像アップロードボタン -->
                    <button class="btn btn-info text-light w-100" type="button" data-bs-toggle="collapse" data-bs-target="#collapse1" aria-expanded="false" aria-controls="collapse1">
                        <i class="fa-solid fa-cloud-arrow-up"></i>アバター画像アップロード
                    </button>
                    <div class="collapse" id="collapse1">
                    <div class="card card-body">
                        <%= form_for :user, url: users_edit_url(current_user), action: :update, method: :post do |f| %>
                            <%= f.file_field :avatar, class: "input-group-text", onchange: "this.form.submit();" %>
                        <% end %>   
                    </div>
                </div>
                <hr/>
                <div>
                    <%= link_to "部屋新規登録", new_room_path, class: "btn btn-danger" %>
                </div>
            
                <hr/>
                <!-- ステータス -->
                <div type="button" data-bs-toggle="collapse" data-bs-target="#collapse2" aria-expanded="false" aria-controls="collapse2">

                    <% if current_user.status %>
                        <span class="btn btn-success"><i class="toggle far fa-edit"></i>オンライン</span>
                    <% else %>
                        <span class="btn btn-secondary"><i class="toggle far fa-edit"></i>オフライン</span>
                    <% end %>                
                
                </div>
                <div class="collapse" id="collapse2">
                    <div class="card card-body">

                        <%= form_for :user, url: users_edit_url(current_user), action: :update, method: :post do |f| %>
                            <%= f.select(:status, options_for_select([["オンライン", true], ["オフライン", false]]), {}, {class: "custom-select"}) %>                        
                            <%= f.submit "保存", class: "btn btn-dark" %>
                        <% end %>

                    </div>
                </div>
                <hr/>
                <!-- 自己紹介 -->
                <div class="h5"><%= current_user.about %></div>
                <button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#collapse3" aria-expanded="false" aria-controls="collapse3">
                    自己紹介編集
                </button>

                <div class="collapse" id="collapse3">
                    <div class="card card-body">
                        <%= form_for :user, url: users_edit_url(current_user), action: :update, method: :post do |f| %>
                            <div><%= f.text_area :about, autofocus: true, autocomplete: 'form'%></div>
                            <%= f.submit "保存", class: "btn btn-dark" %>
                        <% end %>                        
                    </div>
                </div>
                <hr/>
                <!-- 電話番号 -->
                <div>
                    <% if !current_user.phone_number.blank? %>
                        <span class="pull-right icon-babu"><i class="far fa-check-circle" style="color:green;"></i></span>&nbsp;&nbsp;電話番号
                    <% else %>
                        <div class="text-danger">電話番号が登録されていません</div>
                        <%= link_to  "電話番号登録", edit_user_registration_path, class: "btn btn-danger" %>
                    <% end %>                    
                </div>
                <hr/>
                <!-- アカウント登録日 -->
                登録:<%= I18n.l(current_user.created_at, format: :full_date) %>                                
                
            </div>
        </div>


        </div>
        <!-- 右側 -->
        <div class="col-md-8">

            <!-- お知らせ -->
            <div class="card mb-2">
                <div class="card-body">
                    <h5 class="card-title">お知らせ</h5>
                    <h6 class="card-subtitle mb-2 text-body-secondary">
                    </h6>
                    <p class="card-text">
                    </p>    
                </div>
            </div>

            <!-- 登録している部屋 -->
            <div class="card">
                <div class="card-body">
                    <h5 class="card-title">登録している部屋</h5>
                    <h6 class="card-subtitle mb-2 text-body-secondary">
                    </h6>

                    <div class="row">
                        <% current_user.rooms.each do |room| %>
                            <% if room.active? %>
                                <div class="col-md-4">
                                    <div class="card mb-2">
                                        <div class="card-body">
                                            <span><i class="fa fa-star fa-1x" style="color: gold;"></i><%= pluralize(room.average_rating, "") %></span>
                                            <%= link_to room_path(room), data: { turbolinks: false} do %>
                                                <%= image_tag room_cover(room), style: "width: 100%;" %>
                                                <h5 class="card-title">
                                                    <span class="btn btn-light"><%= room.listing_name %></span>
                                                </h5>
                                            <% end %>
                                            <div class="card-text" style="margin-left: 0.5rem;">
                                                <p style="font-size: 0.8rem; margin-bottom: -0.3rem;">Address</p>
                                                <p><%= room.address %></p>
                                                <h5 class="badge rounded-pill bg-danger text-light mb-4" style="font-size: 1rem;">1泊<%= number_to_currency(room.price) %></h5>               
                                                <%= link_to listing_room_path(room), class: "btn btn-outline-primary w-100 mb-4" do %>
                                                    登録内容編集
                                                <% end %>
                                              
                                            </div>
                             
                                        </div>
                                    </div>
                                </div>
                            <% end %>
                        <% end %>
                    </div>

                </div>
            </div>

            <!-- レビュー -->
            <div class="card mt-2">           
                <div class="card-body">
                    <h5 class="card-title"><%= current_user.full_name %>さんへのレビュー</h5>
                        <%= render "reviews/guest_list" %>
                        <%= render "reviews/host_list" %>

                </div>
            </div>

        </div>
     
    </div>

</div>



ダッシュボードページを確認します。
ブラウザ確認
http://localhost:3000/dashboard

PCレイアウト
PCレイアウト


モバイルレイアウト
モバイルレイアウト



31 | 予約確認】 << 【ホーム】 >> 【33 | Ransack




↓↓クリックして頂けると励みになります。

YAE C5 CLINIC(札幌美容クリニック)

関連記事(外部サイト)