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

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

Rails6.0 | 仕事売買サイトの構築 | 49 | メッセージと会話| コメント

[48]メッセージと会話 | リアルタイムメッセージ << [ホームに戻る] >> [50]メッセージと会話| リアルタイムコメント


注文したお仕事に対してコメント出来るようにします。


コマンド
rails g model Comment content:text user:references order:references --no-test-framework


「db\migrate\20200712092809_create_comments.rb」ファイルの記述を更新します。


記述更新 db\migrate\20200712092809_create_comments.rb
コードをコピーしてファイルの内容を置き換えて下さい。

class CreateComments < ActiveRecord::Migration[6.0]
  def change
    create_table :comments do |t|
      t.text :content
      t.references :user, foreign_key: true
      t.references :order, foreign_key: true, type: :uuid

      t.timestamps
    end
  end
end



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


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


1.記述追加 app\controllers\orders_controller.rb(4行目)

 before_action :is_authorised, only: [:show]



2.記述追加 app\controllers\orders_controller.rb(39行目)

    def show
        @order = Order.find(params[:id])
        @gig = @order.gig_id ? Gig.find(@order.gig_id) : nil
        @request = @order.request_id ? Request.find(@order.request_id) : nil
        @comments = Comment.where(order_id: params[:id])
    end



3.記述追加 app\controllers\orders_controller.rb(48行目)

    def is_authorised
        redirect_to dashboard_path, 
            alert: "You don't have permission" unless Order.where("id = ? AND (seller_id = ? OR buyer_id = ?", 
                                                                    params[:id], current_user.id, current_user.id)
    end



記述更新 app\controllers\orders_controller.rb

class OrdersController < ApplicationController

    before_action :authenticate_user!
    before_action :is_authorised, only: [:show]
    
    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)
            if charge(gig, pricing)
               return redirect_to buying_orders_path
            end
        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

    def complete
        @order = Order.find(params[:id])
        if !@order.completed?
            if @order.completed!
                flash[:notice] = "保存しました"
            else
                flash[:notice] = "問題が発生しました"
            end
            redirect_to request.referrer
        end    
    end

    def show
        @order = Order.find(params[:id])
        @gig = @order.gig_id ? Gig.find(@order.gig_id) : nil
        @request = @order.request_id ? Request.find(@order.request_id) : nil
        @comments = Comment.where(order_id: params[:id])
    end

    private

    def is_authorised
        redirect_to dashboard_path, 
            alert: "You don't have permission" unless Order.where("id = ? AND (seller_id = ? OR buyer_id = ?", 
                                                                    params[:id], current_user.id, current_user.id)
    end

    def charge(gig, pricing)
        order = gig.orders.new
        order.title = gig.title
        order.due_date = Date.today() + pricing.delivery_time.days
        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 * 1.1
        amount = pricing.price * 1.1
        
        host_amount = (amount * 0.8).to_i # 売上の80%がホストに入る
        charge = Stripe::Charge.create({
            amount: (amount).to_i,
            customer: current_user.stripe_id,
            source: params[:payment],
            currency: "jpy",
            transfer_data: {
                    amount: host_amount, 
                    destination: gig.user.merchant_id, # ホストのストライプID
                    },
        })
        order.save
        flash[:notice] = "決済が完了しました。。"
        return true
    rescue ActiveRecord::RecordInvalid
        flash[:alert] = "問題が発生しました。"
        return false
    end
    
end



「app\controllers」フォルダに「comments_controller.rb」ファイルを新規作成します。


app\controllers\comments_controller.rb(新規作成したファイル)

class CommentsController < ApplicationController
    before_action :authenticate_user!
    before_action :is_valid_order

    def create
        order = Order.find(comment_params[:order_id])

        if comment_params[:content].blank?
            # 「無効なメッセージ」とアラートを返す
            return render json: {success: false}
        end


        @comment = Comment.new(
            user_id: current_user.id,
            order_id: order.id,
            content: comment_params[:content],
    
        )

        if @comment.save
            redirect_to request.referrer, notice: "コメントを送りました"

        else
            redirect_to request.referrer, alert: "コメントすることができません"
    
        end
    end

    private

    def comment_params
        params.require(:comment).permit(:content, :order_id, :attachment_file)
    end

    def is_valid_order
        redirect_to dashboard_path, alert: "無効です" unless Order.find(comment_params[:order_id]).present?
    end
end



ルートの設定をします。


1.記述追加 config\routes.rb(25行目)

get '/orders/:id', to: 'orders#show', as: "order_detail"



2.記述追加 config\routes.rb(32行目)

