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

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

Rails6.0 | 民泊予約サイトの構築 | 37 | AJAX検索

[36]ホームページ<< [ホームに戻る] >> [38]jQueryスライダー


記述追加 GemFile
「gem 'ransack', '~> 2.3'」の記述追加(83行目)

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.6.6'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

# Bulma
gem 'bulma-rails', '~> 0.7.4'
gem 'bulma-extensions-rails', '~> 1.0.30'

# デバイス
gem 'devise'

# 日本語化
gem 'rails-i18n'

# アマゾンS3
gem "aws-sdk"

# アクションテキスト画像表示
gem "mini_magick"
gem 'image_processing', '~> 1.2'

#googleマップ
gem 'geocoder', '~> 1.4'

# facebook認証
gem 'omniauth', '= 1.9.0'
gem 'omniauth-facebook', '= 5.0.0'

# google認証
gem 'omniauth-google-oauth2'

# 検索
gem 'ransack', '~> 2.3'



コマンド
bundle


記述追加 app\controllers\pages_controller.rb
「search()」メソッドの実装

class PagesController < ApplicationController
  
  def home
      @rooms = Room.where(active: true).limit(3)
  end
  
  def search
    # ステップ 1
    if params[:search].present? && params[:search].strip != ""
      session[:loc_search] = params[:search]
    end
    arrResult = Array.new
    # ステップ 2
    if session[:loc_search] && session[:loc_search] != ""
      @rooms_address = Room.where(active: true).near(session[:loc_search], 5, order: 'distance')
    else
      @rooms_address = Room.where(active: true).all
    end
    # ステップ 3
    @search = @rooms_address.ransack(params[:q])
    @rooms = @search.result
    @arrRooms = @rooms.to_a
    # ステップ 4
    if (params[:start_date] && params[:end_date] && !params[:start_date].empty? &&  !params[:end_date].empty?)
      start_date = Date.parse(params[:start_date])
      end_date = Date.parse(params[:end_date])
      @rooms.each do |room|
        not_available = room.reservations.where(
          "(? <= start_date AND start_date <= ?)
          OR (? <= end_date AND end_date <= ?)
          OR (start_date < ? AND ? < end_date)",
          start_date, end_date,
          start_date, end_date,
          start_date, end_date
        ).limit(1)
        if not_available.length > 0
          @arrRooms.delete(room)
        end
      end
    end
  end

end



記述追加 config\routes.rb
「get 'search' => 'pages#search'」の記述を追加(16行目)

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'
  get 'search' => 'pages#search'
  
  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\pages\home.html.erb
「<%= form_tag search_path, method: :get, data: { turbolinks: false} do %>」に記述変更(12行目)

<% content_for :head do %>
    <meta name="turbolinks-cache-control" content="no-cache">
<% end %>

<section class="hero is-medium has-bg-img">
    <div class="hero-body">
        <div class="container">
            <h1 class="title has-text-white">
                素敵なご旅行をあなたに <br>
                どんなお部屋をお探しですか?
            </h1>
            <%= form_tag search_path, method: :get, data: { turbolinks: false} do %>
                <br/>
                <br/>
                <div class="field is-horizontal">
                    <div class="field-body">
                        <div class="control has-icons-left">
                            <span class="icon is-small is-left">
                                <i class="fa fa-search"></i>
                            </span>
                            <%= text_field_tag :search, params[:search], placeholder: "どちらをお探し?", class: "input" %>
                        </div>
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                        <div class="field">
                            <p class="control is-expanded has-icons-left">
                                <%= text_field_tag :start_date, params[:start_date], placeholder: "チェックイン", readonly: true, class: "input is-primary is-rounded" %>
                                <span class="icon is-small is-left">
                                <i class="far fa-calendar-alt"></i>
                                </span>
                            </p>
                        </div>
                    
                        <div class="field">
                            <p class="control is-expanded has-icons-left has-icons-right">
                                <%= text_field_tag :end_date, params[:end_date], placeholder: "チェックアウト", readonly: true, class: "input is-primary is-rounded" %>
                                <span class="icon is-small is-left">
                                <i class="far fa-calendar-alt"></i>
                                </span>
                            </p>
                        </div>
                        <%= submit_tag "検索", class: "button is-primary" %>
                    </div>
                </div>
            <% end %>
        </div>
    </div>
</section>

