feat: implement full CRUD support and database schema for multi-tenant architecture

This commit is contained in:
2026-06-26 16:57:59 -03:00
parent 8708cea122
commit 31b65eb676
10 changed files with 562 additions and 58 deletions

View File

@@ -37,7 +37,7 @@ class StoreTenantRequest extends FormRequest
public function rules(): array
{
$logoRule = [
'nullable',
'required',
static function (string $attribute, mixed $value, Closure $fail): void {
if ($value instanceof UploadedFile) {
$mime = $value->getMimeType();
@@ -92,15 +92,15 @@ class StoreTenantRequest extends FormRequest
$fail("The {$attribute} field must contain a valid domain or URL.");
}
},
'nullable',
'required',
'string',
'max:255',
Rule::unique('tenants', 'dominio'),
],
'primary_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'secondary_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'danger_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'header_footer_bg_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'primary_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'secondary_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'danger_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'header_footer_bg_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
'header_logo' => $logoRule,
'footer_logo' => $logoRule,
];

View File

@@ -21,15 +21,17 @@ class UpdateTenantRequest extends FormRequest
protected function prepareForValidation(): void
{
$rawDomain = $this->input('dominio');
$normalizedDomain = TenantDomainNormalizer::normalize($rawDomain);
if ($this->has('dominio')) {
$rawDomain = $this->input('dominio');
$normalizedDomain = TenantDomainNormalizer::normalize($rawDomain);
$this->hasInvalidDomain = TenantDomainNormalizer::hasValue($rawDomain)
&& $normalizedDomain === null;
$this->hasInvalidDomain = TenantDomainNormalizer::hasValue($rawDomain)
&& $normalizedDomain === null;
$this->merge([
'dominio' => $normalizedDomain,
]);
$this->merge([
'dominio' => $normalizedDomain,
]);
}
}
/**
@@ -88,12 +90,12 @@ class UpdateTenantRequest extends FormRequest
return [
'codigo' => [
'sometimes',
'nullable',
'string',
'max:255',
Rule::unique('tenants', 'codigo')->ignore($tenant?->id),
],
'nombre' => ['sometimes', 'string', 'max:255'],
'nombre' => ['nullable', 'string', 'max:255'],
'dominio' => [
'bail',
function (string $attribute, mixed $value, Closure $fail): void {

View File

@@ -24,8 +24,9 @@ class TenantResource extends JsonResource
'secondary_color' => $this->secondary_color,
'danger_color' => $this->danger_color,
'header_footer_bg_color' => $this->header_footer_bg_color,
'header_logo' => $this->headerLogo?->getTemporaryUrl(),
'footer_logo' => $this->footerLogo?->getTemporaryUrl(),
// 1 day
'header_logo' => $this->headerLogo?->getTemporaryUrl(1440),
'footer_logo' => $this->footerLogo?->getTemporaryUrl(1440 ),
];
}
}

View File

@@ -27,33 +27,40 @@ class TenantService
unset($data['header_logo'], $data['footer_logo']);
/** @var Tenant $tenant */
$tenant = Tenant::query()->create($data);
$headerAttachmentId = null;
if ($headerLogo) {
$attachment = Str::isUuid($headerLogo)
? \App\Domains\Attachable\Models\Attachment::query()->where('key', $headerLogo)->first()
: $this->attachmentService->store($headerLogo, 'tenants');
if ($attachment) {
$tenant->header_logo_id = $attachment->id;
$tenant->attachments()->attach($attachment->id);
$headerAttachmentId = $attachment->id;
}
}
$footerAttachmentId = null;
if ($footerLogo) {
$attachment = Str::isUuid($footerLogo)
? \App\Domains\Attachable\Models\Attachment::query()->where('key', $footerLogo)->first()
: $this->attachmentService->store($footerLogo, 'tenants');
if ($attachment) {
$tenant->footer_logo_id = $attachment->id;
$tenant->attachments()->attach($attachment->id);
$footerAttachmentId = $attachment->id;
}
}
if ($headerLogo || $footerLogo) {
$tenant->save();
$data['header_logo_id'] = $headerAttachmentId;
$data['footer_logo_id'] = $footerAttachmentId;
/** @var Tenant $tenant */
$tenant = Tenant::query()->create($data);
if ($headerAttachmentId) {
$tenant->attachments()->attach($headerAttachmentId);
}
if ($footerAttachmentId) {
$tenant->attachments()->attach($footerAttachmentId);
}
return $tenant;