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

6

CRUD Generator dan Content Manager

Badaso memisahkan CRUD menjadi dua lapis. Lapis konfigurasi berupa baris di tabel data_types dan data_rows, sedangkan lapis artefak deploy berupa berkas seeder dan migration di aplikasi host. BadasoCRUDController memegang lapis konfigurasi dan menerima FileGenerator lewat constructor, sementara ContentManager, ContentGenerator, dan FileGenerator memegang lapis berkas. Nama tabel kedua model dibentuk saat konstruksi dari config('badaso.database.prefix'). src/Controllers/BadasoCRUDController.php:30-40 src/Models/DataType.php:16-21 src/Models/DataRow.php:18-25

DataType::dataRows adalah relasi hasMany yang diurutkan naik lewat kolom order, jadi urutan field pada form mengikuti nilai order yang ditulis controller. src/Models/DataType.php:43-46

Dua pintu masuk CRUD

Panel admin memakai dua kelompok endpoint. Kelompok crud mengelola konfigurasi CRUD yang sudah ada, sedangkan kelompok table membaca skema tabel nyata dan bisa membuat konfigurasi awal. src/Routes/api.php:136-153

MethodURIHandlerMiddleware
GET/crudBadasoCRUDController@browsebrowse_crud_data
GET/crud/readBadasoCRUDController@readread_crud_data
PUT/crud/editBadasoCRUDController@editedit_crud_data
POST/crud/addBadasoCRUDController@addadd_crud_data
DELETE/crud/deleteBadasoCRUDController@deletedelete_crud_data
GET/crud/read-by-slugBadasoCRUDController@readBySlugread_crud_data
GET/crud/maintenanceBadasoCRUDController@setMaintenanceStatemaintenance_crud_data
GET/tableBadasoTableController@browsebadaso.middleware.authenticate
GET/table/readBadasoTableController@readbadaso.middleware.authenticate
GET/table/dataBadasoTableController@getDataByTablebadaso.middleware.authenticate
GET/table/generate-crudBadasoTableController@generateCRUDbadaso.middleware.authenticate
GET/table/data-typeBadasoTableController@getDataTypebadaso.middleware.authenticate
GET/table/relation-data-by-slugBadasoTableController@getRelationDataBySlugbadaso.middleware.authenticate

BadasoCRUDController::browse menyaring Badaso::getProtectedTables() dari SchemaManager::listTables(), lalu memasangkan setiap tabel dengan DataType hasil where('name', $key)->first() sebagai crud_data. src/Controllers/BadasoCRUDController.php:42-72

BadasoCRUDController::read justru bergerak dari arah sebaliknya. Ia mengambil DataRow yang sudah dimiliki DataType, mengumpulkan kolom lewat pluck('field'), lalu menambahkan field yang belum terdaftar dengan tipe hasil DataTypeToComponent::convert, browse dan read aktif, sedangkan add, edit, dan delete dimatikan. src/Controllers/BadasoCRUDController.php:74-116 readBySlug melakukan hal yang sama dengan kunci slug. src/Controllers/BadasoCRUDController.php:118-137

BadasoTableController::generateCRUD adalah jalur cepat: satu DataType baru dibuat dari SchemaManager::describeTable, lalu satu DataRow per kolom dengan browse, read, edit, add, delete semuanya true dan order berurutan mulai 1. src/Controllers/BadasoTableController.php:72-125 Daftar tabel mentah untuk pilihan ini datang dari BadasoTableController::browse. src/Controllers/BadasoTableController.php:22-41

Menyusun DataType dan DataRow

add memvalidasi bahwa name wajib ada tabelnya di database, unik di DataType, dan tidak termasuk Badaso::getProtectedTables(). Kolom pada rows.*.field juga diverifikasi: kolom harus ada di tabel, dan kolom notnull tanpa nilai default ditolak bila add tidak dicentang. src/Controllers/BadasoCRUDController.php:286-303 edit memakai aturan yang sama tanpa batasan tabel terproteksi. src/Controllers/BadasoCRUDController.php:139-155