<div class="box">
    <article class="media">
        <div class="column">
            <div class="columns is-multiline">
                <!-- 登録されたお部屋 -->
                <% @rooms.each do |room| %>
                    <% if room.active? %>
                        <div class="column is-one-third">
                            <div class="card">
                                <div class="card-image">
                                    <%= link_to listing_room_path(room) do %>
                                        <figure class="image is-4by3">
                                            <%= image_tag room_cover(room) %>
                                        </figure>
                                    <% end %>
                                </div>
                                <br/>
                                <div class="card-content p-t-5 p-b-5">
                                    <p class="subtitle is-6 m-b-5"><%= link_to room.listing_name, room_path(room), data: { turbolinks: false} %></p>
                                    <p class="subtitle is-6 m-b-5"><%= room.address %></p>
                                    <!--レビュー -->
                                    <span class="star-review"><i class="fa fa-star"></i>
                                        <%= room.average_rating %>
                                        <span class="has-text-primary">(<%= room.reviews.count %>)</span>
                                    </span>
                                </div>
                                <footer class="card-footer">
                                    <a class="has-text-danger is-block card-footer-item has-text-right">
                                        <span class="small-title">1泊の宿泊価格</span> 
                                        <strong><%= number_to_currency(room.price) %></strong>                                            
                                    </a>
                                </footer>
                            </div>
                        </div>
                    <% end %>
                <% end %>
            </div>
        </div>
    </article>
</div>

<script>
    $('#start_date').datepicker({
        dateFormat: 'dd-mm-yy',
        minDate: 0,
        maxDate: '3m',
        onSelect: function(selected) {
        $('#end_date').datepicker("option", "minDate", selected);
        $('#end_date').attr("disabled", false);
        }
    });
    $('#end_date').datepicker({
        dateFormat: 'dd-mm-yy',
        minDate: 0,
        maxDate: '3m',
        onSelect: function(selected) {
        $('#start_date').datepicker("option", "maxDate", selected);
        }
    });
</script>



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


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

<div id="main">
  <div id="left">
    <!-- 検索パネル -->

  <section class="section">
    <div class="container">

      <div id="collapsePanel">
          <%= search_form_for @search, url: search_path, remote: true do |f| %>

                <div class="row">
                    <div class="col-md-8">
                        <label>宿泊日</label>
                    </div>

                    <div class="field has-addons">
                        <p class="control">
                            <%= text_field_tag :start_date, params[:start_date], readonly: true, placeholder: "チェックイン日", class: "input" %>
                        </p>

                        &nbsp;&nbsp;&nbsp;&nbsp;
                        <p class="control">
                            <%= text_field_tag :end_date, params[:end_date], readonly: true, placeholder: "チェックアウト日", class: "input" %>
                        </p>

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

                <div class="row">
                    <div class="col-md-8">
                        <label>宿泊費(円)</label>
                    </div>

                    <div class="field has-addons">
                        <p class="control">
                            <%= f.text_field :price_gteq, placeholder: "最低価格", class: "input" %>
                        </p>
                        <p class="control">
                            <a class="button is-static"></a>
                        </p>
                        &nbsp;&nbsp;&nbsp;&nbsp;
                        <p class="control">
                            <%= f.text_field :price_lteq, placeholder: "最高価格", class: "input" %>
                        </p>
                        <p class="control">
                            <a class="button is-static"></a>
                        </p>
                    </div>
                </div>
                <br/>
                <br/>

                <div class="row">
                    <div class="checkbox">
                        <%= check_box_tag "q[room_type_eq_any][]", "プライベート", class: "checkbox" %> プライベート
                    </div>
                    &nbsp;&nbsp;&nbsp;&nbsp;
                    <div class="checkbox">
                        <%= check_box_tag "q[room_type_eq_any][]", "シェア", class: "checkbox" %> シェア
                    </div>
                </div>
                <br/>
                <br/>

                <div class="row">
                    <div class="col-md-4">

                        <div class="field">
                        <div class="control">
                        <label>宿泊人数 </label>
                            <div class="select">
                                <%= f.select :accommodate_gteq, [["1人", 1], ["2人", 2], ["3人", 3], ["4人以上", 4]], id: "accommodate", prompt: "選択してください", class: "select" %>
                            </div>
                        </div>
                        </div>

                        <div class="field">
                        <div class="control">
                        <label>ベッド数 </label>
                            <div class="select">
                                <%= f.select :bed_room_gteq, [["1台", 1], ["2台", 2], ["3台", 3], ["4台以上", 4]], id: "bed_room", prompt: "選択してください", class: "select" %>
                            </div>
                        </div>
                        </div>

                        <div class="field">
                        <div class="control">
                        <label>部屋数  </label>
                            <div class="select">
                                <%= f.select :bath_room_gteq, [["1部屋", 1], ["2部屋", 2], ["3部屋", 3], ["4部屋以上", 4]], id: "bath_rooms", prompt: "選択してください", class: "select" %>
                            </div>
                        </div>
                        </div>

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


                <div class="row">
                    <div class="col-md-4">
                        <%= check_box_tag "q[is_tv_eq]", true, class: "checkbox" %> テレビ
                    </div>
                    <div class="col-md-4">
                        <%= check_box_tag "q[is_kitchen_eq]", true, class: "checkbox" %> キッチン
                    </div>
                    <div class="col-md-4">
                        <%= check_box_tag "q[is_Internet_eq]", true, class: "checkbox" %> インターネット
                    </div>
                    <div class="col-md-4">
                        <%= check_box_tag "q[is_heating_eq]", true, class: "checkbox" %> 暖房
                    </div>
                    <div class="col-md-4">
                        <%= check_box_tag "q[is_air_eq]", true, class: "checkbox" %> エアコン
                    </div>
                </div>
                <br/>
                <br/>


                <div class="row text-center">
                    <%= f.submit "検索", class: "button is-danger" %>
                </div>
            <% end %>
        </div>

    </div>
