clearMediaCollection($collection); } elseif ($removeIds !== null && $removeIds !== []) { $model->getMedia($collection) ->whereIn('id', $removeIds) ->each->delete(); } // Determine items to add (S3 keys take priority) $useS3 = ! empty($s3Keys); $items = $useS3 ? array_values(array_filter($s3Keys)) : array_values(array_filter($newFiles ?? [])); if ($maxFiles === 1) { $items = array_slice($items, 0, 1); } $currentCount = $model->getMedia($collection)->count(); if ($currentCount + count($items) > $maxFiles) { throw ValidationException::withMessages([ $errorKey ?? $collection => "Maksimal {$maxFiles} gambar per item.", ]); } foreach ($items as $item) { if ($useS3) { $this->registerS3Key($model, $item, $collection, $type); } else { $this->addUploadedFile($model, $item, $collection, $type); } } if ($model instanceof Model) { $model->unsetRelation('media'); } if ($required && $model->getMedia($collection)->isEmpty()) { throw ValidationException::withMessages([ $errorKey ?? $collection => 'Foto wajib diisi.', ]); } } public function addUploadedFile( HasMedia $model, UploadedFile $file, string $collection, ?string $type = null, ): Media { return $model->addMedia($file) ->withCustomProperties($this->customProperties($model, $type ?? $collection)) ->toMediaCollection($collection); } /** * @param HasMedia&InteractsWithMedia $model */ public function registerS3Key( HasMedia $model, string $s3Key, string $collection, ?string $type = null, ): Media { $disk = config('filesystems.default') === 's3' ? 's3' : config('filesystems.default', 'public'); try { return $model->addMediaFromDisk($s3Key, $disk) ->withCustomProperties($this->customProperties($model, $type ?? $collection)) ->toMediaCollection($collection); } finally { Storage::disk($disk)->delete($s3Key); } } public function addBase64Image( HasMedia $model, string $base64Photo, string $collection, ?string $type = null, string $extension = 'jpg', ): Media { $image = base64_decode( (string) preg_replace('#^data:image/\w+;base64,#i', '', $base64Photo), true, ); if ($image === false) { throw ValidationException::withMessages([ 'photo' => 'Foto tidak valid.', ]); } $filename = sprintf('%s.%s', Str::uuid(), $extension); $tempPath = 'temp/'.$filename; Storage::disk('local')->put($tempPath, $image); try { return $model->addMedia(Storage::disk('local')->path($tempPath)) ->usingFileName($filename) ->withCustomProperties($this->customProperties($model, $type ?? $collection)) ->toMediaCollection($collection); } finally { Storage::disk('local')->delete($tempPath); } } public function replaceSingleFile( HasMedia $model, UploadedFile $file, string $collection, ?string $type = null, ): Media { $model->clearMediaCollection($collection); return $this->addUploadedFile($model, $file, $collection, $type); } public function replaceSingleS3Key( HasMedia $model, string $s3Key, string $collection, ?string $type = null, ): Media { $model->clearMediaCollection($collection); return $this->registerS3Key($model, $s3Key, $collection, $type); } private function customProperties(HasMedia $model, ?string $type): array { $properties = [ 'module' => method_exists($model, 'mediaModuleName') ? $model::mediaModuleName() : 'media', ]; if ($type !== null && $type !== '') { $properties['type'] = $type; } return $properties; } }