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

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

Rails6.0 | 仕事売買サイトの構築 | 28 | 仕事のリクエスト

[27]注文の確認 << [ホームに戻る] >> [29]申し込み


買い主が仕事をリクエストできるようにします。


リクエストモデルを作成します。


コマンド
一文です。
rails g model Request description:text title budget:bigint delivery:bigint user:references category:references --no-test-framework


コマンド マイグレーション適用
rails db:migrate


「app\models\request.rb」ファイルの記述を更新します。


記述更新 app\models\request.rb

class Request < ApplicationRecord
  belongs_to :user
  belongs_to :category

  has_one_attached :attachment_file

  validates :title, presence: { message: "空白にはできません" }
  validates :description, presence: { message: "空白にはできません" }
  validates :delivery, numericality: { only_integer: true, message: "数字でなければなりません" }
end



「app\models\category.rb」ファイルに以下の記述を追加します。


記述追加 app\models\category.rb(4行目)

has_many :requests



app\models\category.rb

class Category < ApplicationRecord

    has_many :gigs
    has_many :requests
    
end



「app\models\user.rb」ファイルに以下の記述を追加します。


記述追加 app\models\user.rb(4行目)

has_many :requests



app\models\user.rb

class User < ApplicationRecord

  has_many :gigs
  has_many :requests
  has_many :buying_orders, foreign_key: "buyer_id", class_name: "Order"
  has_many :selling_orders, foreign_key: "seller_id", class_name: "Order"

  has_one_attached :avatar

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, 
         :omniauthable, :confirmable

  validates :full_name, presence: true, length: {maximum: 50}      

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

    if user
      if !user.provider
        user.update(uid: auth.uid, provider: auth.provider, image: auth.info.image)
      end
      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



リクエストコントローラを作成します。


コマンド
一文です。
rails g controller Requests index new create edit update show destroy list --no-test-framework


「app\controllers\requests_controller.rb」ファイルを以下のように編集します。


記述編集 app\controllers\requests_controller.rb

class RequestsController < ApplicationController
  before_action :authenticate_user!
  before_action :set_request, except: [:new, :create, :index, :list]
  before_action :is_authorised, only: [:edit, :update, :destroy]
  before_action :set_categories, only: [:new, :edit, :list]
 

  def index
    @requests = current_user.requests
  end

  def new
    @request = current_user.requests.build
  end

  def create
    @request = current_user.requests.build(request_params)
    if @request.save
      redirect_to requests_path, notice: "保存しました"
    else
      redirect_to request.referrer, flash: {error: @request.errors.full_messages.join(', ')}
    end
  end

  def edit
  end

  def update
    if @request.update(request_params)
      redirect_to requests_path, notice: "保存しました"
    else
      redirect_to request.referrer, flash: {error: @request.errors.full_messages.join(', ')}
    end
  end

  def show
  end

  def destroy
    @request.destroy
    redirect_to requests_path, notice: "削除しました"
  end

  def list
    @category_id = params[:category]

    if @category_id.present?
      @requests = Request.where(category_id: @category_id)
    else
      @requests = Request.all
    end
  end

  private

  def set_categories
    @categories = Category.all
  end

  def set_request
    @request = Request.find(params[:id])
  end

  def is_authorised
    redirect_to root_path, alert: "あなたに権限はありません。" unless current_user.id == @request.user_id
  end

  def request_params
    params.require(:request).permit(:description, :category_id, :delivery, :budget, :attachment_file, :title)
  end
end



ルートの設定をします。
自動で追加されたものはすべて削除します。


記述追加(2ヶ所) config\routes.rb
16行目に「get '/all_requests', to: 'requests#list'」の記述を追加。
30行目に「resources :requests」の記述追加

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: {omniauth_callbacks: 'omniauth_callbacks', registrations: 'registrations'}

  get 'pages/home'
  get '/dashboard', to: 'users#dashboard'
  get '/users/:id', to: 'users#show'
  get '/selling_orders', to: 'orders#selling_orders'
  get '/buying_orders', to: 'orders#buying_orders'
  get '/all_requests', to: 'requests#list'

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

  put '/orders/:id/complete', to: 'orders#complete', as: 'complete_order'

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

  resources :requests

  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end



「app\views\requests」フォルダにある「create.html.erb」「destroy.html.erb」「update.html.erb」の3ファイルは使用しないので削除して大丈夫です。


