*/ public array $opening_hours = []; /** @var array */ public array $facilities = ['Parkir']; public string $opened_date = ''; public int $status = OutletStatus::OPERATIONAL->value; /** @var array> */ public array $featured_image = []; /** @var array> */ public array $images = []; public function rules(): array { return array_merge($this->getBasicRules(), $this->getOpeningHoursRules()); } private function getBasicRules(): array { return [ 'name' => ['required', 'string', 'max:100'], 'phone_number' => ['required', 'string', new PhoneNumber], 'address' => ['required', 'string'], 'landmark' => ['required', 'string', 'max:30'], 'maps_url' => [ 'nullable', 'string', 'regex:/^(https?:\/\/)?(www\.)?(google\.[a-z.]+\/maps\/.+|goo\.gl\/maps\/.+|maps\.app\.goo\.gl\/.+)$/i', ], 'opening_hours' => ['required', 'array'], 'facilities' => ['required', 'array', 'min:1'], 'facilities.*' => ['required', 'string', 'max:20'], 'opened_date' => ['required', 'date'], 'status' => ['required', Rule::in(OutletStatus::cases())], 'featured_image' => ['required', 'array', 'max:1'], 'images' => ['required', 'array', 'max:5'], ]; } private function getOpeningHoursRules(): array { $rules = []; foreach (Day::cases() as $day) { $rules["opening_hours.{$day->value}"] = ['required', 'array']; $rules["opening_hours.{$day->value}.open_time"] = ['nullable', 'date_format:H:i']; $rules["opening_hours.{$day->value}.close_time"] = ['nullable', 'date_format:H:i']; } return $rules; } public function validationAttributes(): array { return [ 'name' => 'nama', 'phone_number' => 'nomor telepon', 'address' => 'alamat', 'landmark' => 'patokan', 'maps_url' => 'tautan google maps', 'opening_hours' => 'jam beroperasi', 'opening_hours.*' => 'jam beroperasi', 'facilities' => 'fasilitas', 'facilities.*' => 'fasilitas', 'opened_date' => 'tanggal buka', 'featured_image' => 'gambar utama', 'images' => 'gambar', ]; } public function setOutlet(Outlet $outlet): void { $outlet->load(['openingHours', 'facilities']); $this->outlet = $outlet; $this->fillBasicAttributes($outlet); $this->opening_hours = $this->formatOpeningHoursForForm($outlet); $this->facilities = $outlet->facilities->pluck('name')->toArray(); $this->featured_image = $this->mapMediaCollection($outlet->getMedia('featured_image')); $this->images = $this->mapMediaCollection($outlet->getMedia('images')); } private function fillBasicAttributes(Outlet $outlet): void { $this->name = $outlet->name; $this->phone_number = $outlet->phone_number; $this->address = $outlet->address; $this->landmark = $outlet->landmark; $this->maps_url = $outlet->maps_url; $this->opened_date = $outlet->opened_date; $this->status = $outlet->status->value; } private function formatOpeningHoursForForm(Outlet $outlet): array { return $outlet->openingHours ->mapWithKeys(fn ($item) => [ $item->day->value => [ 'open_time' => $item->open_time ? formatTime($item->open_time) : null, 'close_time' => $item->close_time ? formatTime($item->close_time) : null, ], ]) ->toArray(); } public function store(): void { $this->validate(); $data = $this->prepareDataForSave(); DB::transaction(function () use ($data) { $outlet = Outlet::create($data); $this->createOpeningHours($outlet, $data['opening_hours']); $this->createFacilities($outlet, $data['facilities']); $this->handleMediaUpload($outlet); }); } private function prepareDataForSave(): array { $data = $this->all(); $data['closed_date'] = $data['status'] == OutletStatus::TERMINATED->value ? now() : null; return $data; } private function createOpeningHours(Outlet $outlet, array $openingHours): void { $hoursData = collect($openingHours) ->map(fn ($time, $day) => [ 'outlet_id' => $outlet->id, 'day' => $day, 'open_time' => $time['open_time'], 'close_time' => $time['close_time'], ]) ->values() ->toArray(); OpeningHour::insert($hoursData); } private function createFacilities(Outlet $outlet, array $facilities): void { $facilitiesData = collect($facilities) ->map(fn ($facility) => ['name' => $facility]) ->toArray(); $outlet->facilities()->createMany($facilitiesData); } private function handleMediaUpload(Outlet $outlet): void { $this->uploadMedia($this->featured_image, $outlet, 'featured_image'); $this->uploadMedia($this->images, $outlet, 'images'); } public function update(): void { $this->validate(); $data = $this->prepareDataForSave(); DB::transaction(function () use ($data) { $this->outlet->update($data); $this->updateOpeningHours($data['opening_hours']); $this->updateFacilities($data['facilities']); $this->handleMediaSync($data); $this->handleMediaUpload($this->outlet); }); } private function updateOpeningHours(array $openingHours): void { // Delete opening hours for days that are not in the new data $this->outlet->openingHours() ->whereNotIn('day', array_keys($openingHours)) ->delete(); // Add or update opening hours foreach ($openingHours as $day => $hours) { OpeningHour::updateOrCreate( [ 'outlet_id' => $this->outlet->id, 'day' => $day, ], [ 'open_time' => $hours['open_time'] ?? null, 'close_time' => $hours['close_time'] ?? null, ] ); } } private function updateFacilities(array $facilities): void { // Delete facilities that are not in the new list $this->outlet->facilities() ->whereNotIn('name', $facilities) ->delete(); // Add or update facilities foreach ($facilities as $facilityName) { $this->outlet->facilities()->updateOrCreate( ['name' => $facilityName], ['name' => $facilityName] ); } } private function handleMediaSync(array $data): void { $this->syncMedia($data['featured_image'], $this->outlet, 'featured_image'); $this->syncMedia($data['images'], $this->outlet, 'images'); } }