Ada banyak cara untuk memecahkan masalah, dan itulah yang terjadi dengan mengelola peran dan status pengguna dalam sistem perangkat lunak. Dalam artikel ini, Anda akan menemukan evolusi sederhana dari ide tersebut serta beberapa tips dan contoh kode yang berguna.
Ide Dasar
Di sebagian besar sistem, biasanya ada kebutuhan untuk memiliki peran dan status user pengguna .
Peran terkait dengan hak yang dimiliki pengguna saat menggunakan sistem setelah berhasil masuk. Contoh peran adalah “karyawan pusat panggilan”, “manajer pusat panggilan”, “karyawan back office”, “manajer back office”, atau “manajer”. Secara umum itu berarti bahwa pengguna akan memiliki akses ke beberapa fungsi jika dia memiliki peran yang sesuai. Sebaiknya asumsikan bahwa pengguna dapat memiliki banyak peran pada saat yang bersamaan.
Status jauh lebih ketat dan menentukan apakah pengguna memiliki hak untuk masuk ke sistem atau tidak. Seorang pengguna hanya dapat memiliki satu status pada suatu waktu. Contoh statusnya adalah:“bekerja”, “berlibur”, “cuti sakit”, “kontrak berakhir”.
Saat kami mengubah status pengguna, kami masih dapat mempertahankan semua peran yang terkait dengan pengguna tersebut tidak berubah. Itu sangat membantu karena sebagian besar waktu kami hanya ingin mengubah status pengguna. Jika pengguna yang bekerja sebagai karyawan call center pergi berlibur, kita cukup mengubah statusnya menjadi “liburan” dan mengembalikannya ke status “bekerja” saat dia kembali.
Menguji peran dan status selama login memungkinkan kami memutuskan apa yang akan terjadi. Misalnya, mungkin kita ingin melarang login meskipun username dan password sudah benar. Kita dapat melakukannya jika status pengguna saat ini tidak menunjukkan bahwa dia bekerja atau jika pengguna tidak memiliki peran apa pun dalam sistem.
Dalam semua model yang diberikan di bawah ini, tabel status
dan role
adalah sama.
Tabel status
memiliki bidang id
dan status_name
dan atribut is_active
. Jika atribut is_active
disetel ke “True”, artinya pengguna yang memiliki status tersebut sedang bekerja. Misalnya, status "bekerja" akan memiliki atribut is_active
dengan nilai Benar, sementara yang lain (“berlibur”, “cuti sakit”, “kontrak berakhir”) akan memiliki nilai Salah.
Tabel peran hanya memiliki dua bidang:id
dan role_name
.
user_account
tabel sama dengan user_account
tabel yang disajikan dalam artikel ini. Hanya pada model pertama user_account
tabel berisi dua atribut tambahan (role_id
dan status_id
).
Beberapa model akan dihadirkan. Semuanya berfungsi dan dapat digunakan tetapi memiliki kelebihan dan kekurangannya masing-masing.
Model Sederhana
Ide pertama bisa jadi kita cukup menambahkan hubungan kunci asing ke user_account
tabel, merujuk pada tabel status
dan role
. Keduanya role_id
dan status_id
adalah wajib.
Ini cukup sederhana untuk dirancang dan juga untuk menangani data dengan kueri tetapi memiliki beberapa kelemahan:
-
Kami tidak menyimpan data riwayat (atau masa depan).
Saat kami mengubah status atau peran, kami cukup memperbarui
status_id
danrole_id
diuser_account
meja. Itu akan berfungsi dengan baik untuk saat ini, jadi ketika kami membuat perubahan, itu akan tercermin dalam sistem. Tidak apa-apa jika kita tidak perlu tahu bagaimana status dan peran telah berubah secara historis. Juga ada masalah karena kami tidak dapat menambahkan masa depan peran atau status tanpa menambahkan tabel tambahan ke model ini. Satu situasi di mana kita mungkin ingin memiliki pilihan itu adalah ketika kita tahu bahwa seseorang akan berlibur mulai Senin depan. Contoh lain adalah ketika kita memiliki karyawan baru; mungkin kita ingin memasukkan status dan perannya sekarang dan untuk itu menjadi valid di beberapa titik di masa depan.Ada juga komplikasi jika kami memiliki acara terjadwal yang menggunakan peran dan status. Acara yang menyiapkan data untuk hari kerja berikutnya biasanya berjalan saat sebagian besar pengguna tidak menggunakan sistem (misalnya saat malam hari). Jadi, jika seseorang tidak bekerja besok, kita harus menunggu sampai akhir hari ini dan kemudian mengubah peran dan statusnya sebagaimana mestinya. Misalnya, jika kita memiliki karyawan yang saat ini bekerja dan memiliki peran "karyawan call center", mereka akan mendapatkan daftar klien yang harus mereka hubungi. Jika seseorang secara tidak sengaja memiliki status dan peran itu, dia juga akan mendapatkan kliennya dan kita harus meluangkan waktu untuk memperbaikinya.
-
Pengguna hanya dapat memiliki satu peran dalam satu waktu.
Umumnya pengguna harus dapat memiliki lebih dari satu peran didalam sistem. Mungkin pada saat Anda mendesain database tidak perlu hal seperti itu. Perlu diingat bahwa perubahan alur kerja/proses dapat terjadi. Misalnya, pada suatu saat klien dapat memutuskan untuk menggabungkan dua peran menjadi satu. Salah satu solusi yang mungkin adalah membuat peran baru dan menetapkan semua fungsi dari peran sebelumnya ke peran tersebut. Solusi lain (jika pengguna dapat memiliki lebih dari satu peran) adalah klien hanya menetapkan kedua peran tersebut kepada pengguna yang membutuhkannya. Tentu saja solusi kedua lebih praktis dan memberikan klien kemampuan untuk menyesuaikan sistem dengan kebutuhannya lebih cepat (yang tidak didukung oleh model ini).
Di sisi lain, model ini juga memiliki satu keunggulan besar dibandingkan yang lain. Ini sederhana dan kueri untuk mengubah status dan peran juga sederhana. Juga, kueri yang memeriksa apakah pengguna memiliki hak untuk masuk ke sistem jauh lebih sederhana daripada dalam kasus lain:
select user_account.id, user_account.role_id from user_account left join status on user_account.status_id = status.id where status.is_user_working = True and user_account.user_name = @user_name and user_account.password_hash_algorithm = @password;
@user_name dan @password adalah variabel dari formulir input sementara kueri mengembalikan id pengguna dan role_id yang dia miliki. Jika nama_pengguna atau kata sandi tidak valid, pasangan_nama_pengguna dan kata sandi tidak ada, atau pengguna memiliki status yang ditetapkan yang tidak aktif, kueri tidak akan mengembalikan hasil apa pun. Dengan begitu kita bisa melarang login.
Model ini dapat digunakan jika:
- kami yakin tidak akan ada perubahan dalam proses yang mengharuskan pengguna memiliki lebih dari satu peran
- kami tidak perlu melacak perubahan peran/status dalam riwayat
- kami tidak berharap memiliki banyak peran/status administrasi.
Komponen Waktu Ditambahkan
Jika kita perlu melacak peran pengguna dan riwayat status, kita harus menambahkan banyak ke banyak hubungan antara user_account
dan role
dan user_account
dan status
. Tentu saja kami akan menghapus role_id
dan status_id
dari user_account
meja. Tabel baru dalam model adalah user_has_role
dan user_has_status
dan semua bidang di dalamnya, kecuali waktu berakhir, adalah wajib.
Tabel user_has_role
berisi data tentang semua peran yang pernah dimiliki pengguna dalam sistem. Kunci alternatifnya adalah (user_account_id
, role_id
, role_start_time
) karena tidak ada gunanya menetapkan peran yang sama secara bersamaan kepada pengguna lebih dari sekali.
Tabel user_has_status
berisi data tentang semua status yang pernah dimiliki pengguna dalam sistem. Kunci alternatif di sini adalah (user_account_id
, status_start_time
) karena pengguna tidak dapat memiliki dua status yang dimulai secara bersamaan.
Waktu mulai tidak boleh nol karena saat kita menyisipkan peran/status baru, kita tahu saat mulainya. Waktu berakhir dapat menjadi null jika kita tidak tahu kapan peran/status akan berakhir (mis. peran berlaku mulai besok hingga sesuatu terjadi di masa mendatang).
Selain memiliki histori yang lengkap, kini kita dapat menambahkan status dan peran di masa mendatang. Namun hal ini menimbulkan komplikasi karena kita harus memeriksa tumpang tindih saat melakukan penyisipan atau pembaruan.
Misalnya, pengguna hanya dapat memiliki satu status dalam satu waktu. Sebelum kita menyisipkan status baru, kita harus membandingkan waktu mulai dan waktu berakhir status baru dengan semua status yang ada untuk pengguna tersebut di database. Kita bisa menggunakan query seperti ini:
select * from user_has_status where user_has_status.user_account_id = @user_account_id and ( # test if @start_time included in interval of some previous status (user_has_status.status_start_time <= @start_time and ifnull(user_has_status.status_end_time, "2200-01-01") >= @start_time) or # test if @end_time included in interval of some previous status (user_has_status.status_start_time <= @end_time and ifnull(user_has_status.status_end_time, "2200-01-01") >= ifnull(@end_time, "2199-12-31")) or # if @end_time is null we cannot have any statuses after @start_time (@end_time is null and user_has_status.status_start_time >= @start_time) or # new status "includes" old satus (@start_time <= user_has_status.status_start_time <= @end_time) (user_has_status.status_start_time >= @start_time and user_has_status.status_start_time <= ifnull(@end_time, "2199-12-31")) )
@start_time
dan @end_time
adalah variabel yang berisi waktu mulai dan waktu berakhir dari status yang ingin kita sisipkan dan @user_account_id
adalah id pengguna yang kami masukkan. @end_time
bisa menjadi nol dan kita harus menanganinya dalam kueri. Untuk tujuan ini, nilai null diuji dengan ifnull()
fungsi. Jika nilainya nol, nilai tanggal tinggi ditetapkan (cukup tinggi sehingga ketika seseorang melihat kesalahan dalam kueri, kami akan lama pergi :). Kueri memeriksa semua kombinasi waktu mulai dan waktu berakhir untuk status baru dibandingkan dengan waktu mulai dan waktu berakhir dari status yang ada. Jika kueri mengembalikan catatan apa pun, maka kami memiliki tumpang tindih dengan status yang ada dan kami harus melarang penyisipan status baru. Juga akan menyenangkan untuk memunculkan kesalahan khusus.
Jika kami ingin memeriksa daftar peran dan status saat ini (hak pengguna), kami cukup menguji menggunakan waktu mulai dan waktu berakhir.
select user_account.id, user_has_role.id from user_account left join user_has_role on user_has_role.user_account_id = user_account.id left join user_has_status on user_account.id = user_has_status.user_account_id left join status on user_has_status.status_id = status.id where user_account.user_name = @user_name and user_account.password_hash_algorithm = @password and user_has_role.role_start_time <= @time and ifnull(user_has_role.role_end_time,"2200-01-01") >= @time and user_has_status.status_start_time <= @time and ifnull(user_has_status.status_end_time,"2200-01-01") >= @time and status.is_user_working = True
@user_name
dan @password
adalah variabel dari formulir input sementara @time
dapat diatur ke Now(). Saat pengguna mencoba masuk, kami ingin memeriksa haknya saat itu. Hasil adalah daftar semua peran yang dimiliki pengguna dalam sistem jika nama_pengguna dan kata sandi cocok dan pengguna saat ini memiliki status aktif. Jika pengguna memiliki status aktif tetapi tidak ada peran yang ditetapkan, kueri tidak akan mengembalikan apa pun.
Kueri ini lebih sederhana daripada yang ada di bagian 3 dan model ini memungkinkan kita memiliki riwayat status dan peran. Selain itu, kami dapat mengelola status dan peran untuk masa depan dan semuanya akan berfungsi dengan baik.
Model Akhir
Ini hanyalah gambaran tentang bagaimana model sebelumnya dapat diubah jika kami ingin meningkatkan kinerja. Karena pengguna hanya dapat memiliki satu status aktif dalam satu waktu, kami dapat menambahkan status_id
ke user_account
tabel (current_status_id
). Dengan begitu, kita dapat menguji nilai atribut tersebut dan tidak perlu bergabung dengan user_has_status
meja. Kueri yang dimodifikasi akan terlihat seperti ini:
select user_account.id, user_has_role.id from user_account left join user_has_role on user_has_role.user_account_id = user_account.id left join status on user_account.current_status_id = status.id where user_account.user_name = @user_name and user_account.password_hash_algorithm = @password and user_has_role.role_start_time <= @time and ifnull(user_has_role.role_end_time,"2200-01-01") >= @time and status.is_user_working = True
Jelas ini menyederhanakan kueri dan menghasilkan kinerja yang lebih baik tetapi ada masalah yang lebih besar yang perlu dipecahkan. current_status_id
di user_account
tabel harus diperiksa dan diubah jika perlu dalam situasi berikut:
- pada setiap sisipan/perbarui/hapus di
user_has_status
meja - setiap hari dalam acara terjadwal, kita harus memeriksa apakah status seseorang berubah (status aktif saat ini kedaluwarsa atau/dan beberapa status di masa mendatang menjadi aktif) dan memperbaruinya sebagaimana mestinya
Sebaiknya simpan nilai yang akan sering digunakan kueri. Dengan begitu kita akan menghindari melakukan pemeriksaan yang sama berulang-ulang dan membagi pekerjaan. Di sini kita akan menghindari bergabung dengan user_has_status
tabel dan kami akan membuat perubahan pada current_status_id
hanya ketika itu terjadi (masukkan/perbarui/hapus) atau ketika sistem tidak terlalu banyak digunakan (peristiwa terjadwal biasanya berjalan ketika sebagian besar pengguna tidak menggunakan sistem). Mungkin dalam hal ini kita tidak akan mendapatkan banyak keuntungan dari current_status_id
tetapi lihat ini sebagai ide yang dapat membantu dalam situasi serupa.