ビューを編集していきます。


記述編集 app\views\requests\new.html.erb

<section class="section">
    <div class="container">
        <div class="form-container">
            <p class="title is-4 has-text-centered">どんなお仕事をお探しですか?</p>

            <%= form_for @request do |f| %>
                <div class="field">
                    <label class="label">タイトル(*)</label>
                    <%= f.text_field :title, placeholder: "こんなお仕事を探しています", class: "input" %>
                </div>
                <div class="field">
                    <label class="label">内容 (*)</label>
                    <%= f.text_area :description, placeholder: "こんな内容のお仕事を探しています", class: "textarea" %>
                </div>
                <div class="field">
                    <label class="label">カテゴリー選択</label>
                    <div class="select is-fullwidth">
                        <%= f.select(:category_id, options_for_select(@categories.map { |c| [c.name, c.id] })) %>
                    </div>
                </div>
                <div class="field">
                    <label class="label">期日</label>
                    <%= f.number_field :delivery, placeholder: "1-30 日", class: "input" %>
                </div>
                <div class="field">
                    <label class="label">予算(円)</label>
                    <%= f.number_field :budget, placeholder: "最低 500円", class: "input" %>
                </div>
                <div class="field">
                    <label class="label">ファイルのアップロード (オプション)</label>
                    <div class="file is-warning has-name">
                        <label class="file-label">
                            <%= f.file_field :attachment_file, class: "file-input" %>
                            <span class="file-cta"><span class="file-label">ファイルを選択して下さい</span></span>
                            <span class="file-name" id="file-name">何も選択されていません</span>
                        </label>
                    </div>
                </div>
                <br/>
                <div class="field has-text-centered">
                    <%= f.submit 'この内容でリクエストする', class: "button is-primary" %>
                </div>
              
            <% end %>
        </div>
    </div>
</section>

<script>
    $(document).ready(function() {
        var file = $('.file-input');
        file.change(function(e) {
            if (file[0].files.length > 0) {
                var attachment = file[0].files[0];
                $('.file-name').text(attachment.name + " (" + attachment.size + " bytes)");
            }
        })
    })
</script>



ブラウザ確認
http://localhost:3000/requests/new


リクエストの登録ができます。

リクエストの登録
リクエストの登録


記述編集 app\views\requests\edit.html.erb

<section class="section">
    <div class="container">
        <div class="form-container">
            <p class="title is-4 has-text-centered">どんなお仕事をお探しですか?</p>

            <%= form_for @request do |f| %>
                <div class="field">
                    <label class="label">タイトル (*)</label>
                    <%= f.text_field :title, placeholder: "こんなお仕事を探しています", class: "input" %>
                </div>
                <div class="field">
                    <label class="label">内容 (*)</label>
                    <%= f.text_area :description, placeholder: "こんな内容のお仕事を探しています", class: "textarea" %>
                </div>
                <div class="field">
                    <label class="label">カテゴリー選択</label>
                    <div class="select is-fullwidth">
                        <%= f.select(:category_id, options_for_select(@categories.map { |c| [c.name, c.id] }, selected: @request.category_id)) %>
                    </div>
                </div>
                <div class="field">
                    <label class="label">期日</label>
                    <%= f.number_field :delivery, placeholder: "1-30 日", class: "input" %>
                </div>
                <div class="field">
                    <label class="label">予算(円)</label>
                    <%= f.number_field :budget, placeholder: "最低 500円", class: "input" %>
                </div>
                <div class="field">
                    <label class="label">ファイルのアップロード (オプション)</label>
                    <div class="file is-warning has-name">
                        <label class="file-label">
                            <%= f.file_field :attachment_file, class: "file-input" %>
                            <span class="file-cta"><span class="file-label">ファイルを選択して下さい</span></span>
                            <span class="file-name" id="file-name">何も選択されていません</span>
                        </label>
                    </div>
                </div>

                <% if @request.attachment_file.attached? %>
                    <p>
                        <%= link_to url_for(@request.attachment_file), 
                                        class: "tag small is-warning m-t-20",  
                                        download: "Attachment_#{@request.attachment_file.id}" do %>
                            <i class="fas fa-paperclip fa-lg p-r-5"></i> <%= @request.attachment_file.filename %>

                        <% end %>
                    </p>
                <% end %>

                <div class="field has-text-centered">
                    <%= f.submit 'リクエストの内容を更新する', class: "button is-primary" %>
                </div>
              
            <% end %>
        </div>
    </div>
