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

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

Rails6.0 | 動画学習サイトを作成| 29 | サブスクリプション

[28]動画(Wistia) << [ホームに戻る] >> [30]マイプロジェクトページ


モデルを作成していきます。


コマンド
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
  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">
                                                                &nbsp;&nbsp;&nbsp;&nbsp;<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

タスクを見ることができない
タスクを見ることができない



プロジェクトを購入してみます。
もちろんまだ決済はされません。

プロジェクトを購入
プロジェクトを購入



表示が「購入済みです」に変わりました。

購入済み
購入済み



サブスクリプションテーブルを確認します。

サブスクリプションテーブル確認
サブスクリプションテーブル確認



これでタスクを見ることができるようになりました。


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


[28]動画(Wistia) << [ホームに戻る] >> [30]マイプロジェクトページ