Field yang disimpan add mencakup display_name_singular, display_name_plural (default Str::plural), icon, model_name, policy_name, controller, order_column, generate_permissions, server_side, notification yang di-json_encode, dan is_soft_delete dari create_soft_delete. src/Controllers/BadasoCRUDController.php:342-361 edit menimpa field yang sama pada DataType yang ditemukan lewat id. src/Controllers/BadasoCRUDController.php:196-215

Setiap baris form menjadi satu DataRow dengan order = $index + 1. Kolom relation hanya di-json_encode bila type bernilai relation dan jumlah kunci relasi berada di antara 4 sampai 5. src/Controllers/BadasoCRUDController.php:363-401 Pada edit, seluruh DataRow lama dihapus lebih dulu lewat DataRow::where('data_type_id', ...)->delete(), lalu ditulis ulang dari payload. src/Controllers/BadasoCRUDController.php:217-261

sequenceDiagram
    participant Admin
    participant CRUD as BadasoCRUDController
    participant DB as data_types + data_rows
    participant Ev as CRUDDataAdded
    participant Orch as BadasoDeploymentOrchestrator
    participant FG as FileGenerator
    participant Seed as CRUD seeder files
    Admin->>CRUD: POST /crud/add
    CRUD->>CRUD: validate name, rows, create_soft_delete
    CRUD->>DB: simpan DataType
    CRUD->>DB: simpan DataRow per field
    CRUD->>Ev: event new CRUDDataAdded
    Ev->>Orch: CRUDDataChanged dengan change_type Added
    Orch->>Orch: cek config badaso-watch-tables
    Orch->>FG: CRUDDataAddedHandler
    FG->>Seed: generateDataTypeSeedFile dan generateDataRowSeedFile
    FG->>Seed: updateOrchestraSeeder
%% fig: Alur add CRUD sampai penulisan seeder
%% desc: Controller menyimpan konfigurasi CRUD, memicu event, lalu FileGenerator menulis seeder dan menyisipkan kelasnya ke orchestrator seeder.
Fig. 1: Alur add CRUD sampai penulisan seederController menyimpan konfigurasi CRUD, memicu event, lalu FileGenerator menulis seeder dan menyisipkan kelasnya ke orchestrator seeder.

Data relasi dibaca kembali oleh accessor getRelationAttribute, yang melakukan json_decode pada kolom relation. src/Models/DataRow.php:27-38

Permission, menu, dan jejak aktivitas

Bila generate_permissions bernilai benar, edit memanggil Permission::generateFor($data_type->name, true); bila salah, Permission::removeFrom yang dipanggil. add hanya memanggil generateFor. Sesudah itu addEditMenuItem menyiapkan item menu, event perubahan data dipicu, dokumentasi API digenerate, dan transaksi di-commit; kegagalan apa pun memicu DB::rollBack. src/Controllers/BadasoCRUDController.php:263-284 src/Controllers/BadasoCRUDController.php:403-431

addEditMenuItem memakai config('badaso.default_menu') sebagai kunci menu, membuat Menu bila belum ada, dan menyusun url menjadi / + kunci menu + / + slug. Permission item menu diisi browse_ + name bila generate_permissions aktif. deleteMenuItem menghapus item menu berdasarkan url yang sama. src/Controllers/BadasoCRUDController.php:472-519

delete menjalankan deleteAPIDocs, Permission::removeFrom, deleteMenuItem, lalu $data_type->delete() dan event(new CRUDDataDeleted(...)) di dalam satu transaksi. src/Controllers/BadasoCRUDController.php:433-470 generateAPIDocs menulis berkas dari ApiDocs::getStub lewat LaravelFileSystem::put, sedangkan deleteAPIDocs menghapusnya bila ada. src/Controllers/BadasoCRUDController.php:521-542

Event dan orchestrator

CRUDDataAdded menyimpan data_type dan data, lalu di dalam constructor memicu CRUDDataChanged dengan penanda 'Added'. src/Events/CRUDDataAdded.php:16-23 CRUDDataChanged membawa tiga properti: data_type, data, dan change_type. src/Events/CRUDDataChanged.php:18-25

