LoginSignup
1
0

More than 1 year has passed since last update.

[Rails] deviseで2種類のユーザーアカウントを作成する②

Last updated at Posted at 2021-06-01

はじめに

この記事は、前回の「[Rails] deviseで2種類のユーザーアカウントを作成する①」の続きです。
deviseによるログイン機能とそのビューとコントローラーを作成し、実際にブラウザ上でdeviseのビューが確認できたところまで完了しているものとして始めていきます。

それぞれ最低限のカラムしか持たせていません。

2種類のユーザーアカウントから派生したそれぞれのトップページ作成までをゴールとします。

またteacherにだけ投稿機能を持たせます。

先生用Textbookモデルを作成

teacherはtextbookを投稿できるようにします。
まずは先生用のTextbookモデルを作成します。

ターミナル
% rails g model textbook name:string

今回はnameカラムだけ追加しました。。必要があればマイグレーションする前にカラムを追加しましょう。

ターミナル
% rails db:migrate

先生用textbooksコントローラーとビューを作成

コントローラー

ターミナル
% rails g scaffold_controller teachers/textbook --skip-test-framework --skip-assets

teachers/textbookの記述でteachersの配下になります。

ルーティング修正

namespaceを使ってパスを整えます。

config/routes.rb
Rails.application.routes.draw do
  namespace :teachers do
    resources :textbooks
  end

  devise_for :students, controllers: {
    sessions:      'students/sessions',
    passwords:     'students/passwords',
    registrations: 'students/registrations'
  }
  devise_for :teachers, controllers: {
    sessions:      'teachers/sessions',
    passwords:     'teachers/passwords',
    registrations: 'teachers/registrations'
  }
  root to: 'home#index'
end

先生用レイアウトを追加

先生としてログインした人だけが使うレイアウトを追加します。

'app/views/layouts/teachers/application.html.erb'というファイルを作成します。
そしてapp/views/layouts/application.html.erbの中身をそのままコピペしてください。

app/views/layouts/teachers/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>SampleApp</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

さらに下記の記述を加えます。

app/views/layouts/teachers/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>SampleApp</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <% if teacher_signed_in? %>
      <%= "お疲れ様です! #{current_teacher.email} 先生"%>
      <%= link_to 'SignOut', destroy_teacher_session_path, method: :delete %>
    <% else %>
      <%= link_to 'SignUp', new_teacher_registration_path %>
      <%= link_to 'Sigin', new_teacher_session_path %>
    <% end %>
    <%= yield %>
  </body>
</html>

先生用のapplication_controllerを追加

application_controllerはすべてのcontrollerの元となっている親みたいなものです。
ですからapplication_controllerの記述はその配下のすべてのcontrollerに反映されます。

ここではその仕組みを利用したいものの、teachers配下とstudents配下の区別はしたいです。
だったらteachersとstudentsの中にそれぞれのapplication_controllerを作成し、それをそれぞれの継承元とするのです。

私の中のイメージはこんな感じです。
間違ってたら教えて欲しいです

これが通常なんですが↓
スクリーンショット 2021-06-01 14.20.01.png

こんな感じにするイメージ↓

スクリーンショット 2021-06-01 14.40.56.png

早速teachersのapplication_controller.rbを作成します。

ターミナル
% rails g controller teachers/application_controller

下記の記述を加えます

app/controllers/teachers/application_controller.rb
class Teachers::ApplicationController < ApplicationController
  layout 'teacher/application'
  before_action :authenticate_teacher!
end

上記の記述はapp/controllers/teachers 配下に作成されているコントローラーはすべて
app/views/layouts/teachers/application.html.erbをレイアウトに使うようにし、teacherで認証されるようにしています。

先生用textbooksコントローラーを修正

先生用textbooksコントローラーの一行目を下記のように書き換えます。

app/controllers/teachers/textbook_controller.rb
class Teachers::TextbookController < Teachers::ApplicationController
end

