feat(tenant): add site title and favicon support for tenants and website types
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('website_type', function (Blueprint $table): void {
|
||||
$table->string('site_title')->nullable()->after('scanner_domain');
|
||||
$table->unsignedBigInteger('favicon_id')->nullable()->after('footer_logo');
|
||||
|
||||
$table->foreign('favicon_id')
|
||||
->references('id')
|
||||
->on('attachments')
|
||||
->nullOnDelete();
|
||||
});
|
||||
|
||||
Schema::table('tenants', function (Blueprint $table): void {
|
||||
$table->string('site_title')->nullable()->after('dominio');
|
||||
$table->unsignedBigInteger('favicon_id')->nullable()->after('footer_logo_id');
|
||||
|
||||
$table->foreign('favicon_id')
|
||||
->references('id')
|
||||
->on('attachments')
|
||||
->nullOnDelete();
|
||||
});
|
||||
|
||||
DB::table('website_type')
|
||||
->whereNull('site_title')
|
||||
->update(['site_title' => DB::raw('nombre')]);
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('tenants', function (Blueprint $table): void {
|
||||
if (Schema::getConnection()->getDriverName() !== 'sqlite') {
|
||||
$table->dropForeign(['favicon_id']);
|
||||
}
|
||||
|
||||
$table->dropColumn(['site_title', 'favicon_id']);
|
||||
});
|
||||
|
||||
Schema::table('website_type', function (Blueprint $table): void {
|
||||
if (Schema::getConnection()->getDriverName() !== 'sqlite') {
|
||||
$table->dropForeign(['favicon_id']);
|
||||
}
|
||||
|
||||
$table->dropColumn(['site_title', 'favicon_id']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/** @var array<string, array{title: string, image: string, filename: string}> */
|
||||
private const BRANDING = [
|
||||
'fiesta_futbol_infantil' => [
|
||||
'title' => 'Fiesta Fútbol Infantil',
|
||||
'image' => 'images/tennants/fiesta_futbol_infantil/futbol_infantil_favicon.png',
|
||||
'filename' => 'futbol_infantil_favicon.png',
|
||||
],
|
||||
'desfile_pura_tendencia' => [
|
||||
'title' => 'Desfile Pura Tendencia',
|
||||
'image' => 'images/tennants/desfile_pura_tendencia/pura_tendencia_favicon.png',
|
||||
'filename' => 'pura_tendencia_favicon.png',
|
||||
],
|
||||
];
|
||||
|
||||
/** @var list<string> */
|
||||
private array $storedPaths = [];
|
||||
|
||||
public function up(): void
|
||||
{
|
||||
try {
|
||||
DB::transaction(function (): void {
|
||||
foreach (self::BRANDING as $tenantCode => $branding) {
|
||||
$tenant = DB::table('tenants')
|
||||
->where('codigo', $tenantCode)
|
||||
->first(['id', 'favicon_id']);
|
||||
|
||||
if ($tenant === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$faviconId = $tenant->favicon_id;
|
||||
$currentFilename = $faviconId === null
|
||||
? null
|
||||
: DB::table('attachments')->where('id', $faviconId)->value('filename');
|
||||
|
||||
if ($currentFilename !== $branding['filename']) {
|
||||
$faviconId = $this->storeFavicon(
|
||||
$tenantCode,
|
||||
$branding['image'],
|
||||
$branding['filename'],
|
||||
);
|
||||
}
|
||||
|
||||
DB::table('tenants')
|
||||
->where('id', $tenant->id)
|
||||
->update([
|
||||
'site_title' => $branding['title'],
|
||||
'favicon_id' => $faviconId,
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
});
|
||||
} catch (Throwable $throwable) {
|
||||
Storage::disk('s3')->delete($this->storedPaths);
|
||||
|
||||
throw $throwable;
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
foreach (self::BRANDING as $tenantCode => $branding) {
|
||||
$tenant = DB::table('tenants')
|
||||
->where('codigo', $tenantCode)
|
||||
->first(['id', 'site_title', 'favicon_id']);
|
||||
|
||||
if ($tenant === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$favicon = $tenant->favicon_id === null
|
||||
? null
|
||||
: DB::table('attachments')->where('id', $tenant->favicon_id)->first();
|
||||
|
||||
DB::table('tenants')
|
||||
->where('id', $tenant->id)
|
||||
->update([
|
||||
'site_title' => $tenant->site_title === $branding['title'] ? null : $tenant->site_title,
|
||||
'favicon_id' => $favicon?->filename === $branding['filename'] ? null : $tenant->favicon_id,
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
if ($favicon?->filename === $branding['filename']) {
|
||||
Storage::disk('s3')->delete($favicon->path);
|
||||
DB::table('attachments')->where('id', $favicon->id)->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function storeFavicon(string $tenantCode, string $relativePath, string $filename): int
|
||||
{
|
||||
$sourcePath = public_path($relativePath);
|
||||
|
||||
if (! is_file($sourcePath)) {
|
||||
throw new RuntimeException("Favicon not found at path: {$sourcePath}");
|
||||
}
|
||||
|
||||
$contents = file_get_contents($sourcePath);
|
||||
|
||||
if ($contents === false) {
|
||||
throw new RuntimeException("Could not read favicon at path: {$sourcePath}");
|
||||
}
|
||||
|
||||
$key = (string) Str::uuid();
|
||||
$storedPath = "tenants/{$tenantCode}/{$key}.png";
|
||||
|
||||
if (! Storage::disk('s3')->put($storedPath, $contents)) {
|
||||
throw new RuntimeException("Could not store favicon at path: {$storedPath}");
|
||||
}
|
||||
|
||||
$this->storedPaths[] = $storedPath;
|
||||
|
||||
return DB::table('attachments')->insertGetId([
|
||||
'key' => $key,
|
||||
'path' => $storedPath,
|
||||
'filename' => $filename,
|
||||
'type' => 'image',
|
||||
'mime_type' => 'image/png',
|
||||
'extension' => 'png',
|
||||
'size' => strlen($contents),
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
};
|
||||
@@ -104,6 +104,7 @@ class TenantSeeder extends Seeder
|
||||
'codigo' => 'fiesta_futbol_infantil',
|
||||
'nombre' => 'Fiesta Fútbol Infantil',
|
||||
'dominio' => $fiestaDomain,
|
||||
'site_title' => 'Fiesta Fútbol Infantil',
|
||||
'primary_color' => '#00973F',
|
||||
'secondary_color' => '#A0A0A0',
|
||||
'danger_color' => '#FF8888',
|
||||
@@ -121,6 +122,10 @@ class TenantSeeder extends Seeder
|
||||
'images/tennants/fiesta_futbol_infantil/futbol_infantil_footer.png',
|
||||
'futbol_infantil_footer.png',
|
||||
),
|
||||
'favicon' => $this->uploadedImage(
|
||||
'images/tennants/fiesta_futbol_infantil/futbol_infantil_favicon.png',
|
||||
'futbol_infantil_favicon.png',
|
||||
),
|
||||
'social_media' => self::SOCIAL_MEDIA,
|
||||
'website_type_code' => 'onticket',
|
||||
'extras' => [
|
||||
@@ -229,6 +234,18 @@ class TenantSeeder extends Seeder
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$tenant->favicon
|
||||
&& $tenant->favicon_id !== $tenant->header_logo_id
|
||||
&& $tenant->favicon_id !== $tenant->footer_logo_id
|
||||
) {
|
||||
try {
|
||||
$attachmentService->delete($tenant->favicon);
|
||||
} catch (Throwable) {
|
||||
// Ignore cleanup errors while recreating demo data.
|
||||
}
|
||||
}
|
||||
|
||||
$tenant->delete();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ class WebsiteTypeSeeder extends Seeder
|
||||
'nombre' => 'ShopIt',
|
||||
'dominio' => 'localhost',
|
||||
'scanner_domain' => 'scanner.localhost',
|
||||
'site_title' => 'ShopIt',
|
||||
...self::PRESENTATION,
|
||||
'site_logo' => $this->onTicketLogo(),
|
||||
'footer_logo' => $this->onTicketFooterLogo(),
|
||||
@@ -65,6 +66,7 @@ class WebsiteTypeSeeder extends Seeder
|
||||
'nombre' => 'OnTicket',
|
||||
'dominio' => 'onticket.localhost',
|
||||
'scanner_domain' => 'scanner.onticket.localhost',
|
||||
'site_title' => 'OnTicket',
|
||||
...self::PRESENTATION,
|
||||
'site_logo' => $this->onTicketLogo(),
|
||||
'footer_logo' => $this->onTicketFooterLogo(),
|
||||
|
||||
Reference in New Issue
Block a user