Skip to content

Trait WithAddress

Trait WithAddress menambahkan relasi alamat polimorfik tunggal ke model Anda, memungkinkan mereka untuk memiliki satu alamat terkait dengan data wilayah administratif Indonesia yang lengkap.

Namespace

php
Creasi\Nusa\Models\Concerns\WithAddress

Penggunaan

Implementasi Dasar

php
namespace App\Models;

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

class User extends Model
{
    use WithAddress;
    
    protected $fillable = [
        'name',
        'email'
    ];
}

Pengaturan Database

Trait ini menggunakan tabel alamat bawaan Laravel Nusa. Pastikan Anda telah mempublikasikan dan menjalankan migrasi:

bash
php artisan vendor:publish --tag=creasi-migrations
php artisan migrate

Fitur

Relasi Alamat Tunggal

Mengakses alamat yang terkait:

php
$user = User::find(1);
$address = $user->address;

if ($address) {
    echo "Address: {$address->address_line}";
    echo "Village: {$address->village->name}";
    echo "District: {$address->district->name}";
    echo "Regency: {$address->regency->name}";
    echo "Province: {$address->province->name}";
    echo "Postal Code: {$address->postal_code}";
}

Membuat Alamat

php
$user = User::find(1);

$user->address()->create([
    'address_line' => 'Jl. Merdeka No. 123',
    'province_code' => '33',
    'regency_code' => '33.74',
    'district_code' => '33.74.01',
    'village_code' => '33.74.01.1001',
    'postal_code' => '50711'
]);

Memperbarui Alamat

php
$user = User::find(1);

if ($user->address) {
    $user->address->update([
        'address_line' => 'Jl. Sudirman No. 456',
        'village_code' => '33.74.02.1005'
    ]);
} else {
    $user->address()->create([
        'address_line' => 'Jl. Sudirman No. 456',
        'province_code' => '33',
        'regency_code' => '33.74',
        'district_code' => '33.74.02',
        'village_code' => '33.74.02.1005'
    ]);
}

Contoh Penggunaan Umum

1. Profil Pengguna

php
class User extends Model
{
    use WithAddress;
    
    protected $fillable = ['name', 'email'];
    
    public function getFullAddressAttribute()
    {
        if (!$this->address) return null;
        
        return collect([
            $this->address->address_line,
            $this->address->village->name,
            $this->address->district->name,
            $this->address->regency->name,
            $this->address->province->name,
            $this->address->postal_code
        ])->filter()->implode(', ');
    }
    
    public function hasCompleteAddress()
    {
        return $this->address && 
               $this->address->address_line && 
               $this->address->village_code;
    }
}

// Penggunaan
$user = User::with('address.village.district.regency.province')->first();
echo $user->full_address;

2. Manajemen Pelanggan

php
class Customer extends Model
{
    use WithAddress;
    
    protected $fillable = [
        'name',
        'email',
        'phone'
    ];
    
    public function scopeInProvince($query, $provinceCode)
    {
        return $query->whereHas('address.province', function ($q) use ($provinceCode) {
            $q->where('code', $provinceCode);
        });
    }
    
    public function scopeInRegency($query, $regencyCode)
    {
        return $query->whereHas('address.regency', function ($q) use ($regencyCode) {
            $q->where('code', $regencyCode);
        });
    }
    
    public function getShippingZoneAttribute()
    {
        if (!$this->address) return null;
        
        $provinceCode = $this->address->province_code;
        
        return match($provinceCode) {
            '31', '32', '33', '34', '35', '36' => 'Java',
            '11', '12', '13', '14', '15', '16', '17', '18', '19', '21' => 'Sumatra',
            '51', '52', '53' => 'Bali & Nusa Tenggara',
            default => 'Outer Islands'
        };
    }
}

// Penggunaan
$jakartaCustomers = Customer::inProvince('31')->get();
$shippingZone = $customer->shipping_zone;

3. Lokasi Bisnis

php
class Store extends Model
{
    use WithAddress;
    
    protected $fillable = [
        'name',
        'description',
        'phone'
    ];
    
    public function scopeNearby($query, $provinceCode, $regencyCode = null)
    {
        return $query->whereHas('address', function ($q) use ($provinceCode, $regencyCode) {
            $q->where('province_code', $provinceCode);
            
            if ($regencyCode) {
                $q->where('regency_code', $regencyCode);
            }
        });
    }
    
