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

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

Rails6.1 | 仕事売買アプリ作成 | 46 | ファイル添付コメント

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



45 | 会話表示】 << 【ホーム】 >> 【47 | Full Calendar




購入した(された)仕事に対して、お互いに添付ファイル付きコメントを送信できるようにします。

モデル



コメントモデルを作成します。
コマンド
rails g model Comment content:text user:references order:references


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


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

class CreateComments < ActiveRecord::Migration[6.1]
  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\models\comment.rb」に以下の記述を追加します。


記述追加 app\models\comment.rb(5行目)

has_one_attached :attachment_file



記述追加 app\models\comment.rb

class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :order

  has_one_attached :attachment_file
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],
            attachment_file: comment_params[:attachment_file]    
        )

        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



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


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

 before_action :is_authorised, only: [:show]



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

    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]).order(updated_at: :desc)
    end



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

    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]).order(updated_at: :desc)
    end

    private
    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

    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
end


ルートの設定をします。


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

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



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

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



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'
  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
  post '/users/edit', to: 'users#update'
  post '/offers', to: 'offers#create'
  post '/reviews', to: 'reviews#create'
  post '/search', to: 'pages#search'
  post '/settings/payment', to: 'users#update_payment', as: "update_payment"
  post 'messages', to: 'messages#create'
  post '/comments', to: 'comments#create'

  # 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'}
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end


ビュー



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


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

<div style="border: solid 1px #c0c0c0; margin-bottom: 0.3rem; border-radius: 5px;">
    <ul class="list-group list-group-horizontal">
        <li class="list-group-item bn" style="border:none;">
            <figure class="figure">
            <%= image_tag avatar_url(comment.user), style: "width: 40px;", class: "figure-img img-fluid rounded-pill" %>
                <figcaption class="figure-caption">
                    <div class="font2"><%= comment.user.full_name %></div>
                </figcaption>
            </figure>
        </li>
        <li class="list-group-item bn" style="width: 80%; border:none">
            <span class="font2"><%= comment.content %></span>
            <span class="text-secondary font2"><small><%= time_ago_in_words(comment.created_at) %></small></span>
        </li>
    </ul>
    <ul class="list-group list-group-horizontal">        
        <% if comment.attachment_file.attached? %>
            <li class="list-group-item bn" style="width: 80%; border:none">
                <%= link_to url_for(comment.attachment_file), 
                                class: "btn btn-warning",  
                                download: "Attachment_#{comment.attachment_file.id}" do %>
                    <i class="fas fa-paperclip fa-lg p-r-5"></i><%= comment.attachment_file.filename %>
                <% end %>
            </li>
        <% end %>
    </ul>
</div>



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


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

<div class="container">
    <div class="row">
        <!-- 左側 -->
        <div class="col-md-8">
            <div class="container mt-4">
                <div class="card mb-4">
                    <div class="card-body">
                        <h4 class="font1">
                            コメント
                        </h4>
                        <div class="container mt-4">
                        <%= form_with model: Comment.new do |f| %>
                            <%= f.hidden_field :order_id, value: @order.id %>
                            <%= f.text_area :content, class: "form-control", placeholder: "コメントを入力して下さい", style: "height: 10rem;" %>
                            <div class="mt-4">
                                <%= f.file_field :attachment_file, class: "file-input" %>
                            </div>
                            <div class="card mb-4">
                                <div class="card-body">
                                    <div class="file-name" id="file-name">ファイルが選択されていません</div>
                                </div>
                            </div>
                            <%= f.submit "コメントを送る", class: "btn btn-primary w-100" %>                          
                        <% end %>
                        </div>
                    </div>
                </div>
                <div>
                    <% @comments.each do |c| %>
                        <%= render 'orders/comment', comment: c %>
                    <% end %>
                </div>
            </div>
        </div>

        <!-- 右側 -->
        <div class="col-md-4">
            <div class="card mt-4">
                <%= image_tag gig_cover(@gig), style: "width: 100%;", class: "card-img-top" %> 
                <div class="card-body">
                    <h5 class="font2 bg-light p-2" style="border-radius: 10px;"><%= @order.title %></h5>
                    <div class="badge bg-danger font2"><%= number_to_currency(@order.amount) %></div>
                    <div>フリーランサー: <%= @order.seller_name %></div>
                    <div>オーダーID: <%= @order.id %></div>
                    <div>購入日:<%= I18n.l(@order.created_at, format: :full_date) %></div>  

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

    </div>
</div>

<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\buying_orders.html.erb」ファイルの記述を更新します。


記述更新 app\views\orders\buying_orders.html.erb
31行目に以下の記述を追加しています。

<li class="list-group-item" style="border: none;">
    <%= link_to "コメント", order_detail_path(o), class: "btn btn-primary", data: { turbolinks: false} %>
</li>



記述更新 app\views\orders\buying_orders.html.erb