</section>

<script>
    $(document).ready(function() {
        var file = $('.file-input');
        file.change(function(e) {
            if (file[0].files.length > 0) {
                var attachment = file[0].files[0];
                $('.file-name').text(attachment.name + " (" + attachment.size + " bytes)");
            }
        })
    })
</script>



ブラウザ確認
リクエストの編集ができます。
http://localhost:3000/requests/1/edit


記述編集 app\views\requests\index.html.erb

<section class="section">
    <div class="container">
        <p class="title">
            リクエストの確認
            <%= link_to '新しいリクエストを登録する', new_request_path, class: "button is-primary is-pulled-right"%>
        </p>

        <table class="table is-fullwidth">
            <thead class="is-centered">
                <tr>
                    <td>リクエスト日</td>
                    <td>タイトル</td>
                    <td class="has-text-right">申し込み</td>
                    <td class="has-text-right">アクション</td>
                </tr>
            </thead>
            <tbody>
                <% if @requests.blank? %>
                    <tr>
                        <td colspan="4" class="has-text-centered"><h1>表示できるリクエストがありません</h1></td>
                    </tr>
                <% end %>
                <% @requests.each do |r| %>
                    <tr>
                        <td><%= I18n.l(r.created_at, format: :full_date) %></td>
                        <td><%= link_to r.title, request_path(r) %></td>
                        <td class="has-text-right">3</td>
                        <td class="has-text-right">
                            <%= link_to edit_request_path(r), class: "m-r-5" do %>
                                <span class="icon has-text-success">
                                    <i class="far fa-edit fa-lg"></i>
                                </span>
                            <% end %>

                            <%= link_to r, method: :delete, data: {confirm: "削除してよろしいですか?"} do %>
                                <span class="icon has-text-danger">
                                    <i class="far fa-trash-alt fa-lg"></i>
                                </span>
                            <% end %>
                        </td>
                    </tr>
                <% end %>
            </tbody>

        </table>
    
    </div>
</section>



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


リクエストの一覧表示

リクエストの一覧表示
リクエストの一覧表示


fakerを使ってランダムなリクエストを登録します。


「db\seeds.rb」ファイルの前回登録した部分(10行目から33行目まで)をコメントアウトします。
コメントアウトは範囲選択して「Command+K」のあとに「Command+C」で一気にできます。
その後、次の記述を追加してください。


記述追加 db\seeds.rb(34行目)

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(500, 5000),
        delivery: Faker::Number.between(1, 30),
        user_id: random_user.id,
        category_id: category.id
    )
end



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 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.free_email,
#         about: Faker::Quote.matz,
#         password: '123456',
#         from: Faker::Address.country,
#         language: Faker::Nation.language,
#         created_at: Date.today
#     )
#     user.avatar.attach(                
#         io: image = 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(500, 5000),
        delivery: Faker::Number.between(1, 30),
        user_id: random_user.id,
        category_id: category.id
    )
end



コマンド
rails db:seed


Posticoでテーブルを確認します。

ランダムなリクエスト登録
ランダムなリクエスト登録



ナビゲーションバーにリンクを追加します。


記述追加 app\views\shared\_navbar.html.erb
74,81,82行目にリンクを追加しています。

  • 74行目:<%= link_to 'リクエストを見る', all_requests_path, class: "navbar-item" %>
  • 81行目:<%= link_to 'お仕事をリクエストする', new_request_path, class: "navbar-item" %>
  • 82行目:<%= link_to 'リクエストの確認', requests_path, class: "navbar-item" %>