これで先生用のtextbooksコントローラーはapp/controllers/teachers/application_controllerを継承します。

先生用textbookコントローラーの中身はrails g scaffold_controller コマンドで作成したので自動でいろいろな記述をしてくれていますが、いくつか修正が必要です。

モデルを記述している箇所が
Teachers::Textbookのようになっているので下記のように直しましょう。

app/controllers/teachers/textbooks_controller.rb
# 例
  def index
    @teachers_textbooks = Textbook.all
  end

続いて、createアクションとupdateアクションのif文の中のredirect_toの後を変更

app/controllers/teachers/textbooks_controller.rb
# createアクションの場合
def create
    @teachers_textbook = Textbook.new(teachers_textbook_params)

    respond_to do |format|
      if @teachers_textbook.save
        format.html { redirect_to [:teachers, @teachers_textbook], notice: "Textbook was successfully created." }
        format.json { render :show, status: :created, location: @teachers_textbook }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @teachers_textbook.errors, status: :unprocessable_entity }
      end
    end
  end

そしてストロングパラメーターも変えます。デフォルトでfetchという記述が書いてありますが、使い方がイマイチよく理解できていないため、私の書き慣れた方法で書き直します。

app/controllers/teachers/textbooks_controller.rb
# 中略
  def teachers_textbook_params
    params.require(:textbook).permit(:name)
  end

先生用textbookのビューを修正

自動で作成されているビューの記述を変更する。

app/views/teachers/textbooks/index.html.erb(変更前)
<p id="notice"><%= notice %></p>

<h1>Teachers Textbooks</h1>

<table>
  <thead>
    <tr>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @teachers_textbooks.each do |teachers_textbook| %>
      <tr>
        <td><%= link_to 'Show', teachers_textbook %></td>
        <td><%= link_to 'Edit', edit_teachers_textbook_path(teachers_textbook) %></td>
        <td><%= link_to 'Destroy', teachers_textbook, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Teachers Textbook', new_teachers_textbook_path %>
app/views/teachers/textbooks/index.html.erb(変更後)
<p id="notice"><%= notice %></p>

<h1>Teachers Textbooks</h1>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @teachers_textbooks.each do |teachers_textbook| %>
      <tr>
        <td><%= teachers_textbook.name %></td>
        <td><%= link_to 'Show', [:teachers, teachers_textbook] %></td>
        <td><%= link_to 'Edit', edit_teachers_textbook_path(teachers_textbook) %></td>
        <td><%= link_to 'Destroy', [:teachers, teachers_textbook], method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Teachers Textbook', new_teachers_textbook_path %>

続いて、app/views/teachers/_form.html.erbの一行目のform_withの内容を修正し、:nameの入力フォームも作成しましょう。

app/views/teachers/_form.html.erb
<%= form_with(model: [:teachers, @teachers_textbook], local: true) do |form| %>
  <% if teachers_textbook.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(teachers_textbook.errors.count, "error") %> prohibited this teachers_textbook from being saved:</h2>

      <ul>
        <% teachers_textbook.errors.full_messages.each do |message| %>
          <li><%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

    <%= form.label :name, "名前" %>
    <%= form.text_field :name %>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

localhost:3000/teachers/foodsにアクセスし、localhost:3000/teachers/sign_inにリダイレクトされればOKです。

生徒用のTextbooksコントローラーとビュー作成

ほとんど、先生用で行ったことをstudentで置き換えるだけです。
同じように、studentsのtextbooksコントローラーを作成します。

ターミナル
% rails g scaffold_controller students/textbook --skip-test-framework --skip-assets

ルーティングでnamespaceを使ってtextbooksをstudentsの配下にも置いてあげます。

config/routes.rb
Rails.application.routes.draw do
  namespace :teachers do
    resources :textbooks
  end

  namespace :students do
    resources :textbooks
  end

  devise_for :students, controllers: {
    sessions:      'students/sessions',
    passwords:     'students/passwords',
    registrations: 'students/registrations'
  }
  devise_for :teachers, controllers: {
    sessions:      'teachers/sessions',
    passwords:     'teachers/passwords',
    registrations: 'teachers/registrations'
  }
  root to: 'home#index'