</section>

<!-- お部屋 -->
<div class="row" id="room_listing">
    <%= render partial: "rooms/rooms_list", locals: {rooms: @arrRooms} %>
</div>


<div id="right">
<!-- GOOGLEマップ -->
    <div id="map" style="width: 100%; height: 100%"></div>
    <script src="https://maps.googleapis.com/maps/api/js"></script>
    <script>
        function initialize(rooms) {
            var location = {lat: 43.061771, lng: 141.354451}
            if (rooms.length > 0) {
            location = {lat: rooms[0].latitude, lng: rooms[0].longitude}
            }
            var map = new google.maps.Map(document.getElementById('map'), {
            center: location,
            zoom: 12
            });
            var marker, inforwindow;
            rooms.forEach(function(room) {
            marker = new google.maps.Marker({
                position: {lat: room.latitude, lng: room.longitude},
                map: map
            });
            inforwindow = new google.maps.InfoWindow({
                content: "<div class='map_price'>" + room.price + "円</div>"
            });
            inforwindow.open(map, marker);
            })
        }
        google.maps.event.addDomListener(window, 'load', function() {
            initialize(<%= raw @arrRooms.to_json %>)
        });
    </script>
    <script async defer src="https://maps.googleapis.com/maps/api/js?key=ご自分のAPIキーをここに記述してください&callback=initMap" type="text/javascript"></script>
    </div>
</div>

<script>
  $('#start_date').datepicker({
    dateFormat: 'dd-mm-yy',
    minDate: 0,
    maxDate: '3m',
    onSelect: function(selected) {
      $('#end_date').datepicker("option", "minDate", selected);
      $('#end_date').attr("disabled", false);
    }
  });
  $('#end_date').datepicker({
    dateFormat: 'dd-mm-yy',
    minDate: 0,
    maxDate: '3m',
    onSelect: function(selected) {
      $('#start_date').datepicker("option", "maxDate", selected);
    }
  });

  var open = true;
  
  $('#filter').click(function() {
    if (open) {
      $('#filter').html("More filters <i class='fa fa-chevron-up'></i>")
    } else {
      $('#filter').html("More filters <i class='fa fa-chevron-down'></i>")
    }
    open = !open;
  });

</script>



「app\views\rooms」フォルダに「_rooms_list.html.erb」ファイルを新規作成して下さい。


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

<% @rooms.each do |room| %>

    <% if room.active? %>
        <div class="column is-half">
            <div class="card">
                <div class="card-image">
                    <%= link_to listing_room_path(room) do %>
                        <figure class="image is-4by3">
                            <%= image_tag room_cover(room) %>
                        </figure>
                    <% end %>
                </div>
                <br/>
                <div class="card-content p-t-5 p-b-5">
                    <p class="subtitle is-6 m-b-5"><%= link_to room.listing_name, room_path(room), data: { turbolinks: false} %></p>
                    <%= room.home_type %> | ベッド<%= room.bed_room %><p class="subtitle is-6 m-b-5"><%= room.address %></p>
                    <!--レビュー -->
                    <span class="star-review"><i class="fa fa-star"></i>
                        <%= room.average_rating %>
                        <span class="has-text-primary">(<%= room.reviews.count %>)</span>
                    </span>
                </div>

                <footer class="card-footer">
                    <a class="has-text-danger is-block card-footer-item has-text-right">
                        <span class="small-title">1泊の宿泊価格</span> 
                        <strong><%= number_to_currency(room.price) %></strong>                                            
                    </a>
                </footer>
            </div>
        </div>
    <% end %>
<% end %>



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


app\views\pages\search.js.erb(新規作成したファイル)

$('#room_listing').html('<%= j render partial: "rooms/rooms_list", locals: {rooms: @arrRooms} %>')
initialize(<%= raw @arrRooms.to_json %>)



ブラウザ確認
AJAX検索ができるようになりました。


http://localhost:3000/

検索画面
検索画面



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


[36]ホームページ<< [ホームに戻る] >> [38]jQueryスライダー