Pengelolaan Foto Wisata & Galeri Otomatis
Mengatur foto sampul utama dan foto galeri pendukung agar tampil jernih, rapi, dan cepat dimuat saat dibuka dari handphone.
Mengatur foto sampul utama dan foto galeri pendukung agar tampil jernih, rapi, dan cepat dimuat saat dibuka dari handphone.
Tantangan & Kebutuhan Pengguna
Setiap destinasi membutuhkan satu foto utama (`hero`) sebagai sampul katalog dan hingga 8 foto `gallery` dengan urutan tampil yang rapi. Apabila logika penyimpanan dan penghapusan file gambar diletakkan di masing-masing controller (publik dan admin), kode akan terduplikasi dan berisiko meninggalkan file yatim (*orphan files*) di ruang penyimpanan server saat foto diganti atau destinasi dihapus.
Konstrain foreign key di database hanya membersihkan baris tabel; sinkronisasi berkas fisik di dalam disk storage harus dikendalikan secara eksplisit melalui lapisan Service.
Solusi & Cara Kerja
Operasi berkas gambar dienkapsulasi sepenuhnya di dalam `PhotoService` yang di-inject melalui constructor ke `DestinationService` dan `SubmissionService`. Pada level model `Destination`, relasi `photos` dipecah menjadi relasi spesifik `hero()` (`hasOne` dengan klausa `where('type', 'hero')`) dan `gallery()` (`hasMany` dengan klausa `where('type', 'gallery')`). Saat foto Hero diperbarui, `PhotoService::storeHero` menghapus berkas lama dari disk `public` beserta record-nya sebelum menyimpan berkas baru.
Berikut adalah implementasi `app/Services/PhotoService.php` beserta definisi relasi terpisah di `app/Models/Destination.php`:
Detail & Hal yang Diperhatikan
- Penomoran urutan galeri inkremental: Saat admin menambahkan foto galeri baru pada halaman edit, `storeGallery` menghitung jumlah galeri eksisting (`$startOrder`) sehingga indeks `order` foto baru menyambung rapi tanpa menimpa urutan foto lama.
- Eager loading selektif yang hemat memori: Dengan memisahkan relasi `hero()` dari `gallery()`, halaman beranda dan katalog cukup memanggil `with(['hero', 'category'])` tanpa perlu memuat seluruh array foto galeri milik setiap destinasi.
- Pembersihan berkas menyeluruh sebelum penghapusan entitas: `DestinationService::delete()` memanggil `$this->photoService->deleteAll($destination)` terlebih dahulu sebelum `$destination->delete()` agar seluruh file fisik di `storage/app/public/destinations` ikut terhapus.
Di sisi klien, formulir pengajuan dilengkapi pratinjau gambar instan berbasis `FileReader.readAsDataURL()` sehingga pengguna dapat memastikan foto sampul dan galeri yang dipilih sudah tepat sebelum dikirim ke server.