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

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

Rails6.0 | 仕事売買サイトの構築 | 23 | dropzone

[22]仕事登録 << [ホームに戻る] >> [24]仕事の表示


複数の画像をアップロードできるようにします。


コマンド
yarn add dropzone@5.5.1


「app\assets\stylesheets\application.scss」ファイルに以下の記述を追加します。


記述追加 app\assets\stylesheets\application.scss(23行目)

@import 'dropzone/dist/basic.css';
@import 'dropzone/dist/dropzone.css';



app\assets\stylesheets\application.scss

/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
 * vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
 * files in this directory. Styles in this file should be added after the last require_* statement.
 * It is generally better to create a new file per style scope.
 *
 *= require_tree .
 *= require_self
 */
 
 @import 'bulma';
 @import 'bulma-extensions';

 @import 'noty/lib/noty';
 @import 'noty/lib/themes/sunset';

 @import 'dropzone/dist/basic.css';
 @import 'dropzone/dist/dropzone.css';



「app\javascript\packs\application.js」ファイルに以下の記述を追加します。


記述追加 app\javascript\packs\application.js(12行目)

window.Dropzone = require("dropzone")



app\javascript\packs\application.js

// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")

window.Noty = require("noty")
window.Dropzone = require("dropzone")

$(document).ready(() => {
    $('.toggle').on('click', (e) => {
        e.stopPropagation();
        e.preventDefault();
        $('#' + e.target.getAttribute('aria-controls')).toggleClass('is-hidden');
    })
})

// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)

require("trix")
require("@rails/actiontext")



「app\controllers\gigs_controller.rb」ファイルを以下の手順で編集していきます。


1.86行目からの「upload_photo()」メソッド、「delete_photo()」メソッドの内容を以下の記述に編集します。

  def upload_photo
    @gig.photos.attach(params[:file])
    render json: { success: true }
  end

  def delete_photo
    @image = ActiveStorage::Attachment.find(params[:photo_id])
    @image.purge
    redirect_to edit_gig_path(@gig, step: 4)
  end



2.3行目に「protect_from_forgery except: [:upload_photo]」の記述を追加します。


記述更新 app\controllers\gigs_controller.rb
変更箇所が多いのでコードをコピーしてファイルの内容を置き換えて下さい。

class GigsController < ApplicationController
 
  protect_from_forgery except: [:upload_photo]
  before_action :authenticate_user!, except: [:show]
  before_action :set_gig, except: [:new, :create]
  before_action :is_authorised, only: [:edit, :update, :upload_photo, :delete_photo]
  before_action :set_step, only: [:update, :edit]

  def new
    @gig = current_user.gigs.build
    @categories = Category.all
  end

  def create
    @gig = current_user.gigs.build(gig_params)

    if @gig.save
      @gig.pricings.create(Pricing.pricing_types.values.map{ |x| {pricing_type: x} })
      redirect_to edit_gig_path(@gig), notice: "保存しました"
    else
      redirect_to request.referrer, flash: { error: @gig.errors.full_messages }
    end
  end

  def edit
    @categories = Category.all
  end

  def update

    if @step == 2
      gig_params[:pricings_attributes].each do |index, pricing|
        if @gig.has_single_pricing && pricing[:pricing_type] != Pricing.pricing_types.key(0)
          next;
        else
          if pricing[:title].blank? || pricing[:description].blank? || pricing[:delivery_time].blank? || pricing[:price].blank?
            return redirect_to request.referrer, flash: {error: "価格が無効です"}
          end
        end
      end
    end

    if @step == 3 && gig_params[:description].blank?
      return redirect_to request.referrer, flash: {error: "詳細を空白にすることはできません"}
    end

    if @step == 4 && @gig.photos.blank?
      return redirect_to request.referrer, flash: {error: "写真がありません"}
    end

    if @step == 5
      @gig.pricings.each do |pricing|
        if @gig.has_single_pricing && !pricing.basic?
          next;
        else
          if pricing[:title].blank? || pricing[:description].blank? || pricing[:delivery_time].blank? || pricing[:price].blank?
            return redirect_to edit_gig_path(@gig, step: 2), flash: {error: "価格が無効です"}
          end
        end
      end

      if @gig.description.blank?
        return redirect_to edit_gig_path(@gig, step: 3), flash: {error: "詳細を空白にすることはできません"}
      elsif @gig.photos.blank?
        return redirect_to edit_gig_path(@gig, step: 4), flash: {error: "写真がありません"}
      end
    end

    if @gig.update(gig_params)
      flash[:notice] = "保存しました"
    else
      return redirect_to request.referrer, flash: {error: @gig.errors.full_messages}
    end

    if @step < 5
      redirect_to edit_gig_path(@gig, step: @step + 1)
    else
      redirect_to dashboard_path
    end

  end

  def show
  end

  def upload_photo
    @gig.photos.attach(params[:file])
    render json: { success: true }
  end

  def delete_photo
    @image = ActiveStorage::Attachment.find(params[:photo_id])
    @image.purge
    redirect_to edit_gig_path(@gig, step: 4)
  end

  private

  def set_step
    @step = params[:step].to_i > 0 ? params[:step].to_i : 1
    if @step > 5
      @step = 5
    end
  end

  def set_gig
    @gig = Gig.find(params[:id])
  end

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

  def gig_params
    params.require(:gig).permit(:title, :video, :description, :active, :category_id, :has_single_pricing, 
                                pricings_attributes: [:id, :title, :description, :delivery_time, :price, :pricing_type])
  end
end



ルートの設定を以下の内容に更新します。


記述更新 config\routes.rb(17行目)

  resources :gigs do
    member do
      delete :delete_photo
      post :upload_photo
    end
  end



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'

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

  resources :gigs do
    member do
      delete :delete_photo
      post :upload_photo
    end
  end

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



ブラウザ確認
実際に仕事を登録してみます。
http://localhost:3000/gigs/new


概要を入力して「保存して続ける」ボタンを押します。

概要の登録
概要の登録


シングルプランの時はチェックをつけますが、今回はそのまま「保存して続ける」ボタンを押します。
そのまま保存して続ける
そのまま保存して続ける


項目を入力して「保存して続ける」ボタンを押します。
プランを登録
プランを登録


リッチテキストが使えます。
ファイルも添付できます。
仕事の内容を登録
仕事の内容を登録


写真のアップロードができます。
複数の写真も同時にアップロードできます。
「Youtubeコード」には動画のIDを入れます。
画像をアップロード
画像をアップロード


「公開ボタン」を押して公開します。
公開
公開


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


[22]仕事登録 << [ホームに戻る] >> [24]仕事の表示