Lompat ke konten
dokumentasi.id
Commit b472665
v1
Star1,3 rb

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

8

Menu dan Konfigurasi Aplikasi

Badaso mengelola dua hal dari panel yang sama: struktur navigasi (menus dan menu_items) dan nilai konfigurasi situs (configurations). Keduanya disimpan di tabel berprefiks badaso_ dan dibaca oleh BadasoMenuController serta BadasoConfigurationsController. Pembatasan akses per permission yang dipakai menu item dijelaskan di Autentikasi dan Otorisasi; daftar permission-nya sendiri dibahas di Manajemen User, Role, dan Permission.

Tabel berprefiks dinamis

Ketiga model tidak menuliskan nama tabel secara literal. Konstruktor masing-masing membaca config('badaso.database.prefix') lalu menempelkannya ke nama tabel, sehingga prefiks yang dipilih saat instalasi ikut berlaku pada menu, menu item, dan konfigurasi (src/Models/Menu.php:15-25). Pola yang sama dipakai MenuItem untuk tabel menu_items (src/Models/MenuItem.php:18-36) dan Configuration untuk tabel configurations (src/Models/Configuration.php:18-34). Kolom can_delete pada Configuration dibaca lewat accessor getCanDeleteAttribute yang mengembalikan boolean, bukan angka 0/1 (src/Models/Configuration.php:45-48).

Relasi data

menus adalah induk langsung menu_items lewat kolom menu_id. Di dalam menu_items sendiri ada relasi diri lewat parent_id, sehingga satu item bisa menjadi anak item lain dan membentuk pohon dua tingkat atau lebih. configurations berdiri sendiri: tidak ada foreign key ke menu, hanya kolom group dan order untuk mengelompokkan tampilan di panel.

