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

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

Rails7.1 | 仕事売買アプリ作成 | 38 | SQLによる検索機能実装

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



37 | ActionText】 << 【ホーム】 >> 【39 | ホームページ



SQLを利用した検索機能を実装していきます。

コントローラー

まずはコントローラーを編集します。


記述編集 【app/controllers/pages_controller.rb】

class PagesController < ApplicationController

  skip_before_action :verify_authenticity_token

  def home
  end
  
  def search
    @categories = Category.all
    @category = Category.find(params[:category]) if params[:category].present?
    @q = params[:q]
    @min = params[:min]
    @max = params[:max]
    @delivery = params[:delivery].present? ? params[:delivery] : "0"
    @sort = params[:sort].present? ? params[:sort] : "price asc"
    query_condition = []
    query_condition[0] = "gigs.active = true"
    query_condition[0] += " AND ((gigs.has_single_pricing = true AND pricings.pricing_type = 0) OR (gigs.has_single_pricing = false))"
    if !@q.blank?
      query_condition[0] += " AND gigs.title ILIKE ?"
      query_condition.push "%#{@q}%"
    end
    if !params[:category].blank?
      query_condition[0] += " AND category_id = ?"
      query_condition.push params[:category]
    end
    if !params[:min].blank?
      query_condition[0] += " AND pricings.price >= ?"
      query_condition.push @min
    end
    if !params[:max].blank?
      query_condition[0] += " AND pricings.price <= ?"
      query_condition.push @max
    end
    if !params[:delivery].blank? && params[:delivery] != "0"
      query_condition[0] += " AND pricings.delivery_time <= ?"
      query_condition.push @delivery
    end
    @gigs = Gig
                .select("gigs.id, gigs.title, gigs.user_id, MIN(pricings.price) AS price")
                .joins(:pricings)
                .where(query_condition)
                .group("gigs.id")
                .order(@sort)
  end
end


ルート設定



ルートを設定します。
「config/routes.rb」ファイルの15行目に以下の記述を追加します。

get '/search', to: 'pages#search'



「config/routes.rb」ファイルの21行目に以下の記述を追加します。

post '/search', to: 'pages#search'



記述追加 【config/routes.rb】

Rails.application.routes.draw do

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

  # get
  get 'pages/home'
  get '/dashboard', to: 'users#dashboard'
  get '/users/:id', to: 'users#show', as: 'user'
  get '/selling_orders', to: 'orders#selling_orders'
  get '/buying_orders', to: 'orders#buying_orders'
  get '/all_requests', to: 'requests#list'
  get '/request_offers/:id', to: 'requests#offers', as: 'request_offers'
  get '/my_offers', to: 'requests#my_offers'
  get '/search', to: 'pages#search'

  # post
  post '/users/edit', to: 'users#update'
  post '/offers', to: 'offers#create'
  post '/reviews', to: 'reviews#create'
  post '/search', to: 'pages#search'

  # put
  put '/orders/:id/complete', to: 'orders#complete', as: 'complete_order'
  put '/offers/:id/accept', to: 'offers#accept', as: 'accept_offer'
  put '/offers/:id/reject', to: 'offers#reject', as: 'reject_offer'

  resources :gigs do
    member do
      delete :delete_photo
      post :upload_photo
    end
    resources :orders, only: [:create]
  end

  resources :requests
  resources :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'}
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
  # Can be used by load balancers and uptime monitors to verify that the app is live.
  get "up" => "rails/health#show", as: :rails_health_check

  # Defines the root path route ("/")
  # root "posts#index"
end


ビュー



「app\views\shared\_categories.html.erb」ファイルの記述を変更します。


記述変更 app\views\shared\_categories.html.erb
7行目の記述を「<%= link_to "#{item.name}", search_path(category: item.id), class: "btn btn-light font2" %>」に変更しています。


記述変更 app\views\shared\_categories.html.erb

<div class="container mt-4 mb-4">
    <div class="card">
        <div class="card-body">
            <h5 class="badge bg-dark">カテゴリー</h5>
            <div>
                <% @categories.each do |item| %>
                    <%= link_to "#{item.name}", search_path(category: item.id), class: "btn btn-light font2" %>
                <% end %>
            </div>
        </div>
    </div>
