Skip to content

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\WithAddresses

Penggunaan ​

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 migrate

Fitur ​

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 ​

Released under the MIT License.