Trait WithAddresses
Trait WithAddresses menambahkan beberapa relasi alamat polimorfik ke model Anda, memungkinkan mereka untuk memiliki beberapa alamat terkait dengan data wilayah administratif Indonesia yang lengkap.
Namespace
php
Creasi\Nusa\Models\Concerns\WithAddressesPenggunaan
Implementasi Dasar
php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Creasi\Nusa\Models\Concerns\WithAddresses;
class Company extends Model
{
use WithAddresses;
protected $fillable = [
'name',
'description'
];
}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 migrateFitur
Relasi Alamat Ganda
Mengakses semua alamat yang terkait:
php
$company = Company::find(1);
// Headquarters
$company->addresses()->create([
'type' => 'headquarters',
'address_line' => 'Jl. Sudirman No. 123',
'province_code' => '31',
'regency_code' => '31.71',
'district_code' => '31.71.01',
'village_code' => '31.71.01.1001'
]);
// Branch office
$company->addresses()->create([
'type' => 'branch',
'address_line' => 'Jl. Malioboro No. 456',
'province_code' => '34',
'regency_code' => '34.71',
'district_code' => '34.71.02',
'village_code' => '34.71.02.1005'
]);
// Warehouse
$company->addresses()->create([
'type' => 'warehouse',
'address_line' => 'Jl. Industri No. 789',
'province_code' => '33',
'regency_code' => '33.74',
'district_code' => '33.74.05',
'village_code' => '33.74.05.1010'
]);Contoh Penggunaan Umum
1. Bisnis Multi-Lokasi
php
class Company extends Model
{
use WithAddresses;
protected $fillable = ['name', 'description'];
public function getHeadquartersAttribute()
{
return $this->addresses()->where('type', 'headquarters')->first();
}
public function getBranchesAttribute()
{
return $this->addresses()->where('type', 'branch')->get();
}
public function getWarehousesAttribute()
{
return $this->addresses()->where('type', 'warehouse')->get();
}
public function scopeWithLocationsIn($query, $provinceCode)
{
return $query->whereHas('addresses', function ($q) use ($provinceCode) {
$q->where('province_code', $provinceCode);
});
}
public function getLocationCountAttribute()
{
return $this->addresses()->count();
}
public function getCoverageAreasAttribute()
{
return $this->addresses()
->with('province')
->get()
->pluck('province.name')
->unique()
->values();
}
}
// Usage
$company = Company::with(['addresses.village.district.regency.province'])->first();
echo "Headquarters: " . $company->headquarters?->address_line;
echo "Branches: " . $company->branches->count();
echo "Coverage: " . $company->coverage_areas->implode(', ');2. Manajemen Pelanggan
php
class Customer extends Model
{
use WithAddresses;
protected $fillable = [
'name',
'email',
'phone'
];
public function getHomeAddressAttribute()
{
return $this->addresses()->where('type', 'home')->first();
}
public function getOfficeAddressAttribute()
{
return $this->addresses()->where('type', 'office')->first();
}
public function getShippingAddressesAttribute()
{
return $this->addresses()->where('type', 'shipping')->get();
}
public function addShippingAddress(array $addressData)
{
return $this->addresses()->create(array_merge($addressData, [
'type' => 'shipping'
]));
}
public function getPreferredShippingAddressAttribute()
{
return $this->addresses()
->where('type', 'shipping')
->where('is_default', true)
->first() ?? $this->home_address;
}
public function setDefaultShippingAddress($addressId)
{
// Remove default from all shipping addresses
$this->addresses()
->where('type', 'shipping')
->update(['is_default' => false]);
// Set new default
return $this->addresses()
->where('id', $addressId)
->where('type', 'shipping')
->update(['is_default' => true]);
}
}
// Usage
$customer = Customer::first();
$customer->addShippingAddress([
'address_line' => 'Jl. Delivery No. 123',
'village_code' => '33.74.01.1001',
'district_code' => '33.74.01',
'regency_code' => '33.74',
'province_code' => '33'
]);
$shippingAddress = $customer->preferred_shipping_address;3. Manajemen Acara
php
class Event extends Model
{
use WithAddresses;
protected $fillable = [
'title',
'description',
'event_date'
];
protected $casts = [
'event_date' => 'datetime'
];
public function getVenuesAttribute()
{
return $this->addresses()->where('type', 'venue')->get();
}
public function getAccommodationsAttribute()
{
return $this->addresses()->where('type', 'accommodation')->get();
}
public function addVenue(array $venueData)
{
return $this->addresses()->create(array_merge($venueData, [
'type' => 'venue'
]));
}
public function addAccommodation(array $accommodationData)
{
return $this->addresses()->create(array_merge($accommodationData, [
'type' => 'accommodation'
]));
}
public function getLocationSummaryAttribute()
{
$venues = $this->venues;
if ($venues->isEmpty()) {
return 'Venues TBA';
}
$cities = $venues->map(function ($venue) {
return $venue->regency->name;
})->unique();
return $cities->count() === 1
? $cities->first()
: $cities->count() . ' cities';
}
}4. Manajemen Logistik
php
class LogisticsProvider extends Model
{
use WithAddresses;
protected $fillable = [
'name',
'service_type'
];
public function getWarehousesAttribute()
{
return $this->addresses()->where('type', 'warehouse')->get();
}
public function getDistributionCentersAttribute()
{
return $this->addresses()->where('type', 'distribution_center')->get();
}
public function getServiceAreasAttribute()
{
return $this->addresses()
->with('province')
->get()
->groupBy('province.name')
->map(function ($addresses, $provinceName) {
return [
'province' => $provinceName,
'locations' => $addresses->count(),
'types' => $addresses->pluck('type')->unique()->values()
];
});
}
public function canServeLocation($provinceCode, $regencyCode = null)
{
$query = $this->addresses()->where('province_code', $provinceCode);
if ($regencyCode) {
$query->where('regency_code', $regencyCode);
}
return $query->exists();
}
public function getNearestFacility($provinceCode, $regencyCode, $type = null)
{
$query = $this->addresses()
->where('province_code', $provinceCode)
->where('regency_code', $regencyCode);
if ($type) {
$query->where('type', $type);
}
return $query->first();
}
}
// Usage
$provider = LogisticsProvider::first();
$canServe = $provider->canServeLocation('33', '33.74');
$warehouse = $provider->getNearestFacility('33', '33.74', 'warehouse');
$serviceAreas = $provider->service_areas;Penggunaan Lanjutan
Manajemen Tipe Alamat
php
class AddressableModel extends Model
{
use WithAddresses;
const ADDRESS_TYPES = [
'home' => 'Home Address',
'office' => 'Office Address',
'shipping' => 'Shipping Address',
'billing' => 'Billing Address',
'warehouse' => 'Warehouse',
'branch' => 'Branch Office',
'headquarters' => 'Headquarters'
];
public function getAddressesByTypeAttribute()
{
return $this->addresses->groupBy('type');
}
public function getAddressTypesAttribute()
{
return $this->addresses->pluck('type')->unique()->values();
}
public function hasAddressType($type)
{
return $this->addresses()->where('type', $type)->exists();
}
public function getAddressByType($type)
{
return $this->addresses()->where('type', $type)->get();
}
public function removeAddressType($type)
{
return $this->addresses()->where('type', $type)->delete();
}
}Operasi Alamat Massal
php
class AddressManager
{
public static function bulkCreateAddresses($model, array $addressesData)
{
$addresses = collect($addressesData)->map(function ($data) use ($model) {
return array_merge($data, [
'addressable_id' => $model->id,
'addressable_type' => get_class($model),
'created_at' => now(),
'updated_at' => now()
]);
});
return \DB::table('addresses')->insert($addresses->toArray());
}
public static function syncAddresses($model, array $addressesData)
{
// Delete existing addresses
$model->addresses()->delete();
// Create new addresses
return static::bulkCreateAddresses($model, $addressesData);
}
public static function getLocationStatistics($modelClass)
{
return $modelClass::with('addresses.province')
->get()
->flatMap(function ($model) {
return $model->addresses;
})
->groupBy('province.name')
->map(function ($addresses, $provinceName) {
return [
'province' => $provinceName,
'count' => $addresses->count(),
'types' => $addresses->pluck('type')->unique()->values()
];
});
}
}Validasi
Validasi Alamat Ganda
php
class CompanyAddressRequest extends FormRequest
{
public function rules()
{
return [
'addresses' => 'required|array|min:1',
'addresses.*.type' => 'required|string|in:headquarters,branch,warehouse',
'addresses.*.address_line' => 'required|string|max:255',
'addresses.*.village_code' => 'required|exists:nusa.villages,code',
'addresses.*.postal_code' => 'nullable|string|size:5'
];
}
public function messages()
{
return [
'addresses.*.type.in' => 'Address type must be headquarters, branch, or warehouse.',
'addresses.*.village_code.exists' => 'The selected village is invalid.'
];
}
}Validasi Tipe Alamat Unik
php
class CustomerAddressRequest extends FormRequest
{
public function rules()
{
$customerId = $this->route('customer')->id ?? null;
return [
'type' => [
'required',
'string',
Rule::unique('addresses')
->where('addressable_type', Customer::class)
->where('addressable_id', $customerId)
->ignore($this->address)
],
'address_line' => 'required|string|max:255',
'village_code' => 'required|exists:nusa.villages,code'
];
}
}Tips Kinerja
1. Eager Loading
php
// Good
$companies = Company::with([
'addresses.village.district.regency.province'
])->get();
// Bad - N+1 queries
$companies = Company::all();
foreach ($companies as $company) {
foreach ($company->addresses as $address) {
echo $address->village->name; // Multiple queries
}
}2. Memuat Berdasarkan Tipe secara Selektif
php
$companies = Company::with([
'addresses' => function ($query) {
$query->where('type', 'headquarters')
->with('village.regency.province');
}
])->get();3. Menghitung Alamat
php
$companies = Company::withCount([
'addresses',
'addresses as branches_count' => function ($query) {
$query->where('type', 'branch');
},
'addresses as warehouses_count' => function ($query) {
$query->where('type', 'warehouse');
}
])->get();Dokumentasi Terkait
- Trait WithAddress - Untuk dukungan alamat tunggal
- Model Address - Dokumentasi lengkap model Address
- Panduan Manajemen Alamat - Panduan lengkap fungsionalitas alamat
- Contoh Formulir Alamat - Membangun formulir alamat