erDiagram
  menus ||--o{ menu_items : "menu_id"
  menu_items ||--o{ menu_items : "parent_id"
  menus {
    bigint id PK
    varchar255 key UK
    varchar255 display_name
    varchar255 icon
    integer order
    boolean is_show_header
    boolean is_expand
  }
  menu_items {
    bigint id PK
    bigint menu_id FK
    varchar255 title
    varchar255 url
    varchar255 target
    varchar255 icon_class
    varchar255 color
    integer parent_id FK
    integer order
    boolean is_expand
    text permissions
  }
  configurations {
    bigint id PK
    varchar255 key UK
    varchar255 display_name
    text value
    text details
    varchar255 type
    integer order
    varchar255 group
    boolean can_delete
  }
%% fig: Relasi menus, menu_items, dan configurations
%% desc: menus memiliki banyak menu_items lewat menu_id, menu_items membentuk pohon lewat parent_id, sedangkan configurations berdiri tanpa foreign key.
Fig. 1: Relasi menus, menu_items, dan configurationsmenus memiliki banyak menu_items lewat menu_id, menu_items membentuk pohon lewat parent_id, sedangkan configurations berdiri tanpa foreign key.

Bentuk kolomnya ditentukan oleh migrasi, bukan oleh model.

TabelKolom intiMigrasi pembentuk
menuskey unik, display_name, iconsrc/Migrations/2020_11_18_015020_create_menus.php:17-23
menusis_show_header, is_expand, ordersrc/Migrations/2021_10_21_054710_add_is_show_header_and_expand_in_menus_table.php:16-26
menusorder dihapus lalu dibuat ulang sebagai integer nullablesrc/Migrations/2022_03_08_090850_change_order_integer_data_type_in_menus_table.php:16-25
menu_itemsmenu_id, title, url, target default _self, parent_id, order, permissionssrc/Migrations/2020_11_18_015029_create_menu_items.php:17-29
menu_itemsis_expand default truesrc/Migrations/2021_11_25_100130_add_is_expand_in_badaso_menu_items_table.php:16-20
configurationskey unik, value, details, type, order default 1, group, can_delete default falsesrc/Migrations/2020_11_18_014827_create_configurations.php:17-28

⚠️ Migrasi 2021_10_21_054710 membuat kolom order pada menus sebagai boolean, lalu migrasi 2022_03_08_090850 menghapus kolom itu dan menggantinya dengan integer nullable (src/Migrations/2021_10_21_054710_add_is_show_header_and_expand_in_menus_table.php:16-26). Kedua migrasi itu tidak boleh dijalankan dengan urutan berbeda.

Siklus permintaan menu item

Panel admin meminta menu item berdasarkan menu_key. Controller memvalidasi bahwa key itu ada di tabel menus, mengambil menunya, lalu melakukan join ke menu_items dan hanya mengambil baris dengan parent_id null (src/Controllers/BadasoMenuController.php:146-177). Setiap item yang lolos disaring dengan AuthenticatedUser::isAllowedTo, lalu getChildMenuItems menambahkan anak-anaknya secara rekursif (src/Controllers/BadasoMenuController.php:255-276).

graph LR
  panel["Admin panel"] --> route["GET menus/item-by-key"]
  route --> ctrl["browseMenuItemByKey"]
  ctrl --> menus[("badaso_menus")]
  ctrl --> items[("badaso_menu_items")]
  ctrl --> allow["AuthenticatedUser::isAllowedTo"]
  allow --> child["getChildMenuItems"]
  child --> items
  child --> resp["ApiResponse::success"]
  style menus fill:#336791,color:#fff
  style items fill:#336791,color:#fff
  style ctrl fill:#f59e0b,color:#000
  style child fill:#8b5cf6,color:#fff
  style resp fill:#2d7d46,color:#fff
%% fig: Alur browseMenuItemByKey
%% desc: Controller membaca menus dan menu_items, menyaring tiap item dengan isAllowedTo, lalu menyusun anak item secara rekursif sebelum mengembalikan response.
Fig. 2: Alur browseMenuItemByKeyController membaca menus dan menu_items, menyaring tiap item dengan isAllowedTo, lalu menyusun anak item secara rekursif sebelum mengembalikan response.

Penyaringan menu item berdasarkan permission

isAllowedTo mengembalikan true bila permissions item bernilai null, dan untuk item yang punya daftar permission ia membandingkannya dengan permission user yang sedang login (src/Helpers/AuthenticatedUser.php:44-62). Menu item tanpa permissions karena itu selalu terlihat oleh setiap user yang bisa membuka panel.

Permission sebuah item disimpan sebagai satu string berkoma. setMenuItemPermissions memetakan id permission menjadi key lewat Permission::whereIn('id', $permission_ids), menyimpannya dengan implode(',', $permissions), dan menulis null bila daftar kosong (src/Controllers/BadasoMenuController.php:625-652). getMenuItemPermissions melakukan kebalikannya: setiap Permission diberi tanda selected bernilai 1 atau 0 (src/Controllers/BadasoMenuController.php:591-623).

Urutan, induk, dan status buka

addMenuItem menaruh item baru di urutan terakhir lewat highestOrderMenuItem(), yang mengambil order terbesar pada menu_id dan parent_id yang sama lalu menambahinya satu (src/Models/MenuItem.php:38-57). Pemanggilan itu terjadi di dalam addMenuItem (src/Controllers/BadasoMenuController.php:310-352).

editMenuItemOrder menggeser tetangga: bila urutan baru lebih besar, item di antaranya diturunkan satu; bila lebih kecil, item di antaranya dinaikkan satu; setelah itu order item yang dipindah disimpan (src/Controllers/BadasoMenuController.php:430-494).

editMenuItemsOrder menerima pohon menu_items dan menyerahkannya ke updateMenuItems, yang menulis order = index + 1 beserta parent_id untuk setiap tingkat, lalu turun ke children (src/Controllers/BadasoMenuController.php:522-533).

menuOptions menangani tiga hal sekaligus berdasarkan isi request: pengurutan ulang menus lewat array order, toggle is_expand untuk menu maupun menu item (dibedakan oleh type), dan toggle is_show_header (src/Controllers/BadasoMenuController.php:30-87). hasChildren() sendiri hanya menghitung baris dengan parent_id sama dengan id item, dan hasilnya menentukan apakah penelusuran anak perlu dilanjutkan (src/Models/MenuItem.php:59-65).

Konfigurasi: tipe, details, dan can_delete

browse mengembalikan seluruh konfigurasi terurut kolom order (src/Controllers/BadasoConfigurationsController.php:21-28), sedangkan fetch memfilter berdasarkan key atau group (src/Controllers/BadasoConfigurationsController.php:46-69). applyable mengubah kumpulan baris menjadi peta key => value (src/Controllers/BadasoConfigurationsController.php:104-119).

add menurunkan nama properti dengan Str::snake lalu hanya mengisi kolom yang ada di $configuration->getFillable(). Untuk type checkbox, radio, select, atau select_multiple, details wajib berupa JSON valid; bila tidak, validasi gagal dengan pesan The details must be a valid JSON string. (src/Controllers/BadasoConfigurationsController.php:196-246).

Saat banyak baris diubah sekaligus, editMultiple menyimpannya dalam satu transaksi lalu memanggil ConfigurationRedis::save(Configuration::all()) sebelum commit (src/Controllers/BadasoConfigurationsController.php:152-194).

graph TD
  req["PUT configurations/edit-multiple"] --> valid["Validator::make per baris"]
  valid --> loop["foreach request->configurations"]
  loop --> save["Configuration::save"]
  save --> redis["ConfigurationRedis::save"]
  redis --> db[("badaso_configurations")]
  save --> db
  style req fill:#f59e0b,color:#000
  style redis fill:#8b5cf6,color:#fff
  style save fill:#2d7d46,color:#fff
  style db fill:#336791,color:#fff
%% fig: Alur editMultiple konfigurasi
%% desc: Setiap baris divalidasi dan disimpan dalam satu transaksi, lalu seluruh konfigurasi ditulis ke cache Redis sebelum commit.
Fig. 3: Alur editMultiple konfigurasiSetiap baris divalidasi dan disimpan dalam satu transaksi, lalu seluruh konfigurasi ditulis ke cache Redis sebelum commit.

delete menolak menghapus konfigurasi yang can_delete bernilai false dengan melempar SingleException('Cannot delete this config') (src/Controllers/BadasoConfigurationsController.php:248-272).

Endpoint

MethodPathHandlerPermission
GETmenus/browseMenubrowse_menus
GETmenus/readreadMenuread_menus
PUTmenus/editeditMenuedit_menus
POSTmenus/addaddMenuadd_menus
DELETEmenus/deletedeleteMenudelete_menus
PUTmenus/arrange-itemseditMenuItemsOrderedit_menus
GETmenus/itembrowseMenuItembrowse_menu_items
GETmenus/item/readreadMenuItemread_menu_items
PUTmenus/item/editeditMenuItemedit_menu_items
PUTmenus/item/edit-ordereditMenuItemOrderedit_menu_items
POSTmenus/item/addaddMenuItemadd_menu_items
DELETEmenus/item/deletedeleteMenuItemdelete_menu_items
GETmenus/item/permissionsgetMenuItemPermissionsedit_menu_items
PUTmenus/item/permissionssetMenuItemPermissionsedit_menu_items
GETmenus/item-by-keybrowseMenuItemByKeytanpa middleware permission
GETmenus/item-by-keysbrowseMenuItemByKeystanpa middleware permission
PUTmenus/menu-optionsmenuOptionstanpa middleware permission
GETconfigurations/browsebrowse_configurations
GETconfigurations/readreadread_configurations
GETconfigurations/fetchfetchtanpa middleware permission
GETconfigurations/fetch-multiplefetchMultipletanpa middleware permission
PUTconfigurations/editeditedit_configurations
PUTconfigurations/edit-multipleeditMultipleedit_configurations
POSTconfigurations/addaddadd_configurations
DELETEconfigurations/deletedeletedelete_configurations

Semua route di atas berada di dalam grup middleware yang sama dan memakai config('badaso.middleware.check_permission') sebagai nama middleware pemeriksa permission (src/Routes/api.php:58-92).

⚠️ GET configurations/applyable dan GET configurations/maintenance didaftarkan sebelum route configurations/ yang memakai middleware permission, sehingga keduanya dapat dipanggil tanpa permission browse_configurations (src/Routes/api.php:58-61).

Seeder pembentuk nilai awal

MenusSeeder membuat dua menu dengan key general dan core. Bila menu dengan key tersebut sudah ada, baris di-update, bukan dibuat ulang (src/Seeder/Configurations/MenusSeeder.php:22-42).

FixedMenuItemSeeder mencari kedua menu itu dengan firstOrFail() (src/Seeder/Configurations/FixedMenuItemSeeder.php:23-24), lalu menyusun daftar $menu_items yang mencakup 14 entri untuk menu core dan general (src/Seeder/Configurations/FixedMenuItemSeeder.php:25-199). Pencocokan untuk update memakai pasangan menu_id dan url (src/Seeder/Configurations/FixedMenuItemSeeder.php:201-212). Anak item ditandai dengan parent_id berupa angka, misalnya Files dan Images memakai parent_id 11 agar menempel ke Media Manager (src/Seeder/Configurations/FixedMenuItemSeeder.php:161-184).

ConfigurationsSeeder menyimpan 13 baris konfigurasi dengan group adminPanel, dan seluruhnya memakai can_delete bernilai 0 (src/Seeder/Configurations/ConfigurationsSeeder.php:22-153). Sebelum membuat baris, seeder menghapus baris lama yang key-nya sama (src/Seeder/Configurations/ConfigurationsSeeder.php:155-158).

⚠️ Karena Configuration::where('key', $value['key'])->delete() dijalankan lebih dulu, menjalankan ulang seeder mengembalikan nilai konfigurasi ke default dan membuang perubahan yang dibuat admin (src/Seeder/Configurations/ConfigurationsSeeder.php:155-158).