<nav class="navbar is-danger" role="navigation" aria-label="main navigation">
    <div class="navbar-brand">
        <a class="navbar-item" href="/">
            <h1>テストサイトOshigoto</h1>
        </a>
        <a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
        </a>
    </div>

    <div id="navbarBasicExample" class="navbar-menu">
        <div class="navbar-start">
            <div class="navbar-item">
                <div class="field has-addons">
                    <div class="control">
                        <input class="input" type="text" placeholder="どんなお仕事を?">
                    </div>
                    <div class="control">
                        <a class="button is-success">検索</a>
                    </div>
                </div>
            </div>
        </div>
        <div class="navbar-end">
            <a class="navbar-item"></a>
            <a class="navbar-item"></a>
            
            <!-- もしログインしていなかったら-->
            <% if (!user_signed_in?) %>
                <div class="navbar-item">
                    <div class="buttons">
                        <%= link_to  "新規ユーザ登録", new_user_registration_path, class: "button is-primary" %>
                        <%= link_to  "ログイン", new_user_session_path, class: "button is-light" %>
                    </div>
                </div>

            <!-- ログインしていたら -->
            <% else %>
                <div class="navbar-item has-dropdown is-hoverable">

                    <a class="navbar-item" style="margin-right: 50px;">
                        <figure class="image is-48x48 m-r-5">
                            <div style="margin-top: 0.6rem;">
                            <%= image_tag avatar_url(current_user), class: "is-rounded" %>
                            </div>
                        </figure>                                        
                        <%= current_user.full_name %>
                    </a>
                    
                    <div class="navbar-dropdown">
                        <%= link_to 'ダッシュボード', dashboard_path, class: "navbar-item" %>
                        <%= link_to  "ユーザ登録情報編集", edit_user_registration_path, class: "navbar-item" %>
                        <hr class="navbar-divider">
                        <%= link_to  "ログアウト", destroy_user_session_path, method: :delete, class: "navbar-item" %>
                    </div>
                </div>
            <% end %>
        </div>
    </div>
</nav>

<% if (user_signed_in?) && !current_page?(root_path) && !current_page?("/gigs/#{params[:id]}") && !current_page?("/users/#{params[:id]}") %>
    <nav class="navbar has-shadow" style="z-index: 5;">
        <div class="container">
            <div class="navbar">
                <%= link_to 'ダッシュボード', dashboard_path, class: "navbar-item" %>
                <div class="navbar-item has-dropdown is-hoverable">
                    <a class="navbar-link">お仕事を売る人</a>
                    <div class="navbar-dropdown">
                        <%= link_to 'お仕事を登録する', new_gig_path, class: "navbar-item" %>
                        <%= link_to '売れた注文の確認', selling_orders_path, class: "navbar-item" %>
                        <%= link_to 'リクエストを見る', all_requests_path, class: "navbar-item" %>
                    </div>
                </div>
                <div class="navbar-item has-dropdown is-hoverable">
                    <a class="navbar-link">お仕事を買う人</a>
                    <div class="navbar-dropdown">
                        <%= link_to '買った注文の確認', buying_orders_path, class: "navbar-item" %>
                        <%= link_to 'お仕事をリクエストする', new_request_path, class: "navbar-item" %>
                        <%= link_to 'リクエストの確認', requests_path, class: "navbar-item" %>
                    </div>
                </div>
            </div>
        </div>
    </nav>
<% end %>

<script>
$(document).ready(function() {
  // Check for click events on the navbar burger icon
  $(".navbar-burger").click(function() {
      // Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
      $(".navbar-burger").toggleClass("is-active");
      $(".navbar-menu").toggleClass("is-active");
  });
});
</script>



「app\views\requests\list.html.erb」ファイルを以下の内容に編集します。


記述編集 app\views\requests\list.html.erb

<section class="section">
    <div class="container">
        <p class="title">
            全てのリクエスト
        </p>
        <div class="card">
            <div class="card-header">
                <%= form_tag '', method: :get do %>
                    <div class="field p-10">
                        <div class="select">
                            <%= select_tag 'category', content_tag(:option, '全てのカテゴリー', value: "") + 
                                                        options_for_select(@categories.map { |c| [c.name, c.id] }, 
                                                        selected: @category_id),
                                                        onchange: "this.form.submit();" %>
                        </div>
                    </div>
                <% end %>
            </div>
            <div class="card-content">

                <table class="table is-fullwidth">
                    <thead>
                        <tr>
                            <th>リクエスト日</th>
                            <th>買い手</th>
                            <th>リクエスト</th>
                            <th class="has-text-centered">申し込み</th>
                            <th class="has-text-centered">期日</th>
                            <th class="has-text-centered">予算</th>
                        </tr>
                    </thead>
                    <tbody>
                        <% if @requests.blank? %>
                            <tr>
                                <td colspan="6" class="has-text-centered"><h1>表示できるリクエストがありません</h1></td>
                            </tr>
                        <% end %>
                        <% @requests.each do |r| %>
                            <tr>
                                <td><%= I18n.l(r.created_at, format: :full_date) %></td>
                                <td>
                                    <figure class="image is-48x48">
                                        <%= image_tag avatar_url(r.user), class: "is-rounded" %>
                                    </figure>
                                </td>
                                <td>
                                    <%= link_to request_path(r), class: "tootip" do %>
                                        <label for="" class="tooltip" data-tooltip="<%= r.description %>">
                                            <%= r.title.truncate(25, seperator: ' ') %>
                                        </label>
                                    <% end %>
                                </td>
                                <td class="has-text-right">5</td>
                                <td class="has-text-right"><%= r.delivery %></td>
                                <td class="has-text-right"><%= number_to_currency(r.budget) %></td>
                            </tr>
                        <% end %>
                    </tbody>
                </table>

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



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


