store/BEST_PRACTICE.md

6.6 KiB

Best Practices

PHP

Traits

  • Gabungkan multiple use trait menjadi satu baris dengan koma.
  • Hindari:
    use FlashesEntityMessage;
    use ParsesDataTableQuery;
    
  • Gunakan:
    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:
    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.
      $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:
      $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:
    $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:
    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:
      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:
    import CategoryFormModal from '@/components/admin/master/categories/CategoryFormModal.vue';
    
  • Gunakan:
    // 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)