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
- Trait WithAddresses - Untuk dukungan alamat ganda
- Model Address - Dokumentasi lengkap model Address
- Panduan Manajemen Alamat - Panduan lengkap fungsionalitas alamat
- Contoh Formulir Alamat - Membangun formulir alamat