9
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GeekSalonAdvent Calendar 2022

Day 7

Rails Devise 管理者機能 ユーザー作成機能 所属機能 複数モデル

Last updated at Posted at 2022-12-06

デモ

管理者(Admin)が職員(Worker)を作成する
Workerは店舗(Shop)に所属している

環境

$ rails -v
Rails 6.1.7

$ ruby -v
ruby 3.0.2

既にRuby、Railsの環境構築とrails newはしてある前提です。

全てのコードはこちらのリポジトリから

ER図

image.png

Workerにis_adminのようなカラムを付け足して管理者機能を実装することも考えられますが、今回はAdminとWorkerのできることが大きく違うため、別モデルで実装します。
管理者と非管理者の違いがそこまでないような場合はカラムを追加するだけの実装が簡単かもしれません。
以下のような記事参考にしてください。
https://qiita.com/kubochiro/items/340f32c366740a962cb8

deviseの導入

公式はこちら

Gemfile
gem 'devise'
$ bundle install
$ rails generate devise:install

<body>の下に追加

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>AdminWorkerShop</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= 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>
+    <p class="notice"><%= notice %></p>
+    <p class="alert"><%= alert %></p>
    <%= yield %>
  </body>
</html>

ここまでの差分

Adminの作成

$ rails generate devise Admin
$ rails db:migrate

初期Adminをseedで作成

db/seeds.rb
Admin.create!(email: "admin@example.com", password: "p@ssword")
$ rails db:seed

ここまでの差分

Shopの作成

店舗のCRUDを作成

$ rails g scaffold Shop name:string
$ rails db:migrate

rootパスを追加

config/routes.rb
Rails.application.routes.draw do
  resources :shops
  devise_for :admins
+ root 'shops#index'
end

管理者しか店舗を作成できないようにする

app/controllers/shops_controller.rb
class ShopsController < ApplicationController
+ before_action :authenticate_admin!
  before_action :set_shop, only: %i[ show edit update destroy ]

# 以下省略

管理者のログアウトボタンを設置

app/views/shops/index.html.erb
<%= link_to "ログアウト", destroy_admin_session_path, method: :delete %>

ここで、rails sをして、ログイン機能と店舗の作成等がうまく実装されているか確認してみてください!
image.png

image.png

ここまでの差分

Workerの作成

DeviseでWorkerモデルを作成

$ rails generate devise Worker

nameカラムとshop_idカラムを追加

db/migrate/devise_create_workers.rb
class DeviseCreateWorkers < ActiveRecord::Migration[6.1]
  def change
    create_table :workers do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""
+     t.string :name
+     t.integer :shop_id
# 省略

$ rails db:migrate

Workersのログインのビューを作成する

$ rails generate devise:views workers
config/initializers/devise.rb
# 省略
  # ==> Scopes configuration
  # Turn scoped views on. Before rendering "sessions/new", it will first check for
  # "users/sessions/new". It's turned off by default because it's slower if you
  # are using only default views.
- # config.scoped_views = false
+ config.scoped_views = true

  # Configure the default scope given to Warden. By default it's the first
  # devise role declared in your routes (usually :user).
  # config.default_scope = :user
# 省略

Shopの選択と名前の入力をできるようにする

app/views/workers/registrations/new.html.erb
<h2>Sign up</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= render "workers/shared/error_messages", resource: resource %>

+  <div class="field">
+    <%= f.label :name %><br />
+    <%= f.text_field :name %>
+  </div>

+  <div class="field">
+    <%= f.label :shop %><br />
+    <%= f.collection_select :shop_id, Shop.all, :id, :name, include_blank: "選択して下さい" %>
+  </div>

# 省略

ストロングパラメータを設定

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
+  before_action :configure_permitted_parameters, if: :devise_controller?
+
+  protected
+  def configure_permitted_parameters
+    devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :shop_id])
+    devise_parameter_sanitizer.permit(:account_update, keys: [:name, :shop_id])
+  end
end

ここまででrails sしてhttp://localhost:3000/workers/sign_upでWorkerのログインができるか確認する

差分はこちら

Workersを作成