</div>



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


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

<div class="row">
    <% @gigs.each do |gig| %>
        <div class="col-md-4">
            <div class="card mb-2">
                <%= link_to gig_path(gig), data: { turbolinks: false} do %>
                    <%= image_tag gig_cover(gig), style: "width: 100%;", class: "card-img-top" %>
                <% end %>       
   
                <div class="card-body">
                    <span class="star-review">
                        <i class="fa fa-star text-warning"></i>
                        <%= gig.average_rating %>
                        <span class="has-text-primary">(<%= gig.reviews.count %>)</span>
                    </span>
                    
                    <%= link_to gig_path(gig), data: { turbolinks: false} do %>
                        <h5 class="card-title">
                            <span class="btn btn-light"><%= gig.title %></span>
                        </h5>
                    <% end %>                       

                    <div>
                        <%= link_to user_path(gig.user), style: "text-decoration:none;" do %>
                            <%= image_tag avatar_url(gig.user), class: "bd-placeholder-img figure-img img-fluid rounded-pill", style: "width: 30px;" %>
                            <span class="font2 text-dark"><%= gig.user.full_name %></span>
                        <% end %>
                    </div>    
                    <div class="badge bg-danger mt-2">
                        <span class="font2">最低価格</span> 
                        <strong><%= number_to_currency(gig.price) %></strong>
                    </div>

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



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


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

<%= render 'shared/categories' %>

<div class="container">
    <!-- ヘッダー -->
    <div class="card w-100">
        <div class="card-body">
            <!-- カテゴリー -->
            <div class="mt-2 mb-3">
                    カテゴリー : <% if @category %><%= @category.name %> <% end %>                   
            </div>
            <!-- 並び替え -->
            <div class="font2">
                <%= form_tag '', method: :post, id: "submit_form" do %>
                    <%= select_tag 'sort', options_for_select(
                        [
                            ['価格が低い順', 'price asc'],
                            ['価格が高い順', 'price desc'],
                            ['新着順', 'gigs.created_at desc'],
                            ['古いもの順', 'gigs.created_at asc']
                        ], selected: @sort
                    ), id: "submit_select" %>
                <% end %>                       
            </div>              
        </div>
    </div>
</div>

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


            <div class="card">
                <div class="card-body">
                    <%= form_tag '', method: :get do %>
                        <%= hidden_field_tag 'category', @category ? @category.id : '' %>
                        <%= hidden_field_tag 'sort', @sort %>
                                                        
                        <!-- タイトル検索 -->
                        <div class="mb-2">
                            <%= text_field_tag 'q', @q || '', class: "form-control", placeholder: "タイトルで検索" %>
                        </div>
                        <hr/>
                        <!-- 価格帯 -->
                        <label class="label font1">価格帯</label>
                        <div class="input-group mb-3">
                            <%= number_field_tag 'min', @min || '', class: "form-control", placeholder: "最低" %>
                            <span class="input-group-text"></span>
                        </div>
                        <div class="input-group mb-3">                               
                            <%= number_field_tag 'max', @max || '', class: "form-control", placeholder: "最高" %>                                
                            <span class="input-group-text"></span>
                        </div>
                        <hr/>
                        <!-- 期日 -->
                        <label class="label font1">期日</label>
                        <div>                                    
                            <%= radio_button_tag 'delivery', "1", checked = "1" == @delivery %> 1日以内 <br>
                            <%= radio_button_tag 'delivery', "3", checked = "3" == @delivery %> 3日以内 <br>
                            <%= radio_button_tag 'delivery', "7", checked = "7" == @delivery %> 7日以内 <br>
                            <%= radio_button_tag 'delivery', "10", checked = "10" == @delivery %> 10日以内 <br>
                            <%= radio_button_tag 'delivery', "15", checked = "15" == @delivery %> 15日以内 <br>
                            <%= radio_button_tag 'delivery', "0", checked = "0" == @delivery %> 全て
                        </div>
                        <hr/>
                        
                        <!-- 下部 -->
                        <div class="mt-4">
                            <button  class="btn btn-danger w-100" type="submit"> 検索</button>
                        </div>
                    <% end %>
                    <div class="mt-2">
                        <%= form_tag '', method: :get do %>
                            <button  class="btn btn-outline-dark w-100" type="submit"> 検索条件のクリア</button>
                        <% end %>
                    </div>
                </div>
            </div>
        </div>
        <!-- 右側 -->
        <div class="col-md-8">

        <!-- 仕事のリスト -->
        <div class="container">
            <%= render partial: 'shared/gigs', object: @gigs %>
        </div>

    </div>
