diff --git a/database/migrations/2026_09_24_040000_seed_mutual_smep_carousel.php b/database/migrations/2026_09_24_040000_seed_mutual_smep_carousel.php new file mode 100644 index 0000000..8f686a2 --- /dev/null +++ b/database/migrations/2026_09_24_040000_seed_mutual_smep_carousel.php @@ -0,0 +1,140 @@ + */ + private const IMAGES = [ + '01-tecnologia.webp', + '02-hogar.webp', + '03-vida-activa.webp', + '04-descanso.webp', + ]; + + /** @var list */ + private array $storedPaths = []; + + public function up(): void + { + $tenant = DB::table('tenants') + ->where('codigo', self::TENANT_CODE) + ->first(['codigo', 'storefront_website_type_code']); + + if ($tenant === null) { + return; + } + + $extraId = DB::table('storefront_website_type_extras') + ->where('storefront_website_type_code', $tenant->storefront_website_type_code) + ->where('codigo', self::EXTRA_CODE) + ->value('id'); + + if ($extraId === null || DB::table('websites_extras')->where([ + 'website_code' => self::TENANT_CODE, + 'website_type_extra_id' => $extraId, + ])->exists()) { + return; + } + + try { + DB::transaction(function () use ($extraId): void { + $attachmentIds = []; + + foreach (self::IMAGES as $filename) { + $attachmentIds[] = $this->storeImage($filename); + } + + DB::table('websites_extras')->insert([ + 'website_code' => self::TENANT_CODE, + 'website_type_extra_id' => $extraId, + 'config' => json_encode($attachmentIds, JSON_THROW_ON_ERROR), + 'is_enabled' => true, + 'created_at' => now(), + 'updated_at' => now(), + ]); + }); + } catch (Throwable $throwable) { + Storage::disk('s3')->delete($this->storedPaths); + + throw $throwable; + } + } + + public function down(): void + { + $attachments = DB::table('attachments') + ->where('path', 'like', self::STORAGE_PREFIX.'%') + ->get(['id', 'path']); + + if ($attachments->isEmpty()) { + return; + } + + $attachmentIds = $attachments->pluck('id')->map(fn ($id): int => (int) $id)->all(); + $extraId = DB::table('storefront_website_type_extras') + ->where('storefront_website_type_code', 'shopit') + ->where('codigo', self::EXTRA_CODE) + ->value('id'); + + DB::transaction(function () use ($attachmentIds, $extraId): void { + if ($extraId !== null) { + DB::table('websites_extras') + ->where('website_code', self::TENANT_CODE) + ->where('website_type_extra_id', $extraId) + ->where('config', json_encode($attachmentIds, JSON_THROW_ON_ERROR)) + ->delete(); + } + + DB::table('attachments')->whereIn('id', $attachmentIds)->delete(); + }); + + Storage::disk('s3')->delete($attachments->pluck('path')->all()); + } + + private function storeImage(string $filename): int + { + $sourcePath = public_path(self::IMAGE_DIRECTORY."/{$filename}"); + + if (! is_file($sourcePath)) { + throw new RuntimeException("Carousel image not found: {$sourcePath}"); + } + + $contents = file_get_contents($sourcePath); + + if ($contents === false) { + throw new RuntimeException("Carousel image could not be read: {$sourcePath}"); + } + + $storedPath = self::STORAGE_PREFIX.$filename; + + if (! Storage::disk('s3')->put($storedPath, $contents)) { + throw new RuntimeException("Carousel image could not be stored: {$storedPath}"); + } + + $this->storedPaths[] = $storedPath; + + return (int) DB::table('attachments')->insertGetId([ + 'key' => (string) Str::uuid(), + 'path' => $storedPath, + 'filename' => $filename, + 'type' => 'image', + 'mime_type' => 'image/webp', + 'extension' => 'webp', + 'size' => strlen($contents), + 'created_at' => now(), + 'updated_at' => now(), + ]); + } +}; diff --git a/public/images/tennants/mutual_smep/carousel/01-tecnologia.webp b/public/images/tennants/mutual_smep/carousel/01-tecnologia.webp new file mode 100644 index 0000000..8759d6d Binary files /dev/null and b/public/images/tennants/mutual_smep/carousel/01-tecnologia.webp differ diff --git a/public/images/tennants/mutual_smep/carousel/02-hogar.webp b/public/images/tennants/mutual_smep/carousel/02-hogar.webp new file mode 100644 index 0000000..9b6efe2 Binary files /dev/null and b/public/images/tennants/mutual_smep/carousel/02-hogar.webp differ diff --git a/public/images/tennants/mutual_smep/carousel/03-vida-activa.webp b/public/images/tennants/mutual_smep/carousel/03-vida-activa.webp new file mode 100644 index 0000000..e7400be Binary files /dev/null and b/public/images/tennants/mutual_smep/carousel/03-vida-activa.webp differ diff --git a/public/images/tennants/mutual_smep/carousel/04-descanso.webp b/public/images/tennants/mutual_smep/carousel/04-descanso.webp new file mode 100644 index 0000000..831b8da Binary files /dev/null and b/public/images/tennants/mutual_smep/carousel/04-descanso.webp differ diff --git a/tests/Feature/Migrations/SeedMutualSmepCarouselTest.php b/tests/Feature/Migrations/SeedMutualSmepCarouselTest.php new file mode 100644 index 0000000..0f554f2 --- /dev/null +++ b/tests/Feature/Migrations/SeedMutualSmepCarouselTest.php @@ -0,0 +1,119 @@ +createSchema(); + + DB::table('tenants')->insert([ + 'codigo' => 'mutual_smep', + 'storefront_website_type_code' => 'shopit', + ]); + DB::table('storefront_website_type_extras')->insert([ + 'storefront_website_type_code' => 'shopit', + 'codigo' => 'carousel', + ]); + } + + protected function tearDown(): void + { + foreach (['websites_extras', 'attachments', 'storefront_website_type_extras', 'tenants'] as $table) { + Schema::dropIfExists($table); + } + + parent::tearDown(); + } + + public function test_it_loads_and_rolls_back_the_mutual_smep_carousel(): void + { + $migration = require database_path( + 'migrations/2026_09_24_040000_seed_mutual_smep_carousel.php' + ); + + $migration->up(); + $migration->up(); + + $this->assertDatabaseCount('attachments', 4); + $this->assertDatabaseCount('websites_extras', 1); + + $attachments = DB::table('attachments')->orderBy('filename')->get(); + $this->assertSame([ + '01-tecnologia.webp', + '02-hogar.webp', + '03-vida-activa.webp', + '04-descanso.webp', + ], $attachments->pluck('filename')->all()); + $this->assertSame( + $attachments->pluck('id')->all(), + json_decode( + DB::table('websites_extras')->value('config'), + true, + flags: JSON_THROW_ON_ERROR, + ), + ); + + foreach ($attachments as $attachment) { + $this->assertSame('image/webp', $attachment->mime_type); + $this->assertStringStartsWith( + 'tenants/mutual_smep/extras/carousel-v1/', + $attachment->path, + ); + Storage::disk('s3')->assertExists($attachment->path); + } + + $storedPaths = $attachments->pluck('path')->all(); + $migration->down(); + + $this->assertDatabaseCount('websites_extras', 0); + $this->assertDatabaseCount('attachments', 0); + + foreach ($storedPaths as $storedPath) { + Storage::disk('s3')->assertMissing($storedPath); + } + } + + private function createSchema(): void + { + Schema::create('tenants', function (Blueprint $table): void { + $table->string('codigo')->primary(); + $table->string('storefront_website_type_code'); + }); + Schema::create('storefront_website_type_extras', function (Blueprint $table): void { + $table->id(); + $table->string('storefront_website_type_code'); + $table->string('codigo'); + }); + Schema::create('attachments', function (Blueprint $table): void { + $table->id(); + $table->uuid('key')->unique(); + $table->string('path'); + $table->string('filename'); + $table->string('type'); + $table->string('mime_type'); + $table->string('extension')->nullable(); + $table->unsignedBigInteger('size')->default(0); + $table->timestamps(); + }); + Schema::create('websites_extras', function (Blueprint $table): void { + $table->id(); + $table->string('website_code'); + $table->unsignedBigInteger('website_type_extra_id'); + $table->json('config'); + $table->boolean('is_enabled')->default(true); + $table->timestamps(); + $table->unique(['website_code', 'website_type_extra_id']); + }); + } +}