モデルを作成していきます。
コマンド
rails g model Subscription project:references user:references
「db\migrate\20200804060608_create_subscriptions.rb」ファイルを編集します。
記述追加 db\migrate\20200804060608_create_subscriptions.rb
9行目に「add_index :subscriptions, [:project_id, :user_id], unique: true」の記述を追加しています。
class CreateSubscriptions < ActiveRecord::Migration[6.0] def change create_table :subscriptions do |t| t.references :project, null: false, foreign_key: true t.references :user, null: false, foreign_key: true t.timestamps end add_index :subscriptions, [:project_id, :user_id], unique: true end end
コマンド マイグレーション適用
rails db:migrate
「app\models\project.rb」ファイルに以下の記述を追加します。
記述追加 app\models\project.rb(5,6行目)
has_many :subscriptions has_many :users, through: :subscriptions
app\models\project.rb
class Project < ApplicationRecord class Project < ApplicationRecord belongs_to :user has_many :tasks has_many :subscriptions has_many :users, through: :subscriptions has_rich_text :description has_many_attached :images validates :name, presence: true, length: { maximum: 50 } validates :description, presence: true, length: { maximum: 500 } validates :price, presence: true, numericality: { only_integer: true } end
「app\models\user.rb」ファイルに以下の記述を追加します。
記述追加 app\models\user.rb(4,5行目)
has_many :subscriptions has_many :projects, through: :subscriptions
app\models\user.rb
class User < ApplicationRecord has_many :projects has_many :subscriptions has_many :projects, through: :subscriptions has_one_attached :avatar # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :confirmable, :omniauthable #長さ50文字以下 入力必須 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
コントローラを作成していきます。
「app\controllers」フォルダに「charge_controller.rb」ファイルを新規作成します。
app\controllers\charge_controller.rb(新規作成したファイル)
class ChargeController < ApplicationController before_action :authenticate_user! def free project = Project.find(params[:project_id]) current_user.subscriptions.create(project: project) redirect_to project end end
「app\controllers\project_controller.rb」ファイルの編集をします。
1.記述追加 app\controllers\project_controller.rb(10~16行目)
「show()」メソッドの記述を追加しています。
def show @tasks = @project.tasks.order(:tag) @joined = false if !current_user.nil? && !current_user.projects.nil? @joined = current_user.projects.include?(@project) end @users = @project.users.order('created_at desc').first(10) end
2.記述追加 app\controllers\project_controller.rb(20~24行目)
新たに「list()」メソッドを追加しています。
def list if !current_user.nil? @projects = current_user.projects end end
app\controllers\project_controller.rb
class ProjectController < ApplicationController before_action :set_project, only: [:show] def index @projects = Project.all end def show @tasks = @project.tasks.order(:tag) @joined = false if !current_user.nil? && !current_user.projects.nil? @joined = current_user.projects.include?(@project) end @users = @project.users.order('created_at desc').first(10) end def list if !current_user.nil? @projects = current_user.projects end end private # コールバックを使用して、アクション間で共通のセットアップまたは制約を共有します。 def set_project @project = Project.find(params[:id]) end # 信頼できるパラメータのリストのみを許可します。 def project_params params.require(:project).permit(:name, :content, :price, :active, :description) end end
「app\controllers\task_controller.rb」ファイルを編集します。
記述変更 app\controllers\task_controller.rb
記述を大幅に変更しているのでコードをコピーしてファイルの内容を置き換えて下さい。
class TaskController < ApplicationController before_action :authenticate_user! def show project = Project.find(params[:project_id]) @tasks = project.tasks.order(:tag) joined = false if !current_user.nil? && !current_user.projects.nil? joined = current_user.projects.include?(project) end if joined @task = @tasks.find(params[:id]) @next_task = @task.next @prev_task = @task.prev else flash[:notice] = "プロジェクトを購入して下さい。" redirect_to project end end private # 信頼できるパラメータのリストのみを許可します。 def task_params params.require(:task).permit(:title, :note, :video, :header, :tag, :project_id, :active, :description) end end
ルートを設定します。
記述追加 config\routes.rb
16行目に「post '/free' => 'charge#free'」の記述を追加しています。
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' post '/users/edit', to: 'users#update' post '/free' => 'charge#free' resources :project do resources :task, only: [:show] end # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end
ビューを編集します。
「app\views\project\show.html.erb」ファイルを編集します。
記述変更 app\views\project\show.html.erb(111行目)
<!-- サブスクリプション --> <% if !@joined %> <div class="center"> <h1 class="tag is-success is-light is-large"><%= @project.price == 0? "無料" : "有料" %></h1> </div> <br/> <div class="card-action center"> <%= form_tag free_path do %> <%= hidden_field_tag 'project_id', @project.id %> <button type="submit" class="button is-fullwidth is-danger">プロジェクトを購入する(<%= number_to_currency(@project.price) %>)</button> <% end %> </div> <% else %> <div class="card-content center"> <span class="tag is-success is-light is-large">購入済みです</span> </div> <div class="card-content center"> <div class="card-content is-horizontal-center is-flex"> <figure class="image is-256x256"> <%= image_tag avatar_url(current_user), class: "is-rounded" %><br/> </figure> </div> <h5><%= current_user.full_name %></h5> </div> <% end %>
app\views\project\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"> <div class="card"> <div class="card-content"> <div class="content"> <h1 class="title"><%= @project.name %></h1> <div class="title is-4 has-text-right" style="color: black;"><%= number_to_currency(@project.price) %></div> </div> <hr> <!-- 画像カルーセル表示 --> <div class="hero-carousel" id="carousel-photo"> <% @project.images.each do |photo| %> <div class="carousel-item has-background image is-16by9"> <%= image_tag url_for(photo), class: "is-background", width: "100%" %> </div> <% end %> </div> </div> </div> </div> <div class="column"> <div class="card"> <div class="card-content"> <!-- コンテンツ --> <div class="box"> <article class="media"> <div class="media-content"> <div class="content"> <h2 class="subtitle">このプランについて</h2> <hr> <%= @project.description %> </div> </div> </article> </div> <!-- タスク Section --> <div class="box"> <article class="media"> <div class="media-content"> <div class="content"> <div class="collection"> <% @tasks.each do |task| %> <% if task.header %> <br/> <div class="collection-item active"> <span class="tag is-link is-large"> <%= task.title %></span> <span class="tag is-white is-large"> <%= task.description %></span> </div> <br/> <% else %> <div class="collection-item active"> <span class="tag is-light is-medium" style="margin: 3px;"> <%= link_to [task.project, task], class: "collection-item", data: { turbolinks: false} do %> <%= task.title %> <% end %> </span> </div> <% end %> <% end %> </div> </div> </div> </article> </div> </div> </div> </div> </div> </div> <!-- 右側 --> <div class="column"> <div class="columns is-multiline"> <!-- 購入 --> <div class="column is-full"> <div class="card"> <div class="card-content"> <div class="media"> <div class="media-content"> <strong><%= @project.name %></strong> </div> <div class="media-right"> <p class="title is-4" style="color: black;"><%= number_to_currency(@project.price) %></p> </div> </div> <div class="card-content p-t-5 p-b-5"> <%= link_to users_path(@project.user.id), class: "tootip" do %> <figure class="image is-48x48"> <%= image_tag avatar_url(@project.user), class: "is-rounded" %> <span class="tag is-light"><%= @project.user.full_name %></span> </figure> <% end %> </div> <div class="content f-15"> <p class="m-t-30"><%= @project.content %></p> <p class="m-t-30"> <strong>タスク数: <i class="far fa-clock"></i> <%= @tasks.count %></strong> </p> </div> <% if user_signed_in? %> <!-- サブスクリプション --> <% if !@joined %> <div class="center"> <h1 class="tag is-success is-light is-large"><%= @project.price == 0? "無料" : "有料" %></h1> </div> <br/> <div class="card-action center"> <%= form_tag free_path do %> <%= hidden_field_tag 'project_id', @project.id %> <button type="submit" class="button is-fullwidth is-danger">プロジェクトを購入する(<%= number_to_currency(@project.price) %>)</button> <% end %> </div> <% else %> <div class="card-content center"> <span class="tag is-success is-light is-large">購入済みです</span> </div> <div class="card-content center"> <div class="card-content is-horizontal-center is-flex"> <figure class="image is-256x256"> <%= image_tag avatar_url(current_user), class: "is-rounded" %><br/> </figure> </div> <h5><%= current_user.full_name %></h5> </div> <% end %> <% else %> <button class="button is-fullwidth is-danger" disabled>ログインして下さい</button> <% end %> </div> </div> </div> </div> </div> </div> </div> </section> <script> BulmaCarousel.attach('#carousel-photo', { slidesToScroll: 1, slidesToShow: 1 }); $(document).ready(function() { $('#tabs li').on('click', function() { var type = $(this).data('tab'); $('#tabs li').removeClass('is-active'); $(this).addClass('is-active'); $('.tab-content').hide(); $('#tab-' + type).show(); }) }) </script>
ブラウザ確認
http://localhost:3000/project
プロジェクトを購入してみます。
もちろんまだ決済はされません。
表示が「購入済みです」に変わりました。
サブスクリプションテーブルを確認します。
これでタスクを見ることができるようになりました。