買い主が仕事をリクエストできるようにします。
リクエストモデルを作成します。
コマンド
一文です。
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