feat(tenant): add social media functionality with validation, synchronization, and tests

This commit is contained in:
2026-07-23 15:03:57 -03:00
parent c66d2019d6
commit 25891cefbc
7 changed files with 178 additions and 7 deletions

View File

@@ -16,7 +16,7 @@ class BootstrapTenantController extends Controller
return TenantResource::make(
Tenant::query()
->with(['headerLogo', 'footerLogo', 'mainCarouselImages', 'menues'])
->with(['headerLogo', 'footerLogo', 'mainCarouselImages', 'menues', 'socialMedia'])
->where('dominio', $dominio)
->firstOrFail()
);

View File

@@ -19,7 +19,7 @@ class TenantController extends Controller
{
return TenantResource::collection(
Tenant::query()
->with(['headerLogo', 'footerLogo', 'mainCarouselImages'])
->with(['headerLogo', 'footerLogo', 'mainCarouselImages', 'socialMedia'])
->latest()
->paginateFromRequest()
)->response();
@@ -30,14 +30,14 @@ class TenantController extends Controller
$tenant = $this->tenantService->create($request->validated());
return TenantResource::make(
$tenant->loadMissing(['headerLogo', 'footerLogo', 'mainCarouselImages'])
$tenant->loadMissing(['headerLogo', 'footerLogo', 'mainCarouselImages', 'socialMedia'])
)->response()->setStatusCode(201);
}
public function show(Tenant $tenant): TenantResource
{
return TenantResource::make(
$tenant->loadMissing(['headerLogo', 'footerLogo', 'mainCarouselImages'])
$tenant->loadMissing(['headerLogo', 'footerLogo', 'mainCarouselImages', 'socialMedia'])
);
}
@@ -46,7 +46,7 @@ class TenantController extends Controller
$tenant = $this->tenantService->update($tenant, $request->validated());
return TenantResource::make(
$tenant->loadMissing(['headerLogo', 'footerLogo', 'mainCarouselImages'])
$tenant->loadMissing(['headerLogo', 'footerLogo', 'mainCarouselImages', 'socialMedia'])
);
}

View File

@@ -63,6 +63,14 @@ class StoreTenantRequest extends FormRequest
'hero_bg_image' => ['nullable', new ImageOrBase64Rule],
'main_carousel_images' => ['sometimes', 'array'],
'main_carousel_images.*' => ['required', 'distinct', new ImageOrBase64Rule],
'social_media' => ['sometimes', 'array'],
'social_media.*.code' => [
'required',
'string',
'distinct',
Rule::exists('social_media', 'code'),
],
'social_media.*.url' => ['required', 'url', 'max:2048'],
'hero_config' => ['nullable', 'array'],
'hero_config.title_html' => ['nullable', 'string'],
'hero_config.description_html' => ['nullable', 'string'],

View File

@@ -74,6 +74,14 @@ class UpdateTenantRequest extends FormRequest
'hero_bg_image' => ['nullable', new ImageOrBase64Rule],
'main_carousel_images' => ['sometimes', 'array'],
'main_carousel_images.*' => ['required', 'distinct', new ImageOrBase64Rule],
'social_media' => ['sometimes', 'array'],
'social_media.*.code' => [
'required',
'string',
'distinct',
Rule::exists('social_media', 'code'),
],
'social_media.*.url' => ['required', 'url', 'max:2048'],
'hero_config' => ['nullable', 'array'],
'hero_config.title_html' => ['nullable', 'string'],
'hero_config.description_html' => ['nullable', 'string'],

View File

@@ -44,6 +44,17 @@ class TenantResource extends JsonResource
->map(fn ($attachment) => $attachment->getTemporaryUrl(1440))
->values()
),
'social_media' => $this->whenLoaded(
'socialMedia',
fn () => $this->socialMedia
->map(fn ($socialMedia) => [
'code' => $socialMedia->code,
'icon' => $socialMedia->icon,
'name' => $socialMedia->name,
'url' => $socialMedia->pivot->url,
])
->values()
),
'menues' => $this->whenLoaded('menues'),
];
}

View File

@@ -26,12 +26,14 @@ class TenantService
$footerLogo = $data['footer_logo'] ?? null;
$heroBgImage = $data['hero_bg_image'] ?? null;
$mainCarouselImages = $data['main_carousel_images'] ?? [];
$socialMedia = $data['social_media'] ?? [];
unset(
$data['header_logo'],
$data['footer_logo'],
$data['hero_bg_image'],
$data['main_carousel_images']
$data['main_carousel_images'],
$data['social_media']
);
$headerAttachmentId = null;
@@ -74,6 +76,7 @@ class TenantService
/** @var Tenant $tenant */
$tenant = Tenant::query()->create($data);
$this->syncMainCarouselImages($tenant, $mainCarouselImages);
$this->syncSocialMedia($tenant, $socialMedia);
return $tenant;
});
@@ -91,16 +94,19 @@ class TenantService
$hasFooterLogoKey = array_key_exists('footer_logo', $data);
$hasHeroBgImageKey = array_key_exists('hero_bg_image', $data);
$hasMainCarouselImagesKey = array_key_exists('main_carousel_images', $data);
$hasSocialMediaKey = array_key_exists('social_media', $data);
$headerLogo = $data['header_logo'] ?? null;
$footerLogo = $data['footer_logo'] ?? null;
$heroBgImage = $data['hero_bg_image'] ?? null;
$mainCarouselImages = $data['main_carousel_images'] ?? [];
$socialMedia = $data['social_media'] ?? [];
unset(
$data['header_logo'],
$data['footer_logo'],
$data['hero_bg_image'],
$data['main_carousel_images']
$data['main_carousel_images'],
$data['social_media']
);
$oldHeroBgId = $tenant->hero_bg_image_id;
@@ -167,6 +173,10 @@ class TenantService
$this->syncMainCarouselImages($tenant, $mainCarouselImages);
}
if ($hasSocialMediaKey) {
$this->syncSocialMedia($tenant, $socialMedia);
}
return $tenant;
});
}
@@ -186,6 +196,21 @@ class TenantService
$tenant->mainCarouselImages()->sync($attachments);
}
/**
* @param array<int, array{code: string, url: string}> $socialMedia
*/
private function syncSocialMedia(Tenant $tenant, array $socialMedia): void
{
$associations = [];
foreach ($socialMedia as $item) {
$associations[$item['code']] = ['url' => $item['url']];
}
$tenant->socialMedia()->sync($associations);
$tenant->unsetRelation('socialMedia');
}
private function resolveMainCarouselImage(mixed $image, int $order): Attachment
{
if (is_string($image) && Str::isUuid($image)) {