OrchestratorEventServiceProvider memetakan CRUDDataChanged ke listener BadasoCRUDDataChanged. src/Providers/OrchestratorEventServiceProvider.php:12-16 Listener itu hanya meneruskan event ke BadasoDeploymentOrchestrator::handle. src/Listeners/BadasoCRUDDataChanged.php:21-24

BadasoDeploymentOrchestrator memakai tiga konstanta change_type, yaitu Added, Updated, dan Deleted, yang masing-masing dipetakan ke handler pada konstanta HANDLERS. src/BadasoDeploymentOrchestrator.php:15-29 Sebelum handler dipanggil, handle lebih dulu memeriksa config('badaso-watch-tables'): bila nama tabel tidak ada di daftar pantau, method langsung return tanpa melakukan apa pun. Handler diselesaikan lewat $this->app->make(...), dan kegagalan dibungkus menjadi OrchestratorHandlerNotFoundException. src/BadasoDeploymentOrchestrator.php:47-65

graph LR
    E[CRUDDataChanged] --> L[BadasoCRUDDataChanged]
    L --> O[BadasoDeploymentOrchestrator::handle]
    O -->|nama tabel tidak dipantau| X[return tanpa handler]
    O -->|Added| H1[CRUDDataAddedHandler]
    O -->|Updated| H2[CRUDDataUpdatedHandler]
    O -->|Deleted| H3[CRUDDataDeletedHandler]
    H1 --> F1[generateDataTypeSeedFile dan generateDataRowSeedFile]
    H2 --> F2[deleteAndGenerate]
    H3 --> F3[deleteSeedFiles dan generateSeedFileForDeletedData]
    style O fill:#f59e0b,color:#000
    style L fill:#8b5cf6,color:#fff
    style H1 fill:#2d7d46,color:#fff
    style H2 fill:#2d7d46,color:#fff
    style H3 fill:#dc2626,color:#fff
    style X fill:#dc2626,color:#fff
%% fig: Dispatch event ke handler orchestrator
%% desc: Satu listener meneruskan perubahan ke orchestrator, yang memilih handler berdasarkan change_type dan melewati tabel yang tidak dipantau.
Fig. 2: Dispatch event ke handler orchestratorSatu listener meneruskan perubahan ke orchestrator, yang memilih handler berdasarkan change_type dan melewati tabel yang tidak dipantau.

CRUDDataAddedHandler menghasilkan dua berkas: seeder DataType lalu seeder DataRow. src/OrchestratorHandlers/CRUDDataAddedHandler.php:28-37 CRUDDataUpdatedHandler cukup memanggil deleteAndGenerate. src/OrchestratorHandlers/CRUDDataUpdatedHandler.php:26-31 CRUDDataDeletedHandler memanggil $data_type->destroy($data_type->id), menghapus permission, menghapus seeder lama, lalu membuat seeder penghapus agar data bisa di-rollback. src/OrchestratorHandlers/CRUDDataDeletedHandler.php:29-45

Seeder dari stub

Nama kelas seeder dibentuk dari slug yang dipecah pada tanda -, setiap potongan di-ucfirst, lalu ditambah suffix. src/ContentManager/FileSystem.php:166-176 Ketiga suffix didefinisikan sebagai konstanta: CRUDDataTypeAdded, CRUDDataRowAdded, dan CRUDDataDeleted. src/ContentManager/FileGenerator.php:15-21 Berkas seeder ditulis ke base_path().'/database/seeders/Badaso/CRUD', dan direktori itu dibuat dengan mkdir bila belum ada. src/ContentManager/FileSystem.php:93-100

KonstantaNilaiStub sumber
TYPE_SEEDER_SUFFIXCRUDDataTypeAddeddata_seed.stub
ROW_SEEDER_SUFFIXCRUDDataRowAddedrow_seed.stub
DELETED_SEEDER_SUFFIXCRUDDataDeleteddelete_seed.stub
generateSeedFilesuffix dari pemanggilseed.stub
generateBDOMigrationFileprefix create/altermigration.stub