<div class="container mt-4">
    <div class="card">
        <div class="card-body">

            <h5 class="card-title text-danger h3 font1">依頼内容(クライアント)</h5>
            <% if @orders.blank? %>
                <h5 class="font1">表示できるオーダーはありません。</h5>
            <% end %>

            <% @orders.each do |o| %>
                <div class="card mt-4">
                    <div class="card-body">
                        <% if !o.request.nil? %>
                            <div class="badge bg-info mb-4">リクエスト</div>
                        <% else %>
                            <div class="badge bg-dark mb-4">購入済みの仕事</div>
                         <% end %>  
                        <div class="mt-2">                                  
                            <% if o.inprogress? %>
                                <span class="font1 alert alert-danger text-center">進行中</span>
                                <%= link_to complete_order_path(o), method: :put, data: {confirm: "完了にしてもよろしいですか?"} do %>
                                    <i class="fa fa-thumbs-up fa-lg" style="color: green;"></i><span class="badge bg-success">完了</span>
                                <% end %>
                            <% else %>
                                <span class="font1 alert alert-success text-center">完了</span>
                                <%= render partial: "reviews/form", locals: {o: o} %>
                            <% end %>                 
                        </div>
 
                        <ul class="list-group mt-4">
                            <li class="list-group-item" style="border: none;">
                                <%= link_to "コメント", order_detail_path(o), class: "btn btn-primary", data: { turbolinks: false} %>
                            </li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">依頼日:</span><%= I18n.l(o.created_at, format: :full_date) %>
                            </li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">フリーランサー</span>                            
                                <%= link_to user_path(o.seller), class: "tootip", style: "text-decoration: none;" do %>
                                    <span class="btn btn-light"><%= o.seller_name %></span>
                                <% end %>                                                                     
                            </li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">仕事名:</span>
                                <%= link_to o.title, gig_path(o.gig), class: "btn btn-light" if !o.gig.nil? %>
                                <%= link_to o.title, request_path(o.request), class: "btn btn-light" if !o.request.nil? %>       

                            </li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">期日:</span>   <%= I18n.l(o.due_date) %>                   
                            </li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">価格:</span><%= number_to_currency(o.amount) %>
                            </li>                  
                        </ul>

                    </div>
                </div>

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



ブラウザを確認します。
コメント表示ページが開くか確認してください。
http://localhost:3000/buying_orders

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



コメント表示ページです。
動作確認してください。
アドレスで渡されているパラメーターはオーダーのUUIDです。
http://localhost:3000/orders/84102071-8136-41f1-a79d-26f956d00d16

[:200:alt=モバイルレイアウト]
モバイルレイアウト



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


記述更新 app\views\orders\selling_orders.html.erb
28行目に以下の記述を追加しています。

<li class="list-group-item" style="border: none;">
    <%= link_to "コメント", order_detail_path(o), class: "btn btn-primary", data: { turbolinks: false} %>
</li>


<div class="container mt-4">
    <div class="card">
        <div class="card-body">

            <h5 class="card-title text-danger h3 font1">オーダー内容(フリーランサー)</h5>
            <% if @orders.blank? %>
                <h5 class="font1">表示できるオーダーはありません。</h5>
            <% end %>

            <% @orders.each do |o| %>
                <div class="card mt-4">
                    <div class="card-body">
                        <div class="mt-2">
                            <div>
                                <% if !o.request.nil? %>
                                    <div class="badge bg-info mb-4">リクエスト</div>
                                <% else %>
                                    <div class="badge bg-dark mb-4">購入された仕事</div>
                                <% end %>
                            </div>                                                              
                            <% if o.inprogress? %>
                                <span class="font1 alert alert-danger text-center">進行中</span>
                            <% else %>
                                <span class="font1 alert alert-success text-center">完了</span>
                            <% end %>
                        </div>
                        <ul class="list-group mt-4">
                            <li class="list-group-item" style="border: none;">
                                <%= link_to "コメント", order_detail_path(o), class: "btn btn-primary", data: { turbolinks: false} %>
                            </li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">依頼日:</span><%= I18n.l(o.created_at, format: :full_date) %>
                            </li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">クライアント</span>                            
                                <%= link_to user_path(o.buyer), class: "tootip", style: "text-decoration: none;" do %>
                                    <span class="btn btn-light"><%= o.buyer_name %></span>
                                <% end %>                                                                     
                            </li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">仕事名:</span>
                                <%= link_to o.title, gig_path(o.gig), class: "btn btn-light" if !o.gig.nil? %>
                                <%= link_to o.title, request_path(o.request), class: "btn btn-light" if !o.request.nil? %>   
                            </li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">期日:</span>   <%= I18n.l(o.due_date) %>                   
                            </li>
                            <li class="list-group-item" style="border: none;">
                                <span class="font1">価格:</span><%= number_to_currency(o.amount) %>
                            </li>                  
                        </ul>

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



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


動作を確認してください。

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


45 | 会話表示】 << 【ホーム】 >> 【47 | Full Calendar


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