カテゴリー検索もできます。

全てのリクエスト
全てのリクエスト


「app\views\requests\show.html.erb」ファイルを以下の内容に編集します。


記述編集 app\views\requests\show.html.erb

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

            <!-- 左側 -->
            <div class="column is-two-thirds">
                <div class="columns is-multiline">

                    <!-- タイトル -->
                    <div class="column is-full">
                        <p class="title is-4"><%= @request.title %></p>
                        <article class="media">
                            <div class="media-content">
                                <p>
                                    カテゴリー:<%= @request.category.name %>
                                    <span class="is-pulled-right">リクエスト日: <%= I18n.l(@request.created_at, format: :full_date) %></span>
                                </p>
                                期日: <%= @request.delivery %> 日 | 申し込み: 0 | 予算: <%= number_to_currency(@request.budget) %>
                            </div>
                        </article>
                    </div>

                    <!-- 内容 -->
                    <div class="column is-full">
                        <div class="card">
                            <div class="card-content">
                                <strong>内容</strong>
                                <hr>
                                <%= @request.description %>
                                <% if @request.attachment_file.attached? %>
                                    <p>
                                        <%= link_to url_for(@request.attachment_file), 
                                                class: "tag small is-warning m-t-20",
                                                download: "Attachment_#{@request.attachment_file.id}" do %>

                                            <i class="fas fa-paperclip fa-lg p-r-5"></i><%= @request.attachment_file.filename %>
                                        <% end %>
                                    </p>
                                <% end %>
                            </div>
                        </div>
                    </div>

                    <!-- 買い手について -->
                    <div class="column">
                        <div class="card">
                            <div class="card-content">
                                <strong>買い手について</strong> 
                                <hr/>

                                <div class="content">
                                    <div class="columns">

                                        <!-- 買い手のアバター -->
                                        <div class="column">
                                            <div class="card-content is-horizontal-center is-flex">
                                                <figure class="image is-256x256">
                                                    <%= image_tag avatar_url(@request.user), class: "is-rounded" %>
                                                </figure>
                                            </div>
                                            <div class="content has-text-centered">
                                                <p class="title is-5"><%= @request.user.full_name %></p>
                                                <button class="button is-black is-outlined is-fullwidth">メッセージを送る</button>
                                            </div>
                                        </div>

                                        <!-- 買い手のプロフィール -->
                                        <div class="column f-15">

                                            <article class="media">
                                                <div class="media-content">
                                                    <i class="fas fa-user m-r-5"></i> アカウント登録日
                                                </div>
                                                <div class="media-right">
                                                    <%= I18n.l(@request.user.created_at, format: :full_date) %>
                                                </div>
                                            </article>
                                            <article class="media">
                                                <div class="media-content">
                                                    <i class="fas fa-map-marker-alt m-r-5"></i> 出身地
                                                </div>
                                                <div class="media-right">
                                                    <%= @request.user.from %>
                                                </div>
                                            </article>
                                            <article class="media">
                                                <div class="media-content">
                                                    <%= @request.user.about %>
                                                </div>
                                            </article>
                                            
                                        </div>

                                    </div>
                                </div>

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

            <!-- 右側 -->
            <div class="column">
                
            </div>

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



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


リクエストの詳細
リクエストの詳細


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


[27]注文の確認 << [ホームに戻る] >> [29]申し込み