generateDataTypeSeedFile mengambil data_seed.stub, mengganti {{class}}, memanggil populateContentToStubFile dengan suffix TYPE_SEEDER_SUFFIX, menjalankan addDataTypeId, menulis berkas, lalu memperbarui orchestrator seeder. src/ContentManager/FileGenerator.php:52-84 generateDataRowSeedFile mengulang pola yang sama dengan row_seed.stub dan suffix ROW_SEEDER_SUFFIX. src/ContentManager/FileGenerator.php:91-121 generateSeedFileForDeletedData memakai delete_seed.stub dan suffix DELETED_SEEDER_SUFFIX. src/ContentManager/FileGenerator.php:182-208

addDataTypeId mengganti string '#dataTypeId' menjadi '$data_type->id' agar seeder memakai id hasil insert, bukan angka tetap. src/ContentManager/FileGenerator.php:215-222 deleteAndGenerate menjalankan deleteSeedFiles, lalu membuat ulang seeder tipe dan seeder baris. src/ContentManager/FileGenerator.php:128-135 deleteSeedFiles menghapus dua berkas seeder milik slug tersebut. src/ContentManager/FileGenerator.php:160-175

updateOrchestraSeeder membuka berkas BadasoDeploymentOrchestratorSeeder, meneruskan isinya ke ContentManager::updateDeploymentOrchestraSeederContent, kemudian menulis ulang berkas. src/ContentManager/FileGenerator.php:142-155 Method itu hanya membungkus ContentGenerator::generateOrchestraSeederContent. src/ContentManager/ContentManager.php:60-78

generateOrchestraSeederContent menyisipkan baris $this->seed(<Class>::class); di antara penanda #orchestraseeder_start dan #orchestraseeder_end. Bila penanda tidak ditemukan, ia memakai regex pada run() untuk menyisipkan baris yang sama sebelum kurung tutup. Untuk kelas ber-suffix CRUDDataDeleted, baris seed milik kelas tipe dan baris lama dilepas lebih dulu. src/ContentManager/ContentGenerator.php:204-241 generateManualSeederContent melakukan hal serupa dengan penanda #manualgenerateseeder_start dan #manualgenerateseeder_end. src/ContentManager/ContentGenerator.php:250-287

generateSeedFile dipakai untuk seeder manual: ia menolak tabel yang tidak ada lewat Schema::hasTable, membaca seluruh baris dengan $this->database_manager->table($table_name)->get(), memakai seed.stub, dan menambahkan kelas ke BadasoManualGenerateSeeder dengan memanggil generateManualSeederContent. src/ContentManager/FileGenerator.php:227-260

Placeholder stub yang diisi ContentManager

populateContentToStubFile memilih pengisi berdasarkan suffix, sehingga satu stub hanya menerima placeholder yang relevan. src/ContentManager/ContentManager.php:177-194

PlaceholderPengisiNilai yang disisipkan
{{delete_statements}}getDeleteStatementhapus DataType berdasarkan name
{{permission_insert_statements}}getPermissionStatementPermission::generateFor('<name>')
{{permission_delete_statements}}getPermissionStatement($data_type, 'delete')Permission::removeFrom('<name>')
{{menu_insert_statements}}getMenuInsertStatementsfirstOrFail menu default, lalu isi atau buat MenuItem
{{menu_delete_statements}}generateMenuDeleteStatementshapus MenuItem pada url yang dibentuk
{{datatype_slug_statement}}getDataTypeSlugStatement$data_type = ...where('name', '<name>')->first()
{{insert_statements}}populateInsertStatements\DB::table('<tabel>')->insert(<array>)

