181 lines
6.6 KiB
Markdown
181 lines
6.6 KiB
Markdown
# Best Practices
|
|
|
|
## PHP
|
|
|
|
### Traits
|
|
- Gabungkan multiple `use` trait menjadi satu baris dengan koma.
|
|
- **Hindari:**
|
|
```php
|
|
use FlashesEntityMessage;
|
|
use ParsesDataTableQuery;
|
|
```
|
|
- **Gunakan:**
|
|
```php
|
|
use FlashesEntityMessage, ParsesDataTableQuery;
|
|
```
|
|
|
|
## Database & Migrasi
|
|
|
|
### Struktur File Migrasi
|
|
- Gunakan *anonymous class* yang meng-extend `Migration`.
|
|
- Tulis *return type hint* `: void` secara eksplisit pada method `up()` dan `down()`.
|
|
- **Urutan & Pengelompokan Kolom (Ordering & Grouping):**
|
|
1. **Primary Key**: `$table->id()` ditaruh paling atas.
|
|
2. **Relasi (Foreign Keys / Morphs)**: Ditaruh tepat setelah Primary Key di bagian teratas.
|
|
3. **Kolom Data Utama**: Dikelompokkan secara logis menggunakan spasi (baris kosong) sebagai pemisah antar kelompok data.
|
|
4. **Timestamps & Soft Deletes**: Ditaruh paling bawah.
|
|
- **Gunakan:**
|
|
```php
|
|
use Illuminate\Database\Migrations\Migration;
|
|
use Illuminate\Database\Schema\Blueprint;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
return new class extends Migration
|
|
{
|
|
public function up(): void
|
|
{
|
|
Schema::create('table_name', function (Blueprint $table) {
|
|
$table->id();
|
|
|
|
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
|
$table->foreignId('category_id')->nullable()->constrained()->nullOnDelete();
|
|
|
|
$table->string('name', 100);
|
|
$table->string('description', 255)->nullable();
|
|
|
|
$table->timestamp('created_at')->useCurrent();
|
|
$table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate();
|
|
$table->softDeletes();
|
|
});
|
|
}
|
|
|
|
public function down(): void
|
|
{
|
|
Schema::dropIfExists('table_name');
|
|
}
|
|
};
|
|
```
|
|
|
|
### Definisi Kolom & Tipe Data
|
|
- **ID & Timestamps:**
|
|
- Gunakan `$table->id()` sebagai primary key.
|
|
- Gunakan `timestamp` eksplisit dengan default `useCurrent()` dan `useCurrentOnUpdate()`. **Hindari penggunaan `$table->timestamps()` bawaan Laravel agar format kolom presisi dan konsisten.**
|
|
- Tambahkan `$table->softDeletes()` (atau `deleted_at`) jika model terkait menggunakan trait `SoftDeletes`.
|
|
```php
|
|
$table->timestamp('created_at')->useCurrent();
|
|
$table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate();
|
|
$table->softDeletes();
|
|
```
|
|
- **Kolom Enum:**
|
|
- Hubungkan dengan PHP Backed Enum menggunakan `array_column(EnumName::cases(), 'value')`.
|
|
- **Contoh:**
|
|
```php
|
|
$table->enum('status', array_column(StatusEnum::cases(), 'value'))->default(StatusEnum::PENDING->value);
|
|
```
|
|
|
|
### Relasi & Foreign Key
|
|
- Gunakan `$table->foreignId()`.
|
|
- Tentukan constraint relasi secara eksplisit dengan `constrained()` (dan sebutkan nama tabel target jika nama kolom tidak sesuai dengan nama tabel, misal: `constrained('users')`).
|
|
- Tentukan aksi penghapusan secara eksplisit (`cascadeOnDelete()`, `nullOnDelete()`, atau `restrictOnDelete()`) sesuai kebutuhan integritas data.
|
|
- **Contoh:**
|
|
```php
|
|
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
|
$table->foreignId('category_id')->nullable()->constrained('categories')->nullOnDelete();
|
|
```
|
|
|
|
### Modifikasi Tabel (Alter Schema)
|
|
- Saat menambahkan kolom baru, gunakan modifier `->after('column_name')` agar tata letak kolom di database terstruktur dengan logis.
|
|
- Jika menambahkan kolom baru yang non-nullable pada tabel yang sudah berisi data, jalankan data migration di dalam method `up()` setelah schema builder:
|
|
```php
|
|
DB::table('table_name')->update(['column_name' => 'default_value']);
|
|
```
|
|
- Di dalam method `down()`, pastikan untuk melepas constraint foreign key terlebih dahulu sebelum menghapus kolom bersangkutan. Gunakan `dropConstrainedForeignId` atau `dropForeign` untuk cara yang aman dan ringkas.
|
|
- **Gunakan:**
|
|
```php
|
|
public function down(): void
|
|
{
|
|
Schema::table('table_name', function (Blueprint $table) {
|
|
$table->dropConstrainedForeignId('foreign_key_id');
|
|
$table->dropColumn(['column_one', 'column_two']);
|
|
});
|
|
}
|
|
```
|
|
|
|
## JavaScript / Vue
|
|
|
|
### Struktur Folder Page-Specific Components
|
|
Komponen yang hanya digunakan oleh satu page tertentu (misalnya modal form, kolom tabel, action button) **harus ditempatkan di folder page-nya langsung** di dalam subfolder sesuai fungsinya, bukan di `resources/js/components/`.
|
|
|
|
Folder `resources/js/components/` hanya untuk komponen yang **reusable** lintas page (seperti UI primitives, DataTable, ConfirmDialog, dll).
|
|
|
|
Subfolder di dalam page folder dikelompokkan berdasarkan fungsi:
|
|
- `form/` — modal form, form fields
|
|
- `table/` — columns definition, data-table-actions, cell renderers
|
|
|
|
- **Hindari:**
|
|
```
|
|
resources/js/components/admin/master/categories/
|
|
CategoryFormModal.vue
|
|
columns.ts
|
|
data-table-actions.vue
|
|
resources/js/pages/admin/master/categories/
|
|
Index.vue
|
|
```
|
|
- **Gunakan:**
|
|
```
|
|
resources/js/pages/admin/master/categories/
|
|
Index.vue
|
|
form/
|
|
CategoryFormModal.vue
|
|
table/
|
|
columns.ts
|
|
data-table-actions.vue
|
|
```
|
|
|
|
### Import Path
|
|
Gunakan relative import (`./`) untuk file yang berada di folder/subfolder yang sama, bukan absolute path `@/...`.
|
|
|
|
- **Hindari:**
|
|
```ts
|
|
import CategoryFormModal from '@/components/admin/master/categories/CategoryFormModal.vue';
|
|
```
|
|
- **Gunakan:**
|
|
```ts
|
|
// Dari Index.vue ke subfolder
|
|
import CategoryFormModal from './form/CategoryFormModal.vue';
|
|
import { createColumns } from './table/columns';
|
|
|
|
// Dari columns.ts ke file satu folder
|
|
import DataTableActions from './data-table-actions.vue';
|
|
```
|
|
|
|
### Pola CRUD (Create/Edit)
|
|
Gunakan **modal** untuk CRUD yang sederhana (sedikit field, tidak ada nested data complex), dan **page terpisah** untuk CRUD yang kompleks.
|
|
|
|
- **Modal** — form sederhana, biasanya 1-3 field, tidak memerlukan layout khusus:
|
|
```
|
|
pages/admin/master/categories/
|
|
Index.vue
|
|
form/
|
|
CategoryFormModal.vue ← modal form
|
|
table/
|
|
columns.ts
|
|
data-table-actions.vue
|
|
```
|
|
Contoh: categories (1 field), suppliers (3 field), customers (3 field)
|
|
|
|
- **Page terpisah** — form kompleks, banyak field, nested data, file upload, atau memerlukan layout khusus:
|
|
```
|
|
pages/admin/master/raw-materials/
|
|
Index.vue
|
|
Create.vue ← halaman tambah
|
|
Edit.vue ← halaman ubah
|
|
form/
|
|
RawMaterialForm.vue ← form component
|
|
table/
|
|
RawMaterialGroupedTable.vue
|
|
data-table-actions.vue
|
|
raw-material-status-toggle.vue
|
|
```
|
|
Contoh: raw-materials (nested prices, multi-image upload, dynamic variant rows)
|