Sejak memasang "dark" theme, saya cenderung menjadi malas menulis. Untuk sementara, dark theme saya disable dulu yaa. Terima kasih (^_^) (bandithijo, 2024/09/15) ●
Prerequisite
Ruby 2.6.3
Rails 5.2.4
PostgreSQL 11.5
Prakata
Apa itu Single Table Inheritance?
Dapat didefinisikan sebagai tabel induk yang mewariskan sifat-sifatnya pada tabel anakan yang berelasi dengannya.
Ahahaha (^_^) definisi macam apa itu.
Abaikan.
Pada saat mengimplementasikan Single Table Inheritance (STI), saya menemukan lebih dari satu cara pada Rails. Maka dari itu, tulisan ini akan saya bagi dalam beberapa contoh.
Catatan kali ini adalah contoh kedua.
Teman-teman dapat melihat contoh pertama di sini
Kira-kira seperti ini ERD-nya.
Dari diagram di atas, saya hanya membuat satu buah tabel, yaitu users.
Tabel users ini hasil generate dari Devise.
Yak! Pada catatan kali ini, saya akan mencatat bagaimana membuat Single Table Inheritance (STI) dengan Devise.
Sekenario
Saya berencana membuat sebuah website untuk registrasi sebuah event.
Dimana user yang mendaftar akan langsung diarahkan untuk mendaftar sebagai participant atau menjadi sponsor.
Menurut saya, ini adalah contoh yang pas untuk memnggunakan STI.
Dimana model user akan menurunkan sifatnya pada model participant dan sponsor.
Pemecahan Masalah
Saya akan memulai dari pemasangan Devise
Migrations
Saya akan membuat tabel users dengan menggunakan generator yang dimiliki oleh Devise.
$ rails g devise user
Generator di atas juga akan sekalian membuatkan kita model user.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class DeviseCreateUsers < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
# ...
# ...
t.timestamps null: false
end
# ...
# ...
end
end
Nah, sebenarnya bisa saya modifikasi migration ini, dengan menambahkan field :name
dan :type
.
Namun, saya memilih untuk membuat migration untuk memanbahkan field name dan type secara satu persatu.
Tidak ada alasan, hanya untuk berlatih saja membuat migration.
$ rails g migration add_name_and_name_to_users
Kemudian, tambahkan manual add_column
untuk :name
dan :type
.
1
2
3
4
5
6
class AddNameAndTypeToUsers < ActiveRecord::Migration[5.2]
def change
add_column :users, :name, :string
add_column :users, :type, :string
end
end
Setelah itu, jalankan semua migration di atas.
$ rails db:migrate
Models
Kemudian buat dua model untuk participant dan sponsor.
Kita dapat memanfaatkan rails generator untuk membuat kedua model (participant dan sponsor) dengan turunan dari model user.
$ rails g model participant --parent=User
$ rails g model sponsor --parent=User
Karena saya sudah menambahkan field :type
pada tabel users, maka kedua model ini yang akan mendapat turunan sifat dari model user dengan mengambil dari nama class dari masing-masing model.
Kalau kita buka, kedua model tersebut, class dari kedua model tersebut sudah merupakan turunan dari model user.
1
2
class Participant < User
end
1
2
class Sponsor < User
end
Routes
Kemudian, pada routes untuk :users
juga ganti menjadi dua buah untuk masing-masing model.
1
2
3
4
5
Rails.application.routes.draw do
...
devise_for :participants
devise_for :sponsors
end
Views & Controller
Untuk views template, saya perlu membuat homepage dan halaman dashboard ketika user sudah melakukan sign up dan sign in.
Halaman authentikasi seperti sign up dan sign in, bisa di generate dari Devise.
Oke, langsung saya mulai dari membuat Homepage.
Saya akan mulai dengan mengenerate page controller dengan action index.
$ rails g controller Pages index
1
2
3
class PagesController < ApplicationController
def index; end
end
Kemudian, pada routes definisikan pages menjadi root.
Letakkan di paling atas.
1
2
3
4
5
6
Rails.application.routes.draw do
root to: 'pages#index'
devise_for :participants
devise_for :sponsors
end
Selanjutnya buat dashboards controller untuk tempat participant dan sponsor mendarat setelah sign up atau sign in.
$ rails g controller dashboard/dashboard index
1
2
3
4
5
class Dashboard::DashboardController < ApplicationController
before_action :authenticate_user!
def index; end
end
Nah, saya akan mendifinisikan apabila user mengakses halaman /dashboard
, maka, user akan langsung diarahkan sebagai participant terlebih dahulu.
1
2
3
class ApplicationController < ActionController::Base
devise_group :user, contains: [:participant, :sponsor]
end
Urutan di dalam array menjadi penentu, scope mana yang akan diprioritaskan apabila user mengakses halaman /dashboard
apabaila belum melakukan sing up atau sign in.
Kemudian atur routenya.
1
2
3
4
5
6
7
8
9
10
Rails.application.routes.draw do
root to: 'pages#index'
devise_for :participants
devise_for :sponsors
namespace :dashboard do
root to: 'dashboard#index'
end
end
Selanjutnya untuk view dari Devise.
Nah, ada banyak cara yang dapat dilakukan. Mulai dari mengenerate view untuk masing-masing scope, atau mengcopy paste saja view template hasil view generate.
Saya memilih untuk mengenerate kedua scope.
$ rails g devise:views participant
$ rails g devise:views sponsor
Lalu mengedit bagian registrations new dan edit untuk menambahkan field :name
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<h2>Participant Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name, autofocus: true %>
</div>
...
...
<% end %>
<%= render "devise/shared/links" %>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<h2>Sponsors Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name, autofocus: true %>
</div>
...
...
<% end %>
<%= render "devise/shared/links" %>
Selanjutnya, tinggal memberikan ijin (permit) kepada :name
saat melakukan sign up.
Saya akan tambahkan saja pada application controller.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class ApplicationController < ActionController::Base
devise_group :user, contains: [:participant, :sponsor]
before_action :configure_permitted_parameters, if: :devise_controller?
private
# If you have extra params to permit, append them to the sanitizer.
def configure_permitted_parameters
added_attrs = [:email, :password, :password_confirmation, :remember_me, :name]
devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
devise_parameter_sanitizer.permit :account_update, keys: added_attrs
end
# The path used after sign up.
def after_sign_up_path_for(resource_or_scope)
resource_or_scope.is_a?(User) ? dashboard_root_path : root_path
end
# The path used after sign in.
def after_sign_in_path_for(resource_or_scope)
resource_or_scope.is_a?(User) ? dashboard_root_path : root_path
end
end
Saya juga menambahkan redirect path apabila user sudah melakukan sign up atau sign in untuk diarahkan ke halaman dashboard.
Nah selesai!
Sepertinya segini saja.
Untuk contoh proyeknya dapat dilihat di repo github di sini.
Mudah-mudahan catatan yang sederhana ini dapat bermanfaat bagi teman-teman yang memerlukan.
Terima kasih.
(^_^)
Tambahan
Referensi
-
vsmedia.co.uk/single-table-inheritance-sti-devise/
Diakses tanggal: 2020/02/21 -
guides.rubyonrails.org/routing.html#controller-namespaces-and-routing
Diakses tanggal: 2020/02/21 -
api.rubyonrails.org/classes/ActiveRecord/Inheritance.html
Diakses tanggal: 2020/02/21
Lisensi
Atribusi-NonKomersial-BerbagiSerupa 4.0 Internasional (CC BY-NC-SA 4.0)
Penulis
My journey kicks off from reading textbooks as a former Medical Student to digging bugs as a Software Engineer – a delightful rollercoaster of career twists. Embracing failure with the grace of a Cat avoiding water, I've seamlessly transitioned from Stethoscope to Keyboard. Armed with ability for learning and adapting faster than a Heart Beat, I'm on a mission to turn Code into a Product.
- Rizqi Nur Assyaufi