Statement hapus DataType memakai where('name', '%s') dua kali, sekali untuk first() dan sekali untuk delete(). src/ContentManager/ContentGenerator.php:14-20 Statement menu membentuk url dari config('badaso.default_menu') dan slug, memakai highestOrderMenuItem untuk order, serta mengisi permission browse_ + name hanya bila generate_permissions benar. src/ContentManager/ContentGenerator.php:23-56 src/ContentManager/ContentGenerator.php:179-195 Statement permission dipilih berdasarkan argumen $type: tanpa $type memakai generateFor, dengan $type memakai removeFrom. src/ContentManager/ContentGenerator.php:71-77

populateDataTypeSeederContent mengisi delete, permission, dan menu, lalu membuang kunci translations dan data_rows dari $data_type->toArray() sebelum menulis insert. src/ContentManager/ContentManager.php:97-115 populateDataRowSeederContent memakai $data_type->dataRows, mengambil nama tabel dari $rows->last()->getTable(), dan mengisi {{datatype_slug_statement}} lebih dulu. src/ContentManager/ContentManager.php:122-140 populateCRUDDataDeletedSeederContent mengisi delete, permission delete bila generate_permissions benar, dan menu delete. src/ContentManager/ContentManager.php:147-170

populateInsertStatements membentuk satu baris \DB::table('%s')->insert(%s); dan memakai ContentGenerator::formatContent untuk merapikan hasil var_export. src/ContentManager/ContentManager.php:229-243 formatContent membuang indeks numerik, mengganti indentasi menjadi empat spasi, dan melacak tanda kutip agar string tidak ikut diindentasi. src/ContentManager/ContentGenerator.php:86-127

repackContentData menyiapkan baris sebelum ditulis: kolom id dibuang, data_type_id dan foreign_key diganti menjadi #dataTypeId, sedangkan details dan relation di-json_encode bila masih berupa array atau object. src/ContentManager/ContentManager.php:25-53

Migration dari stub

generateBDOMigrationFile mengambil migration.stub, menyusun {{schema_up}} dari MigrationParser::getMigrationSchemaUp dan {{schema_down}} dari getMigrationSchemaDown, menambahkan schema relasi bila $relations tidak kosong, lalu menulis berkas lewat addContentToMigrationFile. src/ContentManager/FileGenerator.php:290-320

Stub migration mendefinisikan kelas {{class}} extends Migration dengan method up() dan down(). Blok up() membungkus {{schema_up}} dalam try/catch (PDOException $ex) yang memanggil $this->down() lalu melempar ulang exception. src/stubs/migration.stub:7-34

Nama berkas migration dibentuk Carbon::now()->format('Y_m_d_his') diikuti prefix, nama tabel, _table_, dan empat huruf terakhir nama kelas. src/ContentManager/FileSystem.php:65-70

⚠️ addTablePolymorphism bisa menjalankan Artisan::call('migrate', ['--path' => 'database/migrations/badaso/', '--force' => true]) langsung di dalam request add atau edit. Untuk relasi belongs_to_many, tabel pivot dibuat lewat generateBDOMigrationFile hanya bila Schema::hasTable($table) bernilai salah, lalu migrate dijalankan saat itu juga. src/Controllers/BadasoCRUDController.php:544-634

Pemetaan tipe kolom ke komponen

DataTypeToComponent::convert membaca tabel $type_list dan mengembalikan komponen form yang sesuai; tipe yang tidak dikenal dikembalikan apa adanya. Pemetaan yang tersedia mencakup CHAR/VARCHAR/JSON ke text, TEXT ke textarea, ENUM ke select, SET ke select_multiple, BOOL/BOOLEAN ke switch, INT/BIGINT/DECIMAL ke number, serta DATETIME/TIMESTAMP/TIME ke datetime. src/Helpers/DataTypeToComponent.php:8-46 src/Helpers/DataTypeToComponent.php:48-55

BadasoTableController::read memakai konversi ini saat menampilkan kolom tabel beserta notnull sebagai is_not_null. src/Controllers/BadasoTableController.php:43-70 Sebaliknya generateCRUD menyimpan $column['type'] mentah ke DataRow, tanpa melewati DataTypeToComponent. src/Controllers/BadasoTableController.php:91-97