post '/comments', to: 'comments#create'



config\routes.rb

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', as: 'users'
  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'
  get 'settings/payment', to: 'users#payment', as: 'settings_payment'
  get 'settings/payout', to: 'users#payout', as: 'settings_payout'
  get '/gigs/:id/checkout/:pricing_type', to: 'gigs#checkout', as: 'checkout'
  get '/conversations', to: 'conversations#list', as: "conversations"
  get '/conversations/:id', to: 'conversations#show', as: "conversation_detail"
  get '/orders/:id', to: 'orders#show', as: "order_detail"

  post '/users/edit', to: 'users#update'
  post '/offers', to: 'offers#create'
  post '/reviews', to: 'reviews#create'
  post '/settings/payment', to: 'users#update_payment', as: "update_payment"
  post 'messages', to: 'messages#create'
  post '/comments', to: 'comments#create'

  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'

  mount ActionCable.server => '/cable'

  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\orders」フォルダに「show.html.erb」ファイルを新規作成します。


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

<section class="section" data-channel-subscribe="order" data-order-id="<%= @order.id %>">
    <div class="container">
        <div class="columns">

            <!-- 左側 -->
            <div class="column is-two-thirds">
                <p class="menu-label">
                    注文に対するコメント
                </p>
                <hr>
                <div id="comment-list">
                   <% @comments.each do |c| %>
                    <%= render 'orders/comment', comment: c %>
                   <% end %>
                </div>
                <hr>

                <article class="media">
                    <figure class="media-left">
                        <p class="image is-64x64">                            
                            <%= image_tag avatar_url(current_user), class: "is-rounded" %>
                        </p>
                    </figure>
                    <div class="media-content">
                        <%= form_with model: Comment.new, id: "new-comment" do |f| %>
                            <%= f.hidden_field :order_id, value: @order.id %>
                            <div class="field">
                                <%= f.text_area :content, class: "textarea", placeholder: "コメントを入力して下さい" %>
                            </div>

                            <div class="field is-pulled-left">
                                <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>

                            <div class="field is-pulled-right">
                                <%= f.submit "コメントを送る", class: "button is-primary" %>                          
                            </div>
                        <% end %>
                    </div>
                </article>
            </div>

            <!-- 右側 -->
            <div class="column">
                <div class="card">
                    <div class="card-image">                        
                        <figure class="image is-3by2">
                            <%= image_tag gig_cover(@gig) if @gig %>
                            <%= image_tag 'icon_default_image.jpg' if @request %>
                        </figure>
                    </div>
                    <div class="card-content">
                        <p>
                            <strong><%= @order.title %></strong>
                            <strong class="title is-4 is-pulled-right has-text-success"><%= number_to_currency(@order.amount) %></strong>
                        </p>
                        <br>
                        <small>
                            <p>売り主: <%= @order.seller_name %></p>
                            <br>
                            <p>注文: <%= @order.id %></p>
                            <br>
                            <p><%= I18n.l(@order.created_at, format: :full_date) %></p>  
                        </small>                    
                    </div>                   
                </div>
            </div>

        </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>



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


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

<article class="media">
    <figure class="media-left">
        <p class="image is-64x64">
            <%= image_tag avatar_url(comment.user), class: "is-rounded" %>
        </p>
    </figure>
    <div class="media-content">
        <div class="content">
            <p>
                <strong><%= comment.user.full_name %></strong>
                <small class="is-pulled-right"><%= time_ago_in_words(comment.created_at) %></small>
                <br>
                <%= comment.content %>
                <br>
            </p>
        </div>

    </div>
</article>



「app\views\orders\buying_orders.html.erb」ファイルの記述を更新します。


記述更新 app\views\orders\buying_orders.html.erb
11行目と32~34行目の記述を変更しています。