コントローラーの作成

$ rails g controller workers index
app/controllers/workers_controller.rb
class WorkersController < ApplicationController
+ before_action :authenticate_admin!
  def index
+   @workers = Worker.joins(:shop).all
  end
end

routesを設定

app/views/workers/index.html.erb
Rails.application.routes.draw do
+ get 'workers/index'
  devise_for :workers
  resources :shops
  devise_for :admins
+ resources :workers
  root 'shops#index'
end

アソシエーションを設定

app/models/shop.rb
class Shop < ApplicationRecord
+ has_many :workers
end
app/models/worker.rb
class Worker < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
+ belongs_to :shop
end

ビューを作成

app/views/workers/index.html.erb
<h1>Workers#index</h1>
<% @workers.each do |worker| %>
  <div>
    名前:<%= worker.name %>
    所属:<%= worker.shop.name %>
  </div>
<% end %>

ここで、rails sしてみて、http://localhost:3000/workersにアクセスして(Adminのログインが必要な場合もあり)Worker一覧が表示されれば、問題ありません。
image.png

ここまでの差分

Deviseの設定を調整

AdminとWorkerを新規作成をさせないようにする

app/models/admin.rb
class Admin < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
-  devise :database_authenticatable, :registerable,
+  devise :database_authenticatable,
         :recoverable, :rememberable, :validatable
end
app/models/worker.rb
class Worker < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
-  devise :database_authenticatable, :registerable,
+  devise :database_authenticatable,
         :recoverable, :rememberable, :validatable
  belongs_to :shop
end

それぞれのログイン後のリダイレクト先を変更

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

+  def after_sign_in_path_for(resource)
+    case resource
+    when Admin
+      shops_path
+    when Worker
+      worker_path(current_worker)
+    end
+  end

  protected
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :shop_id])
    devise_parameter_sanitizer.permit(:account_update, keys: [:name, :shop_id])
  end
end

workers#showを作成し、Workerの新規登録をAdmin以外ができなくする

app/controllers/workers_controller.rb
class WorkersController < ApplicationController
  def index
    authenticate_admin!
    @workers = Worker.joins(:shop).all
  end

  def new 
    authenticate_admin!
    @worker = Worker.new
  end

  def create 
    authenticate_admin!
    Worker.create!(worker_params)
    redirect_to workers_path
  end

  def show
    authenticate_worker!
    @worker = current_worker
  end

  private
  def worker_params
    params.require(:worker).permit(:name, :password, :email, :shop_id)
  end
end

ここまでの変更

ビューの調整

それぞれのページ(の好きなところ)に追加

app/views/shops/index.html.erb
<%= link_to "Worker一覧", workers_path %>
app/views/shops/show.html.erb
<h4>Worker一覧</h4>
<% @shop.workers.each do |worker| %>
  <%= worker.name %><br>
<% end %>
app/views/workers/index.html.erb
<%= link_to "新規職員", new_worker_path %>
<%= link_to 'ログアウト', destroy_admin_session_path, method: :delete %>
<%= link_to "店舗一覧", shops_path %>
app/views/workers/new.html.erb
<%= form_for @worker do |f| %>
  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>

  <div class="field">
    <%= f.label :shop %><br />
    <%= f.collection_select :shop_id, Shop.all, :id, :name, include_blank: "選択して下さい" %>
  </div>

  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
  </div>

  <div class="field">
    <%= f.label :password %>
    <%= f.password_field :password, autocomplete: "new-password" %>
  </div>

  <div class="actions">
    <%= f.submit "新規登録" %>
  </div>
<% end %>
app/views/workers/show.html.erb
<h1>Workers#Show</h1>
<%= @worker.name %>
<%= @worker.shop %>

<% if current_admin %>
  <%= link_to "職員一覧", wokers_path %>
<% end %>

ここまでの変更

以上で完成になります!

あとがき

Deviseはパッと作るのには向いてるけど、カスタマイズしようとすると、仕様をちゃんと理解していないと大変。

この記事の内容で、エラーや誤植等あれば、遠慮なくご指摘ください!

9
2
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
9
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?