</div>

<!-- 並び替え発火 -->
<script type="text/javascript">
    $(function(){
    $("#submit_select").change(function(){
        $("#submit_form").submit();
    });
    });
</script>



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

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


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



fakerでの仕事サンプル登録方法を書いておきます。。


「gig_cover_1.jpg」「gig_cover_2.jpg」「gig_cover_3.jpg」の3つの画像ファイルを「app\assets\images」フォルダに入れておいてください。何でも良いです。
「db\seeds.rb」ファイルの33から44行目をコメントアウトします。
その後、46行目からの記述を追加します。



コメントアウトは範囲選択して「Command+/」で一気にできます。


記述追加 db\seeds.rb

# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
#
# Examples:
#
#   movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
#   Character.create(name: 'Luke', movie: movies.first)

# テストカテゴリーを追加(10個)
# 10.times do
#     Category.create(
#         name: Faker::Job.unique.field
#     )
# end

# # テストユーザーを追加(5ユーザー パスワードは 123456)
# 5.times do
#     user = User.create(
#         full_name: Faker::Name.name,
#         email: Faker::Internet.email,
#         about: Faker::Quote.matz,
#         password: '123456',
#         created_at: Date.today
#     )

#     user.avatar.attach(                
#         io: image = URI.open("https://i.pravatar.cc/300"),
#         filename: "avatar#{user.id}.jpg", 
#         content_type: 'image/jpg'
#     )
# end

# 10.times do
#     random_user = User.all.sample(1)[0]
#     category = Category.all.sample(1)[0]
#     request = Request.create(
#         title: Faker::Job.title,
#         description: Faker::Quote.matz,
#         budget: Faker::Number.between(from: 500, to: 5000),
#         delivery: Faker::Number.between(from: 1, to: 30),
#         user_id: random_user.id,
#         category_id: category.id
#     )
# end

10.times do
    random_user = User.all.sample(1)[0]
    category = Category.all.sample(1)[0]
    gig = Gig.create(
        title: Faker::Job.unique.title,
        description: Faker::Quote.matz,
        active: Faker::Boolean.boolean,
        user_id: random_user.id,
        category_id: category.id
    )
    number = Faker::Number.between(from: 1, to: 3)
    gig.photos.attach(
        io: File.open("app/assets/images/gig_cover_#{number}.jpg"),
        filename: "category_#{number}.jpeg"
    )    
    gig.pricings.create(
        pricing_type: 0,
        title: Faker::Job.title,
        description: Faker::Quote.matz,
        price: Faker::Number.between(from: 500, to: 1500),
        delivery_time: Faker::Number.between(from: 1, to: 10),
    )
    gig.pricings.create(
        pricing_type: 1,
        title: Faker::Job.title,
        description: Faker::Quote.matz,
        price: Faker::Number.between(from: 2000, to: 10000),
        delivery_time: Faker::Number.between(from: 11, to: 19),
    )
    gig.pricings.create(
        pricing_type: 2,
        title: Faker::Job.title,
        description: Faker::Quote.matz,
        price: Faker::Number.between(from: 15000, to: 30000),
        delivery_time: Faker::Number.between(from: 20, to: 30),
    )
end



コマンド
rails db:seed


gigテーブルに10個の仕事が登録されているのが分かります。
Posticoで「active」カラムを全部「true」に変更してください。

activeカラムをtrueに変更
activeカラムをtrueに変更



37 | ActionText】 << 【ホーム】 >> 【39 | ホームページ




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