↓↓クリックして頂けると励みになります。
【29 | Application Helper】 << 【ホーム】 >> 【31 | Orderの確認】
クライアントが仕事を注文できるように実装します。
まずはpostgresで「uuid」を使えるように設定します。
コマンド
rails generate migration enable_pgcrypto_extension
作成された「db\migrate\20200526015502_enable_pgcrypto_extension.rb」ファイルを以下のように編集します。
記述編集 db\migrate\20200526015502_enable_pgcrypto_extension.rb
3行目に「enable_extension 'pgcrypto'」の記述を追加しています。
class EnablePgcryptoExtension < ActiveRecord::Migration[6.1] def change enable_extension 'pgcrypto' end end
マイグレーションを適用します。
コマンド
rails db:migrate
Orderモデル作成
注文モデルを作成します。
コマンド
一文です。
rails g model Order due_date:date title amount:float status:bigint seller_name buyer_name gig:references buyer:references seller:references
「db\migrate\20200526020610_create_orders.rb」ファイルを以下のように編集します。
記述編集 db\migrate\20200526020610_create_orders.rb
内容を大幅に変えていますのでコードをコピーしてファイルを置き換えてください。
class CreateOrders < ActiveRecord::Migration[6.1] def change create_table :orders, id: :uuid do |t| t.date :due_date t.string :title t.float :amount t.bigint :status, default: 0 t.string :seller_name t.string :buyer_name t.references :gig, null: true, foreign_key: true t.references :buyer, foreign_key: { to_table: :users } t.references :seller, foreign_key: { to_table: :users } t.timestamps end end end
コマンド マイグレーション適用
rails db:migrate
「app\models\order.rb」ファイルを以下のように編集します。
記述編集 app\models\order.rb
class Order < ApplicationRecord belongs_to :gig, required: false belongs_to :buyer, class_name: "User" belongs_to :seller, class_name: "User" enum status: [:inprogress, :completed] end
「app\models\gig.rb」ファイルに以下の記述を追加します。
記述追加 app\models\gig.rb(6行目)
has_many :orders
app\models\gig.rb
class Gig < ApplicationRecord belongs_to :user belongs_to :category has_many :pricings has_many :orders has_many_attached :photos accepts_nested_attributes_for :pricings validates :title, presence: { message: '空白にはできません' } end
「app\models\user.rb」ファイルに以下の記述を追加します。
記述追加 app\models\user.rb(4行目)
has_many :buying_orders, foreign_key: "buyer_id", class_name: "Order" has_many :selling_orders, foreign_key: "seller_id", class_name: "Order"
app\models\user.rb
class User < ApplicationRecord has_many :gigs 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 validates :full_name, presence: true, length: {maximum: 50} # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :confirmable, :omniauthable def self.from_omniauth(auth) user = User.where(email: auth.info.email).first if user 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
Orderコントローラー
注文コントローラを作成していきます。
「app\controllers」フォルダに「orders_controller.rb」ファイルを新規作成してください。
app\controllers\orders_controller.rb(新規作成したファイル)
class OrdersController < ApplicationController before_action :authenticate_user! def create gig = Gig.find(params[:gig_id]) pricing = gig.pricings.find_by(pricing_type: params[:pricing_type]) if (pricing && !gig.has_single_pricing) || (pricing && pricing.basic? && gig.has_single_pricing) charge(gig, pricing) else flash[:alert] = "価格が間違っています。" end redirect_to request.referrer end def selling_orders @orders = current_user.selling_orders end def buying_orders @orders = current_user.buying_orders end private def charge(gig, pricing) order = gig.orders.new order.due_date = Date.today() + pricing.delivery_time.days order.title = gig.title order.seller_name = gig.user.full_name order.seller_id = gig.user.id order.buyer_name = current_user.full_name order.buyer_id = current_user.id order.amount = pricing.price if order.save flash[:notice] = "発注しました。" else flash[:alert] = order.errors.full_messages.join(', ') end end end
ルート設定
ルートの設定をします。
記述追加 config\routes.rb
22行目に「resources :orders, only: [:create]」の記述を追加しています。
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' post '/users/edit', to: 'users#update' resources :gigs do member do delete :delete_photo post :upload_photo end resources :orders, only: [:create] end # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end
ビュー
「app\views\gigs\show.html.erb」ファイルの記述を更新します。
仕事を依頼するボタンを実装します。
記述更新 app\views\gigs\show.html.erb
18行目と68行目の記述を以下に書き換えます。
<%= form_for([@gig, @gig.orders.new]) do |f| %> <%= hidden_field_tag 'pricing_type', p.pricing_type %> <%= f.submit "仕事を依頼する(#{number_to_currency(p.price)})", class: "btn btn-danger w-100", data: {confirm: "本当によろしいですか?"} %> <% end %>
app\views\gigs\show.html.erb
<div class="container"> <div class="mt-4 mb-4"> <%= render 'shared/categories' %> </div> <div class="row"> <!-- 左側 --> <div class="col-md-4"> <% if @gig.has_single_pricing %> <h5 class="font2 bg-light p-2" style="border-radius: 10px;"><%= @gig.title %></h5> <div class="card"> <div class="card-body"> <% @gig.pricings.each do |p| %> <% if p.title %> <div class="card-title font1">シングルプラン <span class="badge bg-danger"><%= number_to_currency(p.price) %></span></div> <div class="font2"><%= p.description %></div> <div><i class="far fa-clock"></i><span class="font2">期日:<%= p.delivery_time %>日</span></div> <div class="mt-4"> <% if (!user_signed_in? && @gig.active) || (user_signed_in? && @gig.active && @gig.user_id != current_user.id) %> <%= form_for([@gig, @gig.orders.new]) do |f| %> <%= hidden_field_tag 'pricing_type', p.pricing_type %> <%= f.submit "仕事を依頼する(#{number_to_currency(p.price)})", class: "btn btn-danger w-100", data: {confirm: "本当によろしいですか?"} %> <% end %> <% else %> <button class="btn btn-danger" disabled>ご利用できません</button> <% end %> </div> <% end %> <% end %> </div> </div> <% else %> <ul class="nav nav-pills mb-3" id="pills-tab" role="tablist"> <h5 class="font2 bg-light p-2" style="border-radius: 10px;"><%= @gig.title %></h5> <% Pricing.pricing_types.each do |key, value| %> <li class="nav-item" role="presentation"> <button class="nav-link <%= 'active' if value == 0 %>" id="pills-<%= key %>-tab" data-bs-toggle="pill" data-bs-target="#pills-<%= key %>" type="button" role="tab" aria-controls="pills-<%= key %>" aria-selected="<%= 'true' if value == 0 %><%= 'false' if !value == 0 %>"> <% if value == 0 %> ベーシック <% elsif value == 1 %> スタンダード <% else %> プレミアム <% end %> </button> </li> <% end %> </ul> <div class="tab-content" id="pills-tabContent"> <% @gig.pricings.each do |p| %> <div class="tab-pane fade <%= 'show active' if p.pricing_type == 'basic' %>" id="pills-<%= p.pricing_type %>" role="tabpanel" aria-labelledby="pills-<%= p.pricing_type %>-tab" tabindex="0"> <div class="card"> <div class="card-body"> <div class="card-title font1"><%= p.title %> <span class="badge bg-danger"><%= number_to_currency(p.price) %></span></div> <div class="font2"><%= p.description %></div> <div><i class="far fa-clock"></i><span class="font2">期日:<%= p.delivery_time %>日</span></div> <div class="mt-4"> <% if (!user_signed_in? && @gig.active) || (user_signed_in? && @gig.active && @gig.user_id != current_user.id) %> <%= form_for([@gig, @gig.orders.new]) do |f| %> <%= hidden_field_tag 'pricing_type', p.pricing_type %> <%= f.submit "仕事を依頼する(#{number_to_currency(p.price)})", class: "btn btn-danger w-100", data: {confirm: "本当によろしいですか?"} %> <% end %> <% else %> <button class="btn btn-danger" disabled>ご利用できません</button> <% end %> </div> </div> </div> </div> <% end %> </div> <% end %> </div> <!--右側 --> <div class="col-md-8"> <div class="card mt-4 mb-4"> <div class="card-body"> <h5 class="font1">フリーランサー</h5> <div class="mt-2"> <%= 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: 80px;" %> <span class="font2 text-dark h4"><%= @gig.user.full_name %></span> <% end %> </div> <div class="font2"> <%= @gig.summary %> </div> </div> </div> <!-- カルーセル表示 --> <div class="card"> <div class="card-body"> <div id="carouselExampleIndicators" class="carousel slide" data-bs-ride="carousel"> <div class="carousel-indicators"> <% @photos.each do |photo| %> <button type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide-to="<%= @i %>" class="<%= 'active' if photo.id == @photos[0].id %>" aria-current="true" aria-label="Slide <%= @i+1 %>"></button> <% @i = @i +1 %> <% end %> </div> <div class="carousel-inner"> <% @gig.photos.each do |photo| %> <div class="carousel-item <%= 'active' if photo.id == @photos[0].id %>"> <%= image_tag url_for(photo), class: "d-block w-100", style: "border-radius: 10px;" %> </div> <% end %> </div> <button class="carousel-control-prev" type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide="prev"> <span class="carousel-control-prev-icon" aria-hidden="true"></span> <span class="visually-hidden">Previous</span> </button> <button class="carousel-control-next" type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide="next"> <span class="carousel-control-next-icon" aria-hidden="true"></span> <span class="visually-hidden">Next</span> </button> </div> </div> </div> <!-- Youtube表示 --> <% if @gig.video.present? %> <div class="card mt-4"> <iframe height="360" src="https://www.youtube.com/embed/<%= @gig.video %>" allowfullscreen></iframe> </div> <% end %> </div> </div> </div>
ブラウザ確認
http://localhost:3000/gigs/1
お仕事を登録したアカウントと別のアカウントでログインして注文してみます。
もしJavascriptが上手く動かないようでしたら、「app/views/layouts/application.html.erb」ファイルのheadタグ内に以下のCDNの記述を追加してください。
<script src="https://cdn.jsdelivr.net/npm/@rails/ujs@7.1.1/app/assets/javascripts/rails-ujs.min.js"></script>
Posticoでオーダーを確認します。
【29 | Application Helper】 << 【ホーム】 >> 【31 | Orderの確認】
↓↓クリックして頂けると励みになります。