Skip to content

Trait WithProvince

Trait WithProvince menambahkan relasi belongsTo ke model Province, memungkinkan model Anda untuk berelasi dengan provinsi tertentu (tingkat administratif tertinggi di Indonesia).

Namespace

php
Creasi\Nusa\Models\Concerns\WithProvince

Penggunaan

Implementasi Dasar

php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Creasi\Nusa\Models\Concerns\WithProvince;

class KantorWilayah extends Model
{
    use WithProvince;
    
    protected $fillable = [
        'name',
        'deskripsi',
        'province_code'
    ];
}

Migrasi Database

php
Schema::create('kantor_wilayah', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->text('deskripsi')->nullable();
    $table->string('province_code');
    $table->timestamps();
    
    // Opsional: Tambahkan constraint foreign key
    $table->foreign('province_code')
          ->references('code')
          ->on('provinces')
          ->onDelete('cascade');
});

Fitur

Fillable Otomatis

Trait ini secara otomatis menambahkan foreign key provinsi ke dalam array $fillable model:

php
// Ditambahkan ke fillable secara otomatis
protected $fillable = ['province_code']; // atau nama kunci kustom

Relasi Provinsi

Mengakses provinsi yang berelasi:

php
$kantor = KantorWilayah::find(1);
$provinsi = $kantor->province;

echo "Kantor berlokasi di: {$provinsi->name}";
echo "Koordinat provinsi: {$provinsi->latitude}, {$provinsi->longitude}";

Eager Loading

php
$kantor = KantorWilayah::with('province')->get();

foreach ($kantor as $k) {
    echo "{$k->name} - {$k->province->name}";
}

Kustomisasi

Foreign Key Kustom

Anda dapat mengkustomisasi nama kolom foreign key:

php
class KantorWilayah extends Model
{
    use WithProvince;
    
    protected $provinceKey = 'province_code_kantor';
    
    protected $fillable = [
        'name',
        'deskripsi',
        'province_code_kantor'
    ];
}

Contoh Penggunaan Umum

1. Manajemen Bisnis Regional

php
class Cabang extends Model
{
    use WithProvince;
    
    protected $fillable = [
        'name',
        'address',
        'telepon',
        'province_code'
    ];
    
    public function scopeInJava($query)
    {
        return $query->whereHas('province', function ($q) {
            $q->whereIn('code', ['31', '32', '33', '34', '35', '36']);
        });
    }
    
    public function scopeOutsideJava($query)
    {
        return $query->whereHas('province', function ($q) {
            $q->whereNotIn('code', ['31', '32', '33', '34', '35', '36']);
        });
    }
}

// Penggunaan
$cabangJawa = Cabang::inJava()->get();
$cabangLuarJawa = Cabang::outsideJava()->get();

2. Statistik Regional

php
class LaporanPenjualan extends Model
{
    use WithProvince;
    
    protected $fillable = [
        'bulan',
        'tahun',
        'total_penjualan',
        'province_code'
    ];
    
    protected $casts = [
        'total_penjualan' => 'decimal:2'
    ];
    
    public static function getProvinceRanking($tahun)
    {
        return static::with('province')
            ->where('tahun', $tahun)
            ->groupBy('province_code')
            ->selectRaw('province_code, SUM(total_penjualan) as total')
            ->orderByDesc('total')
            ->get();
    }
}

// Penggunaan
$peringkat = LaporanPenjualan::getProvinceRanking(2024);
foreach ($peringkat as $laporan) {
    echo "{$laporan->province->name}: Rp " . number_format($laporan->total);
}

3. Manajemen Acara

php
class Acara extends Model
{
    use WithProvince;
    
    protected $fillable = [
        'judul',
        'deskripsi',
        'tanggal_acara',
        'province_code'
    ];
    
    protected $casts = [
        'tanggal_acara' => 'datetime'
    ];
    
    public function getRegionalEventsAttribute()
    {
        return static::where('province_code', $this->province_code)
            ->where('id', '!=', $this->id)
            ->where('tanggal_acara', '>=', now())
            ->orderBy('tanggal_acara')
            ->limit(5)
            ->get();
    }
}

4. Zona Pengiriman

php
class ZonaPengiriman extends Model
{
    use WithProvince;
    
    protected $fillable = [
        'name',
        'biaya_dasar',
        'biaya_per_kg',
        'province_code'
    ];
    
    protected $casts = [
        'biaya_dasar' => 'decimal:2',
        'biaya_per_kg' => 'decimal:2'
    ];
    
    public function calculateShippingCost($berat)
    {
        return $this->biaya_dasar + ($this->biaya_per_kg * $berat);
    }
    
    public static function findByProvince($provinceCode)
    {
        return static::where('province_code', $provinceCode)->first();
    }
}

// Penggunaan
$zona = ZonaPengiriman::findByProvince('33'); // Jawa Tengah
$biaya = $zona->calculateShippingCost(2.5); // 2.5 kg

Kueri Lanjutan

Pengelompokan Geografis

php
// Kelompokkan berdasarkan wilayah pulau
$cabang = Cabang::with('province')
    ->get()
    ->groupBy(function ($branch) {
        $code = $branch->province->code;
        
        if (in_array($code, ['31', '32', '33', '34', '35', '36'])) {
            return 'Jawa';
        } elseif (in_array($code, ['11', '12', '13', '14', '15', '16', '17', '18', '19', '21'])) {
            return 'Sumatra';
        } elseif (in_array($code, ['61', '62', '63', '64', '65'])) {
            return 'Kalimantan';
        } elseif (in_array($code, ['71', '72', '73', '74', '75', '76'])) {
            return 'Sulawesi';
        } elseif (in_array($code, ['51', '52', '53'])) {
            return 'Bali & Nusa Tenggara';
        } elseif (in_array($code, ['81', '82', '91', '92', '93', '94', '95', '96', '97'])) {
            return 'Indonesia Timur';
        }
        
        return 'Lainnya';
    });

Analisis Statistik

php
class AnalitikProvinsi
{
    public static function getCustomerDistribution()
    {
        return Customer::with('province')
            ->groupBy('province_code')
            ->selectRaw('province_code, COUNT(*) as customer_count')
            ->orderByDesc('customer_count')
            ->get()
            ->map(function ($item) {
                return [
                    'provinsi' => $item->province->name,
                    'jumlah' => $item->customer_count,
                    'persentase' => round(($item->customer_count / Customer::count()) * 100, 2)
                ];
            });
    }
}

Validasi

Validasi Form Request

php
class RegionalOfficeRequest extends FormRequest
{
    public function rules()
    {
        return [
            'name' => 'required|string|max:255',
            'deskripsi' => 'nullable|string',
            'province_code' => 'required|exists:nusa.provinces,code'
        ];
    }
    
    public function messages()
    {
        return [
            'province_code.required' => 'Silakan pilih provinsi.',
            'province_code.exists' => 'Provinsi yang dipilih tidak valid.'
        ];
    }
}

Tips Kinerja

1. Eager Loading

php
// Baik
$kantor = KantorWilayah::with('province')->get();

// Buruk - kueri N+1
$kantor = KantorWilayah::all();
foreach ($kantor as $k) {
    echo $k->province->name; // Kueri N+1
}

2. Memilih Kolom Tertentu

php
$kantor = KantorWilayah::with(['province:code,name'])->get();

3. Caching Data Provinsi

php
class KantorWilayah extends Model
{
    use WithProvince;
    
    public function getProvinceNameAttribute()
    {
        return Cache::remember(
            "province_name_{$this->province_code}",
            3600,
            fn() => $this->province->name
        );
    }
}

Dokumentasi Terkait

Released under the MIT License.