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

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

Rails6.0 | 民泊サイトの構築(改良版)| 33 |レビューの実装 | for MacOSX



| 32 |予約確認ページの実装 <<  [ホーム] >> | 34 |ホームページとAJAX検索


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


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


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



コマンド
yarn add raty-js@2.9.0


「app\javascript\packs\application.js」ファイルに以下の記述を追加します。


記述追加 app\javascript\packs\application.js(15行目)

require("raty-js")



app\javascript\packs\application.js

// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")

require("jquery")

require("jquery-ui/ui/widgets/datepicker");

require("raty-js")

import 'bootstrap';
import '../stylesheets/application';

window.Noty = require("noty")
window.Dropzone = require("dropzone")


// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)

require("trix")
require("@rails/actiontext")


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



コマンド
一文です。
rails g model Review review:text stars: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\20200727021130_create_reviews.rb
「,default: 1」の記述追加(5行目)と「, foreign_key: { to_table: :users }」の記述追加(8,9行目)をしています。

class CreateReviews < ActiveRecord::Migration[6.0]
  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, 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」ファイルを以下のように編集します。


記述追加 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
  
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :confirmable

  validates :full_name, presence: true, length: {maximum: 50}
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(: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[: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(:review, :stars, :room_id, :reservation_id, :host_id)
      end
  end



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

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, :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
  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, :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」ファイルを編集します。


12行目に以下の記述を追加します。

# ユーザーがホストの場合、ホストに対するすべてのゲストレビューを表示
@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

  before_action :authenticate_user!

  def dashboard
  end

  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

  def update
    @user = current_user
    if @user.update_attributes(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


ルートを設定します。



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

  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'

  devise_for :users, 
              path: '', 
              path_names: {sign_up: 'register', sign_in: 'login', edit: 'profile', sign_out: 'logout'},
              controllers: {registrations: 'registrations'}

  get 'pages/home'
  get '/dashboard', to: 'users#dashboard'
  get '/users/:id', to: 'users#show', as: 'user'
  get '/your_trips' => 'reservations#your_trips'
  get '/your_reservations' => 'reservations#your_reservations'
  
  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]

  # 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-toggle="modal" data-target="#exampleModal<%= reservation.id %>">
<font style="font-size: 0.9rem;">レビュー</font>
</button>
<!-- Modal -->
<div class="modal fade" id="exampleModal<%= reservation.id %>" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <%= form_for GuestReview.new do |f| %>
        <div class="modal-header">
          <h5 class="modal-title" id="exampleModalLabel"><div id="stars<%= reservation.id %>"></div></h5>
          <button type="button" class="close" data-dismiss="modal" aria-label="Close">
            <span aria-hidden="true">&times;</span>
          </button>
        </div>
        <div class="modal-body">
          <br/>
  
          <%= f.hidden_field :reservation_id, value: reservation.id %>
          <%= f.text_area :review, class: "textarea", style: "width: 20rem; height: 10rem; margin-top: -1rem;" %>
          <%= 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>
          <br/>
          <br/>
            <button type="button" class="btn btn-secondary" data-dismiss="modal">キャンセル</button>
            <%= f.submit "レビューする", class: "btn btn-warning" %>
          </div>
  
      <% end %>
        </div>
    </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-toggle="modal" data-target="#exampleModal<%= reservation.id %>">
レビュー
</button>
<!-- Modal -->
<div class="modal fade" id="exampleModal<%= reservation.id %>" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <%= form_for HostReview.new do |f| %>
        <div class="modal-header">
          <h5 class="modal-title" id="exampleModalLabel"><div id="stars<%= reservation.id %>"></div></h5>
          <button type="button" class="close" data-dismiss="modal" aria-label="Close">
            <span aria-hidden="true">&times;</span>
          </button>
        </div>
        <div class="modal-body">
          <br/>
  
          <%= f.hidden_field :reservation_id, value: reservation.id %>
          <%= f.text_area :review, class: "textarea", style: "width: 20rem; height: 10rem; margin-top: -1rem;" %>
          <%= 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>
          <br/>
          <br/>
            <button type="button" class="btn btn-secondary" data-dismiss="modal">キャンセル</button>
            <%= f.submit "レビューする", class: "btn btn-warning" %>
          </div>
  
      <% end %>
        </div>
    </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(53行目)

<%= render partial: "reviews/guest_form", locals: {reservation: trip} %>



app\views\reservations\your_trips.html.erb

<div class="row" style="margin-top: 50px; margin-left: 2rem;">
    <div class="card">
    <div class="bg-dark">
        <h3 style="margin-top: 30px; font-size: 1.5rem; color: white; position: relative; bottom: 0.7rem; left: 3rem;">予約内容 Reservation contents</h3>
    </div>

        <table class="table table-striped text-center">
            <thead>
                <tr>
                    <th>申し込み日</th>
                    <th>ステータス</th>
                    <th>部屋</th>
                    <th>ホスト</th>
                    <th>日程</th>
                    <th>泊数</th>
                    <th>料金</th>
                    <th>レビュー</th>
                </tr>
            </thead>
            <tbody>
                <% if @trips.blank? %>
                  <tr>
                      <td colspan="7"><h5>表示できる予約はありません。</h5></td>
                  </tr>
                <% end %>
                <% @trips.each do |trip| %>
                    <tr>
                        <td><%= I18n.l(trip.created_at, format: :full_date) %></td>
                        <!-- ステータス -->
                        <td>
           
                        </td>
                        <td>
                            <%= link_to room_path(trip.room), data: { turbolinks: false} do %>                                
                                <%= image_tag room_cover(trip.room), style: "width: 60px;" %><br/>
                                <span class="badge bg-dark" style="color: white; font-size: 0.5rem;"><%= trip.room.listing_name %></span>
                            <% end %>
                        </td>
                        <td>
                            <%= link_to user_path(trip.room.user), class: "tootip" do %>
                                    <%= image_tag avatar_url(trip.room.user), style: "width: 50px;", class: "bd-placeholder-img figure-img img-fluid rounded-pill" %><br/>
                                    <span class="badge bg-dark" style="color: white; font-size: 0.5rem;"><%= trip.room.user.full_name %></span>
                            <% end %>
                        </td>
                        <td>
                            <span class="badge bg-success text-light" style="position: relative; top: 0.1rem;">チェックイン</span><%= I18n.l(trip.start_date, format: :full_date) %><br/>
                            <span class="badge bg-danger text-light" style="position: relative; top: 0.01rem;">チェックアウト</span><%= I18n.l(trip.end_date, format: :full_date) %>
                        </td>
                        <td><%=trip.total/trip.price %></td>
                        <td><%= number_to_currency(trip.total) %></td>
                        <!-- アクション -->
                        <td>
                        <% if trip.end_date < Date.today %>
                            <%= render partial: "reviews/guest_form", locals: {reservation: trip} %> 
 
                        <% else %>
                            <span style="font-size: 0.6rem;">チェックアウト後にレビューできます<br/>Can be reviewed after check out</span>
                        <% end %>
                        </td>
                    </tr>
                <% end %>
            </tbody>
        </table>
    </div>
</div>



ブラウザ確認
http://localhost:3000/your_trips

ゲスト予約確認
ゲスト予約確認


ゲストレビューフォーム
ゲストレビューフォーム



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


記述追加(55行目)

<%= render partial: "reviews/host_form", locals: {reservation: reservation} %>



app\views\reservations\your_reservations.html.erb

<div class="row" style="margin-top: 50px; margin-left: 2rem;">
    <div class="card">
    <div class="bg-danger">
        <h3 style="margin-top: 30px; font-size: 1.5rem; color: white; position: relative; bottom: 0.7rem; left: 3rem;">受注 Order received</h3>
    </div>

        <table class="table table-striped text-center">
            <thead>
                <tr>
                    <th>申し込み日</th>
                    <th>ステータス</th>
                    <th>部屋</th>
                    <th>ゲスト</th>
                    <th>日程</th>
                    <th>泊数</th>
                    <th>料金</th>
                    <th>レビュー</th>
                </tr>
            </thead>
            <tbody>
                <% if @rooms.blank? %>
                  <tr>
                      <td colspan="7"><h5>表示できる受注はありません。</h5></td>
                  </tr>
                <% end %>
                <% @rooms.each do |room| %>
                    <% room.reservations.each do |reservation| %>
                    <tr>
                        <td><%= I18n.l(reservation.created_at, format: :full_date) %></td>
                        <!-- ステータス -->
                        <td>
           
                        </td>
                        <td>
                            <%= link_to room_path(reservation.room), data: { turbolinks: false} do %>                                
                                <%= image_tag room_cover(reservation.room), style: "width: 60px;" %><br/>
                                <span class="badge bg-dark" style="color: white; font-size: 0.5rem;"><%= reservation.room.listing_name %></span>
                            <% end %>
                        </td>
                        <td>
                            <%= link_to user_path(reservation.user), class: "tootip" do %>
                                    <%= image_tag avatar_url(reservation.user), style: "width: 50px;", class: "bd-placeholder-img figure-img img-fluid rounded-pill" %><br/>
                                    <span class="badge bg-dark" style="color: white; font-size: 0.5rem;"><%= reservation.user.full_name %></span>
                            <% end %>
                        </td>
                        <td>
                            <span class="badge bg-success text-light" style="position: relative; top: 0.1rem;">チェックイン</span><%= I18n.l(reservation.start_date, format: :full_date) %><br/>
                            <span class="badge bg-danger text-light" style="position: relative; top: 0.01rem;">チェックアウト</span><%= I18n.l(reservation.end_date, format: :full_date) %>
                        </td>
                        <td><%=reservation.total/reservation.price %></td>
                        <td><%= number_to_currency(reservation.total) %></td>
                        <!-- レビュー -->
                        <td>
                        <% if reservation.end_date < Date.today %>
                            <%= render partial: "reviews/host_form", locals: {reservation: reservation} %>
 
                        <% else %>
                            <span style="font-size: 0.6rem;">チェックアウト後にレビューできます<br/>Can be reviewed after check out</span>
                        <% end %>


                        
                        </td>
                    </tr>
                    <% end %>
                <% end %>
            </tbody>
        </table>
    </div>
</div>



ブラウザ確認
http://localhost:3000/your_reservations

ホスト受注確認
ホスト受注確認



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


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

<% @guest_reviews.each do |r| %>
    <article class="media" style="margin: 30px;">
                <%= image_tag avatar_url(r.guest), style: "width: 80px;", class: "bd-placeholder-img figure-img img-fluid rounded-pill" %>
        <div class="media-content">
            <div class="content">
                <p style="margin-left: 30px;">
                    <span class="badge bg-secondary text-light" style="position: relative; top: 0.01rem;"><%= r.guest.full_name %></span><br>
                    <%= r.review %>
                </p>
                <small style="margin-left: 30px;"><%= time_ago_in_words(r.created_at) %></small>
            </div>
        </div>
        <div class="media-right">
            <span class="star-review"><i class="fa fa-star" style="color: gold;"></i> <%= r.stars %></span>
        </div>
        <% if current_user && current_user == r.guest %>
            <div class="media-right">
                <%= link_to r, method: :delete, data: {confirm: "レビューを削除してよろしいですか?"} do %>
                    &nbsp;&nbsp;<i class="fas fa-trash-alt"></i>
                <% end %>
            </div>
        <% end %>
    </article>
<% end %>



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


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

<% @host_reviews.each do |r| %>
    <article class="media">
        <figure class="media-left">
            <p class="image is-64x64">
                <%= image_tag avatar_url(r.host), style: "width: 80px;", class: "bd-placeholder-img figure-img img-fluid rounded-pill" %>
            </p>
        </figure>
        <div class="media-content">
            <div class="content">
                <p>
                    <span class="badge bg-secondary text-light" style="position: relative; top: 0.1rem; left: 0.5rem;"><%= r.host.full_name %></span><br>
                    <span style="position: relative; top: 0.1rem; left: 0.5rem;"><%= r.review %></span>
                </p>
                <span style="position: relative; top: 0.1rem; left: 0.5rem;"><small><%= time_ago_in_words(r.created_at) %></small></span>
            </div>
        </div>
        <div class="media-right">
            <span class="star-review" style=" position: relative; right: 2rem;"><i class="fa fa-star" style="color: gold;"></i></span><span style=" position: relative; bottom: 1.5rem; right: 0.5rem;"><%= r.stars %></span><br/>
        </div>
        <% if current_user && current_user == r.host %>
            <div class="media-right">
                <%= link_to r, method: :delete, data: {confirm: "レビューを削除してよろしいですか?"} do %>
                    <i class="fas fa-trash-alt"></i>
                <% end %>
            </div>
        <% end %>
    </article>
<% end %>



app\views\rooms\show.html.erb

<div class="row" style="margin-left: 10px; margin-right: 10px;">
    <!-- 左パネル -->
    <div class="col-md-8">

        <!-- 写真 -->
        <div class="row" style="margin-top: 1rem;">
            <div class="col-md-11">
                <div class="card" style="width: 100%;">
                    <div class="card-body">
                        <%= image_tag room_cover(@room), style: "width: 100%" %>
                    </div>
                </div>
            </div>
        </div>
        <br/>

        <!-- 部屋の名前 -->
        <div class="row">
            <div class="col-md-11">
                <div class="card">
                    <div class="card-body">
                        <h3  style="font-size: 2rem;;"><%= @room.listing_name %></h3><br/>
                        <label>Address</label>
                        <div style="font-size: 1.5rem; margin-top: -1rem;"><%= @room.address %></div>
                        <div class="text-right">
                            <%= image_tag avatar_url(@room.user), class: "bd-placeholder-img figure-img img-fluid rounded-pill", style: "width: 40px; height: 30px;" %>
                            <%= @room.user.full_name %>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <br/>
        <!-- レビュー -->
        <div class="row">
            <div class="col-md-11">
                <div class="card">
                    <div class="card-header bg-warning">
                        レビュー(<%= @guest_reviews.count %>)
                    </div>
                    <div class="card-content">
                        <!-- レンダー -->
                        <%= render "reviews/guest_list" %>
                    </div>
                </div>
            </div>
        </div>        
        <br/>        
        <!-- 部屋のインフォメーション -->
        <div class="row text-babu">
            <div class="col-md-11">
                <div class="card">
                    <div class="card-body">    
                        <div class="row text-center row-space-1">
                            <div class="col-md-3">
                                <i class="fas fa-home fa-2x" style="color: cadetblue"></i><br/>
                                <%= @room.home_type %>
                            </div>
                            <div class="col-md-3">
                                <i class="fas fa-user fa-2x" style="color: cadetblue"></i><br/>
                                <%= pluralize(@room.accommodate, "人宿泊可能") %>
                            </div>
                            <div class="col-md-3">
                                <i class="fas fa-bed fa-2x" style="color: cadetblue"></i><br/>
                                <%= pluralize(@room.bed_room, "台") %>
                            </div>
                            <div class="col-md-3">
                                <i class="fas fa-door-closed fa-2x" style="color: cadetblue"></i><br/>
                                <%= pluralize(@room.bath_room, "部屋") %>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <br/>
        <!-- 詳細 -->
        <div class="row">
            <div class="col-md-11">
                <div class="card">
                    <div class="card-body">        
                        <h4 class="badge bg-dark" style="font-size: 1.2rem; color: white;">部屋の詳細</h4>
                        <p><font style="font-size: 1.2rem;"><%= @room.description %></font></p>
                    </div>
                </div>
            </div>
        </div>
        <br/>

        <!-- アメニティ -->
        <div class="row">
            <div class="col-md-11">
                <div class="card">
                    <div class="card-body">        
                        <h4 class="badge bg-dark" style="font-size: 1.2rem; color: white;">アメニティ</h4>

                        <div class="col-md-8">
                            <div class="row">
                                <div class="col-md-5">
                                    <ul class="amenities">
                                        <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; font-size: 1.1rem;"><i class="fas fa-wifi"></i>&nbsp;Wi-Fi</span></li>
                                    </ul>
                                </div>
                                <div class="col-md-5">
                                    <ul class="amenities">
                                        <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>
            </div>
        </div>
    
        <br/>

        <!-- カルーセル表示 -->
        <div class="row">
            <div class="col-md-11">
                <div id="carouselExampleControls" class="carousel slide" data-ride="carousel">
                <% if @photos.length > 0 %>    
                    <ol class="carousel-indicators">
                        <% @photos.each do |photo| %>
                            <li data-target="#carouselExampleIndicators" data-slide-to="<%= photo.id %>" class="<%= 'active' if photo.id == @photos[0].id %>"></li>
                        <% end %>
                    </ol>
                    <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: "bd-placeholder-img bd-placeholder-img-lg d-block w-100", width: "100%" %>
                            </div>
                        <% end %>
                        <a class="carousel-control-prev" href="#carouselExampleControls" role="button" data-slide="prev">
                        <span class="carousel-control-prev-icon" aria-hidden="true"></span>
                        <span class="sr-only">Previous</span>
                        </a>
                        <a class="carousel-control-next" href="#carouselExampleControls" role="button" data-slide="next">
                        <span class="carousel-control-next-icon" aria-hidden="true"></span>
                        <span class="sr-only">Next</span>
                        </a>
                <% end %>
                    </div>
            </div>
        </div>
        <hr/>
        <!-- googleマップ -->
        <div class="col-md-11" style="margin-top: 30px;">
            <div id="map" style="width: 100%; height: 600px"></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(@room), style: "width: 300px;", :alt => '' %></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=AIzaSyDoAh6S8kEpArgzGFEvjT_0xK_VeUL5SEs&callback=initMap" type="text/javascript"></script>
        </div>
        <!-- 近くのお部屋を検索 -->

        <div class="row" style="margin-top: 3rem; margin-bottom: 5rem;">
            <div class="col-md-11">
                <div class="card">
                    <div class="card-header bg-info text-light">
                    近くの部屋
                    </div>
                    <div class="row" style="margin: 2rem;">
                        <% for room in @room.nearbys(10) %>
                            <div class="col-6 col-md-3">
                                <%= image_tag room_cover(room), style: "width: 100%; height: 180;" %>
                                <h5 class="card-title badge bg-dark"><%= link_to room.listing_name, room, data: { turbolinks: false},  style: "font-size: 0.5rem; color: white; text-decoration: none;" %></h5><br/>
                                <span style="font-size: 0.8rem;"><%= room.address %></span>
                                <footer>(距離:<%= room.distance.round(2) %> Km)</footer>
                            </div>
                        <% end %>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- 右パネル -->
<div class="col-md-4" style="margin-top: 1rem;">


<div class="card" style="width: 24rem; margin-left: -70px;">
  <div class="card-body">
    <h5 class="card-title"><%= @room.listing_name %></h5>
    <h6 class="card-subtitle mb-2 text-muted">
        <label>Address</label>
        <div style="font-size: 1.2rem; margin-top: -0.5rem;"><%= @room.address %></div>
    <p class="card-text"><%= @room.description %></p>
    </h6>

        <div class="row text-center row-space-1">
            <div class="col-md-3">
                <i class="fas fa-home fa-1x" style="color: cadetblue"></i><br/>
                <span style="font-size: 0.2rem;"><%= @room.home_type %></span>
            </div>
            <div class="col-md-3">
                <i class="fas fa-user fa-1x" style="color: cadetblue"></i><br/>
                <span style="font-size: 0.2rem;"><%= pluralize(@room.accommodate, "人宿泊可能") %></span>
            </div>
            <div class="col-md-3">
                <i class="fas fa-bed fa-1x" style="color: cadetblue"></i><br/>
                <span style="font-size: 0.2rem;"><%= pluralize(@room.bed_room, "台") %></span>
            </div>
            <div class="col-md-3">
                <i class="fas fa-door-closed fa-1x" style="color: cadetblue"></i><br/>
                <span style="font-size: 0.2rem;"><%= pluralize(@room.bath_room, "部屋") %></span>
            </div>

        </div>
        <br/>

        <div class="row text-center row-space-1">

            <% if @room.is_tv %>
                <div class="col-md-3">
                    <span style="color: black;"><i class="fas fa-tv fa-1x"></i><span style="font-size: 0.2rem;">&nbsp;テレビ</span></span>
                </div>
            <% end %>
            <% if @room.is_kitchen %>
                <div class="col-md-3">
                    <span style="color: black;"><i class="fas fa-blender fa-1x"></i><span style="font-size: 0.2rem;">&nbsp;キッチン</span></span>
                </div>
            <% end %>
            <% if @room.is_internet %>
                <div class="col-md-3">
                    <span style="color: black;"><i class="fas fa-wifi fa-1x"></i><span style="font-size: 0.2rem;">&nbsp;Wi-fi</span></span>
                </div>
            <% end %>
            <% if @room.is_heating %>
            <div class="col-md-3">
                <span style="color: black;"><i class="fab fa-hotjar fa-1x"></i><span style="font-size: 0.2rem;">&nbsp;暖房</span></span>
            </div>
            <% end %>
            <% if @room.is_air %>
            <div class="col-md-3">
                <diiv class="<%= 'text-line-through' if !@room.is_air %>" style="position: relative; left: 0.4rem;"><span style="color: black;"><i class="fas fa-temperature-low fa-1x"></i><span style="font-size: 0.2rem;">&nbsp;エアコン</span></span></diiv>
            </div>   
            <% end %>                                        

        </div>
        <br/>

        <span class="badge bg-warning" style="font-size: 1.5rem; position: relative; top: 0.1rem;">1泊 <%= number_to_currency(@room.price) %></span>
  </div>
</div>


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

</div>



ブラウザ確認
http://localhost:3000/rooms/1

部屋詳細レビュー表示
部屋詳細レビュー表示



app\views\users\show.html.erb

<div class="row" style="margin: 20px;">

    <!-- 左側 -->
    <div class="col-4">
        <div class="card">
            <div class="card-body">      
                <!-- アバター -->
                <%= image_tag avatar_url(@user), class: "img-fluid img-thumbnail rounded-pill" %>
                <!-- ユーザー名 -->
                <div class="mx-auto" style="text-align: center;">
                
                    <h5 class="card-title btn-block">
              
                    <br/>
                    <!-- オンラインステータス -->
                    <span style="position:relative; top: 0.8rem; left: 0.5rem;">                        
                    <% if @user.status %>
                        <span class="badge rounded-pill bg-success text-light">Online</span>
                    <% else %>
                        <span class="badge rounded-pill bg-secondary text-light">Offline</span>
                    <% end %>                                  
                    </span>
                    <%= @user.full_name %>


                    </h5>
                    <%= @user.about %>   
                </div>
    

                <br/>
                    <!-- 自己紹介 -->                                                           
                    <div style="margin-left: 20px;">
                                            
                        <div class="collapse" id="collapseExample2">
                            <div class="card card-body" style="width: 16rem;">
                                <%= form_for :user, url: users_edit_url(current_user), action: :update, method: :post do |f| %>
                                    <%= f.text_area :about, autofocus: true, autocomplete: 'form'%>
                                    <br/>
                                    <br/>
                                    <%= f.submit "保存", class: "btn btn-outline-info" %>
                                <% end %>

                            </div>
                        </div>
                        <div class="mx-auto" style="margin-top: 1rem; text-align: right;">
                        <%= l(current_user.created_at, format: :long) %>
                        </div>   
                    </div>

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

    <!-- 右側 -->
    <div class="col-8">
 
        <!-- お知らせ -->
        <div class="card">
            <div class="card-header">
                お知らせ
            </div>
            <div class="card-body">
                <h5 class="card-title"></h5>
                <p class="card-text"></p>
            </div>
        </div>
        <br/>
        <!-- 登録しているお部屋 -->
        
        <!-- 登録しているお部屋 -->
        <div class="card">
            <div class="card-header bg-dark text-light">
            <%= @user.full_name %>さんが登録しているお部屋(<%= @rooms.length %>)
            </div>
            <div class="card-body">
                <div class="row">
                    <% @user.rooms.each do |room| %>
                        <% if room.active? %>
                        
                            <div class="col-6 col-md-4" style="margin-bottom: 20px;">
                                <%= link_to room_path(room), data: { turbolinks: false} do %>
                                    <%= image_tag room_cover(room), style: "width: 100%; height: 180;" %>
                                <% end %>
                                <%= link_to room.listing_name, room_path(room), data: { turbolinks: false}, class: "badge bg-info text-light" %><br/>
                                <div class="badge bg-secondary text-light">住所:<%= room.address %></div><br/>
                                <footer class="badge bg-warning" style="font-size: 0.9rem;"><%= number_to_currency(room.price) %></footer>
                            </div>
 
                        <%  end %>
                    <% end %>
                </div>
            </div>
        </div>
        <br/>
        <!-- レビュー -->
        <div class="card">
            <div class="card-header bg-warning">
                レビュー
            </div>
            <div class="card-body">
                <%= render "reviews/guest_list" %>
                <%= render "reviews/host_list" %>
            </div>
        </div>
        <br/>
    </div>
</div>



ブラウザ確認
http://localhost:3000/users/2

ユーザー詳細レビュー表示
ユーザー詳細レビュー表示



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


| 32 |予約確認ページの実装 <<  [ホーム] >> | 34 |ホームページとAJAX検索