    public function getOperatingHoursAttribute()
    {
        // Different operating hours based on location
        if (!$this->address) return '09:00 - 21:00';
        
        $provinceCode = $this->address->province_code;
        
        // Jakarta stores open later
        if ($provinceCode === '31') {
            return '10:00 - 22:00';
        }
        
        return '09:00 - 21:00';
    }
}

4. Manajemen Acara

php
class Event extends Model
{
    use WithAddress;
    
    protected $fillable = [
        'title',
        'description',
        'event_date',
        'max_participants'
    ];
    
    protected $casts = [
        'event_date' => 'datetime'
    ];
    
    public function getLocationNameAttribute()
    {
        if (!$this->address) return 'Location TBA';
        
        return collect([
            $this->address->regency->name,
            $this->address->province->name
        ])->implode(', ');
    }
    
    public function scopeUpcoming($query)
    {
        return $query->where('event_date', '>=', now());
    }
    
    public function scopeInLocation($query, $provinceCode, $regencyCode = null)
    {
        return $query->whereHas('address', function ($q) use ($provinceCode, $regencyCode) {
            $q->where('province_code', $provinceCode);
            
            if ($regencyCode) {
                $q->where('regency_code', $regencyCode);
            }
        });
    }
}

// Penggunaan
$upcomingEvents = Event::upcoming()
    ->inLocation('33', '33.74') // Semarang
    ->with('address.regency.province')
    ->get();

Penggunaan Lanjutan

Validasi Alamat

php
class User extends Model
{
    use WithAddress;
    
    public function updateAddress(array $addressData)
    {
        // Validate administrative hierarchy
        $village = Village::with(['district.regency.province'])
            ->where('code', $addressData['village_code'])
            ->first();
            
        if (!$village) {
            throw new \InvalidArgumentException('Invalid village code');
        }
        
        // Auto-fill parent codes
        $addressData['district_code'] = $village->district_code;
        $addressData['regency_code'] = $village->regency_code;
        $addressData['province_code'] = $village->province_code;
        
        if ($this->address) {
            $this->address->update($addressData);
        } else {
            $this->address()->create($addressData);
        }
        
        return $this->fresh('address');
    }
}

Operasi Alamat Massal

php
class AddressService
{
    public static function updateUserAddresses(array $userData)
    {
        foreach ($userData as $data) {
            $user = User::find($data['user_id']);
            
            if ($user) {
                $user->address()->updateOrCreate(
                    ['addressable_id' => $user->id, 'addressable_type' => User::class],
                    $data['address']
                );
            }
        }
    }
    
    public static function getUsersByRegion($provinceCode, $regencyCode = null)
    {
        return User::whereHas('address', function ($query) use ($provinceCode, $regencyCode) {
            $query->where('province_code', $provinceCode);
            
            if ($regencyCode) {
                $query->where('regency_code', $regencyCode);
            }
        })->with('address.village.district.regency.province')->get();
    }
}

Integrasi Formulir

Komponen Formulir Alamat

php
class AddressController extends Controller
{
    public function update(Request $request, User $user)
    {
        $request->validate([
            'address_line' => 'required|string|max:255',
            'village_code' => 'required|exists:nusa.villages,code',
            'postal_code' => 'nullable|string|size:5'
        ]);
        
        $village = Village::with(['district.regency.province'])
            ->where('code', $request->village_code)
            ->first();
        
        $addressData = [
            'address_line' => $request->address_line,
            'village_code' => $village->code,
            'district_code' => $village->district_code,
            'regency_code' => $village->regency_code,
            'province_code' => $village->province_code,
            'postal_code' => $request->postal_code ?? $village->postal_code
        ];
        
        if ($user->address) {
            $user->address->update($addressData);
        } else {
            $user->address()->create($addressData);
        }
        
        return redirect()->back()->with('success', 'Address updated successfully');
    }
}

Tips Kinerja

1. Eager Loading

php
// Baik
$users = User::with('address.village.district.regency.province')->get();

// Buruk - Kueri N+1
$users = User::all();
foreach ($users as $user) {
    echo $user->address->village->name; // Multiple queries
}

2. Memuat Kolom Tertentu

php
$users = User::with([
    'address:id,addressable_id,addressable_type,address_line,village_code',
    'address.village:code,name,district_code',
    'address.village.district:code,name,regency_code',
    'address.village.district.regency:code,name,province_code',
    'address.village.district.regency.province:code,name'
])->get();

Dokumentasi Terkait

Released under the MIT License.