Refactor attachment cropping functionality to support multiple variants

- Introduced AttachmentCrop model to manage crop variants for attachments.
- Updated Attachment model to remove direct crop fields and establish relationships with AttachmentCrop.
- Modified AttachmentService to handle storing and updating multiple crop variants (desktop and mobile).
- Adjusted validation rules to accommodate new crop structure.
- Updated database migration to create attachment_crops table and migrate existing crop data.
- Refactored tests to ensure compatibility with the new cropping structure and validate multiple crop variants.
- Enhanced documentation to reflect changes in attachment handling and cropping capabilities.
This commit is contained in:
2026-08-18 14:40:22 -03:00
parent c00b3d609c
commit e619c51ac9
14 changed files with 526 additions and 150 deletions

View File

@@ -3,6 +3,7 @@
namespace App\Domains\Tenant\Resources\AdminApp;
use App\Domains\Attachable\Models\Attachment;
use App\Domains\Attachable\Models\AttachmentCrop;
use App\Domains\Tenant\Models\WebsiteExtra;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
@@ -49,10 +50,19 @@ class WebsiteExtraResource extends JsonResource
$attachment = $config['background_image_id'];
$fullRange = ['start_percentage' => 0.0, 'end_percentage' => 100.0];
$crops = $attachment->cropVariants->keyBy('variant');
$config['background_image_id'] = [
'url' => $attachment->getTemporaryUrl(1440),
'crop_horizontal' => $attachment->crop_horizontal ?? $fullRange,
'crop_vertical' => $attachment->crop_vertical ?? $fullRange,
'crops' => collect(AttachmentCrop::VARIANTS)->mapWithKeys(
function (string $variant) use ($crops, $fullRange): array {
$crop = $crops->get($variant);
return [$variant => [
'crop_horizontal' => $crop?->crop_horizontal ?? $fullRange,
'crop_vertical' => $crop?->crop_vertical ?? $fullRange,
]];
}
)->all(),
];
return $config;

View File

@@ -3,6 +3,7 @@
namespace App\Domains\Tenant\Resources\AdminApp;
use App\Domains\Attachable\Models\Attachment;
use App\Domains\Attachable\Models\AttachmentCrop;
use App\Domains\Tenant\Models\Tenant;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
@@ -70,16 +71,25 @@ class WebsiteExtrasResource extends JsonResource
}
/**
* @return array{url: string, crop_horizontal: array<string, float>, crop_vertical: array<string, float>}
* @return array{url: string, crops: array<string, array<string, array<string, float>>>}
*/
private function formatHeroAttachment(Attachment $attachment): array
{
$fullRange = ['start_percentage' => 0.0, 'end_percentage' => 100.0];
$crops = $attachment->cropVariants->keyBy('variant');
return [
'url' => $attachment->getTemporaryUrl(1440),
'crop_horizontal' => $attachment->crop_horizontal ?? $fullRange,
'crop_vertical' => $attachment->crop_vertical ?? $fullRange,
'crops' => collect(AttachmentCrop::VARIANTS)->mapWithKeys(
function (string $variant) use ($crops, $fullRange): array {
$crop = $crops->get($variant);
return [$variant => [
'crop_horizontal' => $crop?->crop_horizontal ?? $fullRange,
'crop_vertical' => $crop?->crop_vertical ?? $fullRange,
]];
}
)->all(),
];
}

View File

@@ -3,6 +3,7 @@
namespace App\Domains\Tenant\Resources;
use App\Domains\Attachable\Models\Attachment;
use App\Domains\Attachable\Models\AttachmentCrop;
use App\Domains\Catalog\Models\Category;
use App\Domains\Menu\Models\Menu;
use App\Domains\Tenant\Models\Tenant;
@@ -98,7 +99,18 @@ class TenantResource extends JsonResource
private function formatExtraConfig(mixed $value): mixed
{
if ($value instanceof Attachment) {
return ($value->croppedAttachment ?? $value)->getTemporaryUrl(1440);
$crops = $value->cropVariants->keyBy('variant');
$desktop = $crops->get(AttachmentCrop::DESKTOP)?->croppedAttachment ?? $value;
$mobile = $crops->get(AttachmentCrop::MOBILE)?->croppedAttachment ?? $desktop;
if ($crops->isEmpty()) {
return $value->getTemporaryUrl(1440);
}
return [
'desktop' => $desktop->getTemporaryUrl(1440),
'mobile' => $mobile->getTemporaryUrl(1440),
];
}
if (! is_array($value)) {

View File

@@ -81,7 +81,7 @@ class TenantInformationService
$attachments = Attachment::query()
->whereIn('id', $attachmentIds)
->with('croppedAttachment')
->with('cropVariants.croppedAttachment')
->get()
->keyBy('id');

View File

@@ -334,8 +334,7 @@ class WebsiteExtraService
);
}
$cropHorizontal = is_array($value) ? $value['crop_horizontal'] ?? null : null;
$cropVertical = is_array($value) ? $value['crop_vertical'] ?? null : null;
$crops = $this->cropVariants($value);
$image = is_array($value) ? $value['image'] ?? null : $value;
if (is_string($image) && Str::isUuid($image)) {
@@ -349,19 +348,17 @@ class WebsiteExtraService
]);
}
if (is_array($cropHorizontal) && is_array($cropVertical)) {
$attachment = $this->attachmentService->updateImageCrop(
if ($crops !== null) {
$attachment = $this->attachmentService->updateImageCropVariants(
$attachment,
$cropHorizontal,
$cropVertical,
$crops,
);
}
} elseif (is_array($cropHorizontal) && is_array($cropVertical)) {
$attachment = $this->attachmentService->storeCroppedImage(
} elseif ($crops !== null) {
$attachment = $this->attachmentService->storeCroppedImageVariants(
$image,
"tenants/{$tenant->codigo}/extras/{$definition->codigo}",
$cropHorizontal,
$cropVertical,
$crops,
);
} else {
$attachment = $this->attachmentService->store(
@@ -386,4 +383,37 @@ class WebsiteExtraService
return $attachment->id;
}
/**
* Normalize the current variants payload and the original single-crop contract.
*
* @return array<string, array<string, mixed>>|null
*/
private function cropVariants(mixed $value): ?array
{
if (! is_array($value)) {
return null;
}
if (isset($value['crops']) && is_array($value['crops'])) {
return $value['crops'];
}
$horizontal = $value['crop_horizontal'] ?? null;
$vertical = $value['crop_vertical'] ?? null;
if (! is_array($horizontal) || ! is_array($vertical)) {
return null;
}
$crop = [
'crop_horizontal' => $horizontal,
'crop_vertical' => $vertical,
];
return [
'desktop' => $crop,
'mobile' => $crop,
];
}
}