end

次にteacher同様、student専用のレイアウトを作ります。app/views/layouts/students/application.html.erbを作成し、下記の記述をします。

app/views/layouts/students/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>SampleApp</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <% if student_signed_in? %>
      <%= "お疲れ様です! #{current_student.email} さん"%>
      <%= link_to 'SignOut', destroy_student_session_path, method: :delete %>
    <% else %>
      <%= link_to 'SignUp', new_student_registration_path %>
      <%= link_to 'Signin', new_student_session_path %>
    <% end %>
    <%= yield %>
  </body>
</html>

studentsのapplication_controllerを作成します。

ターミナル
 % rails g controller students/application_controller

そして、students配下のコントローラーはapp/views/layouts/students/application.html.erbをレイアウトに使う記述とstudentじゃなかったら弾く記述を書きます。

app/controllers/students/application_controller.rb
class Students::ApplicationController < ApplicationController
  layout 'students/application'
  before_action :autenthicate_student!
end

studentsのtextbookのコントローラーの参照する親のコントローラーをstudentsのapplication_controllerに変更します。(1行目)
モデルの記述を修正します。
studentsは投稿機能は使えないのでindexとshowだけにします。

app/controllers/students/textbooks_controller.rb
class Students::TextbooksController < Students::ApplicationController
  before_action :set_students_textbook, only: [:show]

  def index
    @students_textbooks = Textbook.all
  end

  def show
  end

  private

    def set_students_textbook
      @students_textbook = Textbook.find(params[:id])
    end
end

生徒用textbookのビューを修正

先生の同じように修正するが、show以外のリンクは必要ないので削除します。
また、Showのリンクのパスも変更しています。

app/views/students/textbooks/index.html.erb
<p id="notice"><%= notice %></p>

<h1>Students Textbooks</h1>

<table>
  <thead>
    <tr>
      <th> Name </th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @students_textbooks.each do |students_textbook| %>
      <tr>
        <td><%= students_textbook.name %></td>
        <td><%= link_to 'Show', [:students, students_textbook] %></td>
      </tr>
    <% end %>
  </tbody>
</table>

scaffold_controllerのコマンドによって自動生成された以下のファイルは生徒の方では不要なので削除する。

・app/view/students/textbook/edit.html.erb
・app/view/students/textbook/new.html.erb
・app/view/students/textbook/_form.html.erb

ここまでできたらlocalhost:3000/students/textbooksをブラウザで確認
localhost:3000/students/sign_inへリダイレクトされれば成功です。

Home#indexから分岐させる

Homeのindexが現在rootに設定されています。このページから生徒と先生を分岐させるため、それぞれのログインページへ遷移できるようにリンクを作ります。

app/views/home/index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

<p><%= link_to '先生はこちら', teachers_textbooks_path %></p>
<p><%= link_to '生徒はこちら', students_textbooks_path %></p>

はじめはsign upで新規登録しましょう。
ここで先生側の新規登録方法ですが、コンソールから行います。

ターミナル
% rails c

irb(main):001:0> Teacher.new({email:"example@example.com",password:"*******"}).save

先生の方でログインしいくつか適当に投稿するとこんな感じになります。
スクリーンショット 2021-06-01 20.05.58(2).png

また、生徒側から見るとこんな感じです
スクリーンショット 2021-06-01 20.19.47(2).png

以上で完了です。

最後に

2種類のユーザーを作ることで、それぞれの見せ方を変えたり、片方だけに何かの機能を付与したり、いろいろ想像が膨らみます。

改めてこうして文字に起こして説明していると自分でも結構理解せずにたまたまうまくいっていたりした箇所もいくつかあったので、これからも新しく学んだことは、文字に起こして整理して、自分が本当に理解できているのか確認しながら自分のものにしていきたいと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0