'png', 'image/webp' => 'webp', 'image/gif' => 'gif', default => 'jpg', }; $date = now()->format('Y/m/d'); $uuid = Str::uuid(); $key = "{$folder}/{$date}/{$uuid}/photo.{$extension}"; Storage::disk('s3')->put($key, $binary, [ 'ContentType' => $mime, 'ACL' => 'public-read', ]); $this->registerMedia( model: $model, s3Key: $key, collectionName: $collectionName, mimeType: $mime, fileSize: strlen($binary), orderColumn: 1, ); } private function registerMedia( Model $model, string $s3Key, string $collectionName, ?int $fileSize = null, ?string $mimeType = null, ?int $orderColumn = null, ?string $name = null, ): void { $fileName = pathinfo($s3Key, PATHINFO_BASENAME); $media = Media::create([ 'model_type' => $model->getMorphClass(), 'model_id' => $model->id, 'uuid' => Str::uuid(), 'collection_name' => $collectionName, 'name' => $name ?? pathinfo($s3Key, PATHINFO_FILENAME), 'file_name' => $fileName, 'mime_type' => $mimeType ?? 'image/jpeg', 'disk' => 's3', 'conversions_disk' => 's3', 'size' => $fileSize ?? 0, 'manipulations' => [], 'custom_properties' => ['s3_key' => $s3Key], 'generated_conversions' => [], 'responsive_images' => [], 'order_column' => $orderColumn ?? 1, ]); $this->generateMediaConversions($media); } private function generateMediaConversions(Media $media): void { $model = $media->model; if (! $model || ! method_exists($model, 'registerMediaConversions')) { return; } $conversions = ConversionCollection::createForMedia($media); if ($conversions->isEmpty()) { return; } Bus::dispatch(new PerformConversionsJob($conversions, $media)); } private function syncPhoto(Model $model, array $data, string $collectionName = 'photos'): void { if (! array_key_exists('photo_key', $data)) { return; } $currentKey = $model->getFirstMedia($collectionName)?->getCustomProperty('s3_key'); if ($data['photo_key'] === $currentKey) { return; } $model->clearMediaCollection($collectionName); if (! empty($data['photo_key'])) { $this->registerMedia( model: $model, s3Key: $data['photo_key'], collectionName: $collectionName, orderColumn: 1, ); } } private function syncReceipt(Model $model, ?string $newKey, string $collectionName = 'receipts', string $cachePrefix = 'receipt', ?int $fileSize = null, ?string $fileMimeType = null): void { $currentMedia = $model->getFirstMedia($collectionName); $currentKey = $currentMedia?->getCustomProperty('s3_key'); if ($newKey === $currentKey) { return; } if ($currentMedia) { Cache::forget("{$cachePrefix}_{$currentMedia->id}"); } $model->clearMediaCollection($collectionName); if (! empty($newKey)) { $this->registerMedia( model: $model, s3Key: $newKey, collectionName: $collectionName, fileSize: $fileSize, mimeType: $fileMimeType, ); } } private function registerPhoto(Model $model, string $s3Key, string $collectionName = 'images'): void { $this->registerMedia( model: $model, s3Key: $s3Key, collectionName: $collectionName, orderColumn: 1, ); } public function registerPhotos(Model $model, array $photoKeys, string $collectionName = 'images'): void { foreach ($photoKeys as $order => $s3Key) { $this->registerMedia( model: $model, s3Key: $s3Key, collectionName: $collectionName, orderColumn: $order + 1, ); } } public function syncPhotos(Model $model, array $photoKeys, string $collectionName = 'images'): void { $existingMedia = $model->getMedia($collectionName); $existingKeys = $existingMedia ->map(fn (Media $m) => $m->getCustomProperty('s3_key') ?? $m->file_name) ->toArray(); foreach ($existingMedia as $media) { $key = $media->getCustomProperty('s3_key') ?? $media->file_name; if (! in_array($key, $photoKeys)) { $media->delete(); } } $newKeys = array_values(array_diff($photoKeys, $existingKeys)); $this->registerPhotos($model, $newKeys, $collectionName); } public function getTemporaryUrls(Model $model, string $collectionName = 'images'): array { $media = $model->getMedia($collectionName); return $media->map(fn ($m) => $this->s3Service->getTemporaryUrl($m->getPath()))->toArray(); } }