<section class="section">
    <div class="container">
        <p class="title">買った注文の確認</p>

        <table class="table is-fullwidth">
            <thead>
                <tr>
                    <th>注文日</th>
                    <th>売り主</th>
                    <th>タイトル</th>
                    <th>コメント</th>
                    <th>期日</th>
                    <th>価格</th>
                    <th>ステータス</th>
                    <th>アクション</th>
                </tr>
            </thead>
            <tbody>
                <% if @orders.blank? %>
                  <tr>
                      <td colspan="7" class="has-text-centered"><h1>表示できる注文がありません。</h1></td>
                  </tr>
                <% end %>
                <% @orders.each do |o| %>
                    <tr>
                        <td><%= I18n.l(o.created_at, format: :full_date) %></td>
                        <td><%= o.seller_name %></td>
                        <td>
                            <%= link_to o.title, gig_path(o.gig) if !o.gig.nil? %>
                            <%= link_to o.title, request_path(o.request) if !o.request.nil? %>                        
                        </td>
                        <td>
                            <%= link_to "コメント", order_detail_path(o), data: { turbolinks: false} %>
                        </td>
                        <td><%= I18n.l(o.due_date) %></td>
                        <td><%= number_to_currency(o.amount) %></td>
                        <td>
                            <span class="tag <%= 'is-warning' if o.inprogress? %> <%= 'is-success' if o.completed? %>">
                                <% if o.inprogress? %>
                                    進行中
                                <% else %>
                                    お仕事完了
                                <% end %>
                            </span>
                        </td>
                        <td>
                            <% if !o.completed? %>
                                <%= link_to 'お仕事完了にする', complete_order_path(o), method: :put, class: "button is-small is-primary #{'is-hidden' if o.completed?}" %>  
                            <% else %>
                                <a class="button is-small is-outlined is-danger toggle-modal" aria-control="<%= o.id %>">レビューする</a>

                                <div class="modal" id="<%= o.id %>">
                                    <div class="modal-background"></div>
                                    <div class="modal-content">
                                        <div class="box">
                                            <%= form_for Review.new do |f| %>
                                                <%= f.hidden_field :order_id, value: o.id %>
                                                <div class="field">
                                                    <div id="star_<%= o.id %>"></div>
                                                </div>
                                                <div class="field">
                                                    <%= f.text_area :review, class: "textarea" %>
                                                </div>
                                                <a class="toggle-modal button is-light" aria-control="<%= o.id %>">キャンセル</a>
                                                <%= f.submit "レビューする", class: "button is-danger" %>
                                            <% end %>
                                        </div>
                                    </div>
                                </div>
                            <% end %>
                            
                        </td>
                    </tr>
                <% end %>
            </tbody>
        </table>

    </div>
</section>

<script>
    $('.toggle-modal').on('click', (e) => {
        e.stopPropagation();
        e.preventDefault();

        var order_id = e.target.getAttribute('aria-control');
        $('#star_' + order_id).raty({
            path: '/assets/raty-js/lib/images',
            scoreName: 'review[stars]',
            score: 1
        });
        $('#star_' + order_id).raty('reload');
        $('#' + order_id).toggleClass('is-active');
    });
</script>



「app\views\orders\selling_orders.html.erb」ファイルの記述を更新します。


記述更新 app\views\orders\selling_orders.html.erb
11行目と31~33行目の記述を変更しています。

<section class="section">
    <div class="container">
        <p class="title">売った注文の確認</p>

        <table class="table is-fullwidth">
            <thead>
                <tr>
                    <th>注文日</th>
                    <th>買い主</th>
                    <th>タイトル</th>
                    <th>コメント</th>
                    <th>期日</th>
                    <th>価格</th>
                    <th>ステータス</th>
                </tr>
            </thead>
            <tbody>
                <% if @orders.blank? %>
                  <tr>
                      <td colspan="6" class="has-text-centered"><h1>表示できる発注はありません。</h1></td>
                  </tr>
                <% end %>
                <% @orders.each do |o| %>
                    <tr>
                        <td><%= I18n.l(o.created_at, format: :full_date) %></td>
                        <td><%= o.buyer_name %></td>
                        <td>
                            <%= link_to o.title, gig_path(o.gig) if !o.gig.nil? %>
                            <%= link_to o.title, request_path(o.request) if !o.request.nil? %>
                        </td>
                        <td>
                            <%= link_to "コメント", order_detail_path(o), data: { turbolinks: false} %>
                        </td>
                        <td><%= I18n.l(o.due_date) %></td>
                        <td><%= number_to_currency(o.amount) %></td>
                        <td>
                            <span class="tag <%= 'is-warning' if o.inprogress? %> <%= 'is-success' if o.completed? %>">
                                <% if o.inprogress? %>
                                    進行中
                                <% else %>
                                    お仕事完了
                                <% end %>
                            </span>
                        </td>
                    </tr>
                <% end %>
            </tbody>
        </table>

    </div>
</section>



ブラウザ確認
http://localhost:3000/selling_orders
http://localhost:3000/buying_orders


コメントのリンクをつけました。

コメントリンク
コメントリンク



お仕事の注文に対してコメントを送ることができます。

コメントを送る
コメントを送る



ファイル添付はまだできません。

コメント成功
コメント成功



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


[48]メッセージと会話 | リアルタイムメッセージ << [ホームに戻る] >> [50]メッセージと会話| リアルタイムコメント