Files
2026-07-27 16:24:09 +07:00

204 lines
6.4 KiB
PHP

<?php
namespace App\Services\Wilayah;
use App\Models\WilayahDesa;
use App\Models\WilayahKabupaten;
use App\Models\WilayahKecamatan;
use App\Models\WilayahProvinsi;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\ValidationException;
class WilayahService
{
private const MODELS = [
'provinsi' => WilayahProvinsi::class,
'kabupaten' => WilayahKabupaten::class,
'kecamatan' => WilayahKecamatan::class,
'desa' => WilayahDesa::class,
];
private const PARENTS = [
'kabupaten' => 'provinsi_id',
'kecamatan' => 'kabupaten_id',
'desa' => 'kecamatan_id',
];
private const RELATIONS = [
'provinsi' => [],
'kabupaten' => ['provinsi'],
'kecamatan' => ['kabupaten.provinsi'],
'desa' => ['kecamatan.kabupaten.provinsi'],
];
public function list(string $tingkat, array $filters = [])
{
$model = $this->model($tingkat);
return $model::query()
->with(self::RELATIONS[$tingkat])
->when($filters['search'] ?? null, function ($query, $search) {
$value = mb_strtolower($search);
$query->where(function ($scope) use ($value) {
$scope->whereRaw('LOWER(kode) LIKE ?', ["%{$value}%"])
->orWhereRaw('LOWER(nama) LIKE ?', ["%{$value}%"]);
});
})
->when(
isset(self::PARENTS[$tingkat]) && ! empty($filters[self::PARENTS[$tingkat]]),
fn ($query) => $query->where(
self::PARENTS[$tingkat],
$filters[self::PARENTS[$tingkat]]
)
)
->orderBy(
$filters['sort_by'] ?? 'nama',
$filters['sort_direction'] ?? 'asc'
)
->paginate($filters['per_page'] ?? 10);
}
public function options(string $tingkat, array $filters = [], ?int $parentId = null)
{
$model = $this->model($tingkat);
$parentColumn = self::PARENTS[$tingkat] ?? null;
return $model::query()
->select(['id', 'kode', 'nama'])
->when(
$parentColumn && $parentId,
fn ($query) => $query->where($parentColumn, $parentId)
)
->when($filters['search'] ?? null, function ($query, $search) {
$value = mb_strtolower($search);
$query->where(function ($scope) use ($value) {
$scope->whereRaw('LOWER(kode) LIKE ?', ["%{$value}%"])
->orWhereRaw('LOWER(nama) LIKE ?', ["%{$value}%"]);
});
})
->orderBy('nama')
->get();
}
public function create(string $tingkat, array $data): Model
{
$model = $this->model($tingkat);
return $model::create($this->payload($tingkat, $data))
->load(self::RELATIONS[$tingkat]);
}
public function find(string $tingkat, int $id): Model
{
$model = $this->model($tingkat);
return $model::with(self::RELATIONS[$tingkat])->findOrFail($id);
}
public function update(string $tingkat, int $id, array $data): Model
{
$wilayah = $this->find($tingkat, $id);
$wilayah->update($this->payload($tingkat, $data));
return $wilayah->fresh(self::RELATIONS[$tingkat]);
}
public function delete(string $tingkat, int $id): void
{
$wilayah = $this->find($tingkat, $id);
$childRelation = match ($tingkat) {
'provinsi' => 'kabupaten',
'kabupaten' => 'kecamatan',
'kecamatan' => 'desa',
default => null,
};
if ($childRelation && $wilayah->{$childRelation}()->exists()) {
throw ValidationException::withMessages([
'wilayah' => "Wilayah {$tingkat} tidak dapat dihapus karena masih memiliki data di bawahnya.",
]);
}
$wilayah->delete();
}
public function alamatLengkap(array $filters): array
{
if (! empty($filters['desa_id'])) {
$desa = WilayahDesa::with('kecamatan.kabupaten.provinsi')->findOrFail($filters['desa_id']);
return $this->addressPayload(
$desa->kecamatan?->kabupaten?->provinsi,
$desa->kecamatan?->kabupaten,
$desa->kecamatan,
$desa
);
}
if (! empty($filters['kecamatan_id'])) {
$kecamatan = WilayahKecamatan::with('kabupaten.provinsi')->findOrFail($filters['kecamatan_id']);
return $this->addressPayload(
$kecamatan->kabupaten?->provinsi,
$kecamatan->kabupaten,
$kecamatan
);
}
if (! empty($filters['kabupaten_id'])) {
$kabupaten = WilayahKabupaten::with('provinsi')->findOrFail($filters['kabupaten_id']);
return $this->addressPayload($kabupaten->provinsi, $kabupaten);
}
$provinsi = WilayahProvinsi::findOrFail($filters['provinsi_id']);
return $this->addressPayload($provinsi);
}
private function model(string $tingkat): string
{
abort_unless(isset(self::MODELS[$tingkat]), 404, 'Tingkat wilayah tidak ditemukan.');
return self::MODELS[$tingkat];
}
private function payload(string $tingkat, array $data): array
{
$fields = ['kode', 'nama'];
if (isset(self::PARENTS[$tingkat])) {
$fields[] = self::PARENTS[$tingkat];
}
return collect($data)->only($fields)->all();
}
private function addressPayload(
?WilayahProvinsi $provinsi,
?WilayahKabupaten $kabupaten = null,
?WilayahKecamatan $kecamatan = null,
?WilayahDesa $desa = null
): array {
$node = fn ($item) => $item ? [
'id' => $item->id,
'kode' => $item->kode,
'nama' => $item->nama,
] : null;
$parts = array_filter([
$desa ? "Desa/Kelurahan {$desa->nama}" : null,
$kecamatan ? "Kecamatan {$kecamatan->nama}" : null,
$kabupaten?->nama,
$provinsi ? $provinsi->nama : null,
]);
return [
'desa' => $node($desa),
'kecamatan' => $node($kecamatan),
'kabupaten' => $node($kabupaten),
'provinsi' => $node($provinsi),
'alamat_wilayah' => implode(', ', $parts),
];
}
}