Compare commits
5 Commits
c66d2019d6
...
5ada399adf
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ada399adf | |||
| aed818da6e | |||
| 018f3f6c13 | |||
| 217f86f624 | |||
| 25891cefbc |
@@ -4,14 +4,16 @@ namespace App\Domains\Menu\Controllers;
|
||||
|
||||
use App\Domains\Menu\Models\Menu;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class MenuController extends Controller
|
||||
{
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
$menues = Menu::all();
|
||||
|
||||
return response()->json($menues);
|
||||
}
|
||||
|
||||
@@ -19,10 +21,22 @@ class MenuController extends Controller
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'code' => 'required|string|unique:menues,code',
|
||||
'parent_menu_code' => [
|
||||
'nullable',
|
||||
'string',
|
||||
Rule::exists('menues', 'code'),
|
||||
'different:code',
|
||||
],
|
||||
'content_type' => [
|
||||
'sometimes',
|
||||
Rule::in([Menu::CONTENT_TYPE_STATIC, Menu::CONTENT_TYPE_DYNAMIC]),
|
||||
],
|
||||
'static_content_schema' => 'required_if:content_type,static|nullable|array',
|
||||
'route' => 'required|string',
|
||||
]);
|
||||
|
||||
$menu = Menu::create($validated);
|
||||
|
||||
return response()->json($menu, 201);
|
||||
}
|
||||
|
||||
@@ -34,17 +48,30 @@ class MenuController extends Controller
|
||||
public function update(Request $request, Menu $menu): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'code' => 'sometimes|required|string|unique:menues,code,' . $menu->id,
|
||||
'code' => 'sometimes|required|string|unique:menues,code,'.$menu->id,
|
||||
'parent_menu_code' => [
|
||||
'nullable',
|
||||
'string',
|
||||
Rule::exists('menues', 'code'),
|
||||
Rule::notIn([$menu->code]),
|
||||
],
|
||||
'content_type' => [
|
||||
'sometimes',
|
||||
Rule::in([Menu::CONTENT_TYPE_STATIC, Menu::CONTENT_TYPE_DYNAMIC]),
|
||||
],
|
||||
'static_content_schema' => 'required_if:content_type,static|nullable|array',
|
||||
'route' => 'sometimes|required|string',
|
||||
]);
|
||||
|
||||
$menu->update($validated);
|
||||
|
||||
return response()->json($menu);
|
||||
}
|
||||
|
||||
public function destroy(Menu $menu): JsonResponse
|
||||
{
|
||||
$menu->delete();
|
||||
|
||||
return response()->json(null, 204);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,28 +5,76 @@ namespace App\Domains\Menu\Models;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class Menu extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public const CONTENT_TYPE_DYNAMIC = 'dynamic';
|
||||
|
||||
public const CONTENT_TYPE_STATIC = 'static';
|
||||
|
||||
protected $table = 'menues';
|
||||
|
||||
protected $attributes = [
|
||||
'content_type' => self::CONTENT_TYPE_DYNAMIC,
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'code',
|
||||
'parent_menu_code',
|
||||
'content_type',
|
||||
'static_content_schema',
|
||||
'route',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'static_content_schema' => 'array',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saving(function (self $menu): void {
|
||||
if (
|
||||
$menu->content_type === self::CONTENT_TYPE_STATIC
|
||||
&& empty($menu->static_content_schema)
|
||||
) {
|
||||
throw ValidationException::withMessages([
|
||||
'static_content_schema' => 'El schema es obligatorio para los menús estáticos.',
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function parent(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(self::class, 'parent_menu_code', 'code');
|
||||
}
|
||||
|
||||
public function children(): HasMany
|
||||
{
|
||||
return $this->hasMany(self::class, 'parent_menu_code', 'code');
|
||||
}
|
||||
|
||||
public function tenants(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
Tenant::class,
|
||||
'tenant_menues',
|
||||
'tenants_menues',
|
||||
'menu_code',
|
||||
'tenant_codigo',
|
||||
'tenant_code',
|
||||
'code',
|
||||
'codigo'
|
||||
)->withTimestamps();
|
||||
)
|
||||
->using(TenantMenu::class)
|
||||
->withPivot('static_content')
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,36 @@
|
||||
|
||||
namespace App\Domains\Menu\Models;
|
||||
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
class TenantMenu extends Pivot
|
||||
{
|
||||
protected $table = 'tenant_menues';
|
||||
protected $table = 'tenants_menues';
|
||||
|
||||
public $incrementing = true;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_code',
|
||||
'menu_code',
|
||||
'static_content',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'static_content' => 'array',
|
||||
];
|
||||
}
|
||||
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class, 'tenant_code', 'codigo');
|
||||
}
|
||||
|
||||
public function menu(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Menu::class, 'menu_code', 'code');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
|
||||
@@ -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'])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@ class SocialMedia extends Model
|
||||
'code',
|
||||
'codigo'
|
||||
)
|
||||
->withPivot('url')
|
||||
->withTimestamps();
|
||||
->withPivot(['url', 'orden'])
|
||||
->withTimestamps()
|
||||
->orderByPivot('orden');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Domains\Tenant\Models;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Menu\Models\Menu;
|
||||
use App\Domains\Menu\Models\TenantMenu;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -107,19 +108,23 @@ class Tenant extends Model
|
||||
'codigo',
|
||||
'code'
|
||||
)
|
||||
->withPivot('url')
|
||||
->withTimestamps();
|
||||
->withPivot(['url', 'orden'])
|
||||
->withTimestamps()
|
||||
->orderByPivot('orden');
|
||||
}
|
||||
|
||||
public function menues(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
Menu::class,
|
||||
'tenant_menues',
|
||||
'tenant_codigo',
|
||||
'tenants_menues',
|
||||
'tenant_code',
|
||||
'menu_code',
|
||||
'codigo',
|
||||
'code'
|
||||
)->withTimestamps();
|
||||
)
|
||||
->using(TenantMenu::class)
|
||||
->withPivot('static_content')
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,15 @@ 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'],
|
||||
'social_media.*.orden' => ['sometimes', 'integer', 'min:0', 'distinct'],
|
||||
'hero_config' => ['nullable', 'array'],
|
||||
'hero_config.title_html' => ['nullable', 'string'],
|
||||
'hero_config.description_html' => ['nullable', 'string'],
|
||||
|
||||
@@ -74,6 +74,15 @@ 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'],
|
||||
'social_media.*.orden' => ['sometimes', 'integer', 'min:0', 'distinct'],
|
||||
'hero_config' => ['nullable', 'array'],
|
||||
'hero_config.title_html' => ['nullable', 'string'],
|
||||
'hero_config.description_html' => ['nullable', 'string'],
|
||||
|
||||
@@ -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'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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,24 @@ class TenantService
|
||||
$tenant->mainCarouselImages()->sync($attachments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{code: string, url: string, orden?: int}> $socialMedia
|
||||
*/
|
||||
private function syncSocialMedia(Tenant $tenant, array $socialMedia): void
|
||||
{
|
||||
$associations = [];
|
||||
|
||||
foreach (array_values($socialMedia) as $index => $item) {
|
||||
$associations[$item['code']] = [
|
||||
'url' => $item['url'],
|
||||
'orden' => $item['orden'] ?? $index,
|
||||
];
|
||||
}
|
||||
|
||||
$tenant->socialMedia()->sync($associations);
|
||||
$tenant->unsetRelation('socialMedia');
|
||||
}
|
||||
|
||||
private function resolveMainCarouselImage(mixed $image, int $order): Attachment
|
||||
{
|
||||
if (is_string($image) && Str::isUuid($image)) {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('tenant_social_media', function (Blueprint $table): void {
|
||||
$table->unsignedInteger('orden')->default(0)->after('url');
|
||||
$table->index(['tenant_code', 'orden']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('tenant_social_media', function (Blueprint $table): void {
|
||||
$table->dropIndex(['tenant_code', 'orden']);
|
||||
$table->dropColumn('orden');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('tenant_menues', function (Blueprint $table) {
|
||||
$table->dropForeign(['tenant_codigo']);
|
||||
$table->dropForeign(['menu_code']);
|
||||
});
|
||||
|
||||
Schema::rename('tenant_menues', 'tenants_menues');
|
||||
|
||||
Schema::table('tenants_menues', function (Blueprint $table) {
|
||||
$table->renameColumn('tenant_codigo', 'tenant_code');
|
||||
$table->json('static_content')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('menues', function (Blueprint $table) {
|
||||
$table->string('parent_menu_code')->nullable()->after('code');
|
||||
$table->enum('content_type', ['static', 'dynamic'])
|
||||
->default('dynamic')
|
||||
->after('parent_menu_code');
|
||||
$table->json('static_content_schema')->nullable()->after('content_type');
|
||||
});
|
||||
|
||||
Schema::table('menues', function (Blueprint $table) {
|
||||
$table->foreign('parent_menu_code')
|
||||
->references('code')
|
||||
->on('menues')
|
||||
->nullOnDelete();
|
||||
});
|
||||
|
||||
Schema::table('tenants_menues', function (Blueprint $table) {
|
||||
$table->foreign('tenant_code')
|
||||
->references('codigo')
|
||||
->on('tenants')
|
||||
->cascadeOnDelete();
|
||||
$table->foreign('menu_code')
|
||||
->references('code')
|
||||
->on('menues')
|
||||
->cascadeOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('tenants_menues', function (Blueprint $table) {
|
||||
$table->dropForeign(['tenant_code']);
|
||||
$table->dropForeign(['menu_code']);
|
||||
});
|
||||
|
||||
Schema::table('menues', function (Blueprint $table) {
|
||||
$table->dropForeign(['parent_menu_code']);
|
||||
$table->dropColumn([
|
||||
'parent_menu_code',
|
||||
'content_type',
|
||||
'static_content_schema',
|
||||
]);
|
||||
});
|
||||
|
||||
Schema::table('tenants_menues', function (Blueprint $table) {
|
||||
$table->dropColumn('static_content');
|
||||
$table->renameColumn('tenant_code', 'tenant_codigo');
|
||||
});
|
||||
|
||||
Schema::rename('tenants_menues', 'tenant_menues');
|
||||
|
||||
Schema::table('tenant_menues', function (Blueprint $table) {
|
||||
$table->foreign('tenant_codigo')
|
||||
->references('codigo')
|
||||
->on('tenants')
|
||||
->cascadeOnDelete();
|
||||
$table->foreign('menu_code')
|
||||
->references('code')
|
||||
->on('menues')
|
||||
->cascadeOnDelete();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -23,6 +23,7 @@ class DatabaseSeeder extends Seeder
|
||||
]);
|
||||
|
||||
$this->call([
|
||||
SocialMediaSeeder::class,
|
||||
TenantSeeder::class,
|
||||
AttributeSeeder::class,
|
||||
CategorySeeder::class,
|
||||
|
||||
42
database/seeders/SocialMediaSeeder.php
Normal file
42
database/seeders/SocialMediaSeeder.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Domains\Tenant\Models\SocialMedia;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class SocialMediaSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$socialMedia = [
|
||||
[
|
||||
'code' => 'facebook',
|
||||
'icon' => 'fa-brands fa-facebook',
|
||||
'name' => 'Facebook',
|
||||
],
|
||||
[
|
||||
'code' => 'instagram',
|
||||
'icon' => 'fa-brands fa-instagram',
|
||||
'name' => 'Instagram',
|
||||
],
|
||||
[
|
||||
'code' => 'whatsapp',
|
||||
'icon' => 'fa-brands fa-whatsapp',
|
||||
'name' => 'WhatsApp',
|
||||
],
|
||||
[
|
||||
'code' => 'linkedin',
|
||||
'icon' => 'fa-brands fa-linkedin-in',
|
||||
'name' => 'LinkedIn',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($socialMedia as $socialMediaData) {
|
||||
SocialMedia::query()->updateOrCreate(
|
||||
['code' => $socialMediaData['code']],
|
||||
$socialMediaData
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,29 @@ class TenantSeeder extends Seeder
|
||||
'06-city-runners.png',
|
||||
];
|
||||
|
||||
private const SOCIAL_MEDIA = [
|
||||
[
|
||||
'code' => 'instagram',
|
||||
'url' => 'https://www.instagram.com/sonderargentina/',
|
||||
'orden' => 0,
|
||||
],
|
||||
[
|
||||
'code' => 'facebook',
|
||||
'url' => 'https://www.facebook.com/SonderArgentina/',
|
||||
'orden' => 1,
|
||||
],
|
||||
[
|
||||
'code' => 'linkedin',
|
||||
'url' => 'https://www.linkedin.com/company/35664983/',
|
||||
'orden' => 2,
|
||||
],
|
||||
[
|
||||
'code' => 'whatsapp',
|
||||
'url' => 'https://wa.me/5493412602222',
|
||||
'orden' => 3,
|
||||
],
|
||||
];
|
||||
|
||||
public function __construct(protected TenantService $tenantService) {}
|
||||
|
||||
/**
|
||||
@@ -117,6 +140,7 @@ class TenantSeeder extends Seeder
|
||||
'header_logo' => $headerLogo,
|
||||
'footer_logo' => $footerLogo,
|
||||
'main_carousel_images' => $mainCarouselImages,
|
||||
'social_media' => self::SOCIAL_MEDIA,
|
||||
]);
|
||||
|
||||
// Check if tenant 'fiesta_futbol_infantil' already exists
|
||||
@@ -214,6 +238,7 @@ class TenantSeeder extends Seeder
|
||||
'location' => 'Sunchales, Santa Fe',
|
||||
'dates' => ['2026-10-09', '2026-10-10', '2026-10-11', '2026-10-12'],
|
||||
],
|
||||
'social_media' => self::SOCIAL_MEDIA,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
52
tests/Feature/Seeders/SocialMediaSeederTest.php
Normal file
52
tests/Feature/Seeders/SocialMediaSeederTest.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Seeders;
|
||||
|
||||
use App\Domains\Tenant\Models\SocialMedia;
|
||||
use Database\Seeders\SocialMediaSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class SocialMediaSeederTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_it_seeds_the_supported_social_media_with_font_awesome_icons(): void
|
||||
{
|
||||
$this->seed(SocialMediaSeeder::class);
|
||||
|
||||
$this->assertSame([
|
||||
[
|
||||
'code' => 'facebook',
|
||||
'icon' => 'fa-brands fa-facebook',
|
||||
'name' => 'Facebook',
|
||||
],
|
||||
[
|
||||
'code' => 'instagram',
|
||||
'icon' => 'fa-brands fa-instagram',
|
||||
'name' => 'Instagram',
|
||||
],
|
||||
[
|
||||
'code' => 'linkedin',
|
||||
'icon' => 'fa-brands fa-linkedin-in',
|
||||
'name' => 'LinkedIn',
|
||||
],
|
||||
[
|
||||
'code' => 'whatsapp',
|
||||
'icon' => 'fa-brands fa-whatsapp',
|
||||
'name' => 'WhatsApp',
|
||||
],
|
||||
], SocialMedia::query()
|
||||
->orderBy('code')
|
||||
->get(['code', 'icon', 'name'])
|
||||
->toArray());
|
||||
}
|
||||
|
||||
public function test_it_can_run_more_than_once_without_creating_duplicates(): void
|
||||
{
|
||||
$this->seed(SocialMediaSeeder::class);
|
||||
$this->seed(SocialMediaSeeder::class);
|
||||
|
||||
$this->assertSame(4, SocialMedia::query()->count());
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Tests\Feature\Seeders;
|
||||
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Database\Seeders\SocialMediaSeeder;
|
||||
use Database\Seeders\TenantSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -16,7 +17,10 @@ class TenantSeederTest extends TestCase
|
||||
{
|
||||
Storage::fake('s3');
|
||||
|
||||
$this->seed(TenantSeeder::class);
|
||||
$this->seed([
|
||||
SocialMediaSeeder::class,
|
||||
TenantSeeder::class,
|
||||
]);
|
||||
|
||||
$tenant = Tenant::query()->where('codigo', 'sonder')->firstOrFail();
|
||||
$images = $tenant->mainCarouselImages()->get();
|
||||
@@ -35,4 +39,34 @@ class TenantSeederTest extends TestCase
|
||||
Storage::disk('s3')->assertExists($image->path);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_it_assigns_social_media_to_both_tenants_in_order(): void
|
||||
{
|
||||
Storage::fake('s3');
|
||||
|
||||
$this->seed([
|
||||
SocialMediaSeeder::class,
|
||||
TenantSeeder::class,
|
||||
]);
|
||||
|
||||
$expectedCodes = ['instagram', 'facebook', 'linkedin', 'whatsapp'];
|
||||
$expectedUrls = [
|
||||
'https://www.instagram.com/sonderargentina/',
|
||||
'https://www.facebook.com/SonderArgentina/',
|
||||
'https://www.linkedin.com/company/35664983/',
|
||||
'https://wa.me/5493412602222',
|
||||
];
|
||||
|
||||
foreach (['sonder', 'fiesta_futbol_infantil'] as $tenantCode) {
|
||||
$socialMedia = Tenant::query()
|
||||
->where('codigo', $tenantCode)
|
||||
->firstOrFail()
|
||||
->socialMedia()
|
||||
->get();
|
||||
|
||||
$this->assertSame($expectedCodes, $socialMedia->pluck('code')->all());
|
||||
$this->assertSame($expectedUrls, $socialMedia->pluck('pivot.url')->all());
|
||||
$this->assertSame([0, 1, 2, 3], $socialMedia->pluck('pivot.orden')->all());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ class TenantSocialMediaTest extends TestCase
|
||||
'tenant_code',
|
||||
'social_media_code',
|
||||
'url',
|
||||
'orden',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
], Schema::getColumnListing('tenant_social_media'));
|
||||
@@ -46,6 +47,7 @@ class TenantSocialMediaTest extends TestCase
|
||||
|
||||
$tenant->socialMedia()->attach($instagram->code, [
|
||||
'url' => 'https://instagram.com/acme',
|
||||
'orden' => 3,
|
||||
]);
|
||||
|
||||
$this->assertTrue($tenant->socialMedia()->firstOrFail()->is($instagram));
|
||||
@@ -53,6 +55,7 @@ class TenantSocialMediaTest extends TestCase
|
||||
'https://instagram.com/acme',
|
||||
$tenant->socialMedia()->firstOrFail()->pivot->url
|
||||
);
|
||||
$this->assertSame(3, $tenant->socialMedia()->firstOrFail()->pivot->orden);
|
||||
$this->assertTrue($instagram->tenants()->firstOrFail()->is($tenant));
|
||||
}
|
||||
|
||||
@@ -76,6 +79,145 @@ class TenantSocialMediaTest extends TestCase
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_tenant_update_synchronizes_social_media_and_returns_pivot_urls(): void
|
||||
{
|
||||
$tenant = $this->createTenant();
|
||||
$instagram = $this->createSocialMedia('instagram', 'Instagram');
|
||||
$facebook = $this->createSocialMedia('facebook', 'Facebook');
|
||||
|
||||
$tenant->socialMedia()->attach($facebook->code, [
|
||||
'url' => 'https://facebook.com/old-acme',
|
||||
]);
|
||||
|
||||
$this->putJson("/api/tenants/{$tenant->codigo}", [
|
||||
'social_media' => [
|
||||
[
|
||||
'code' => $instagram->code,
|
||||
'url' => 'https://instagram.com/acme',
|
||||
'orden' => 4,
|
||||
],
|
||||
],
|
||||
])
|
||||
->assertOk()
|
||||
->assertJsonPath('data.social_media.0.code', 'instagram')
|
||||
->assertJsonPath('data.social_media.0.icon', 'instagram')
|
||||
->assertJsonPath('data.social_media.0.name', 'Instagram')
|
||||
->assertJsonPath('data.social_media.0.url', 'https://instagram.com/acme')
|
||||
->assertJsonCount(1, 'data.social_media');
|
||||
|
||||
$this->assertDatabaseHas('tenant_social_media', [
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'social_media_code' => $instagram->code,
|
||||
'url' => 'https://instagram.com/acme',
|
||||
'orden' => 4,
|
||||
]);
|
||||
$this->assertDatabaseMissing('tenant_social_media', [
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'social_media_code' => $facebook->code,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_tenant_social_media_are_returned_in_configured_order(): void
|
||||
{
|
||||
$tenant = $this->createTenant();
|
||||
$instagram = $this->createSocialMedia('instagram', 'Instagram');
|
||||
$facebook = $this->createSocialMedia('facebook', 'Facebook');
|
||||
|
||||
$this->putJson("/api/tenants/{$tenant->codigo}", [
|
||||
'social_media' => [
|
||||
[
|
||||
'code' => $instagram->code,
|
||||
'url' => 'https://instagram.com/acme',
|
||||
'orden' => 20,
|
||||
],
|
||||
[
|
||||
'code' => $facebook->code,
|
||||
'url' => 'https://facebook.com/acme',
|
||||
'orden' => 10,
|
||||
],
|
||||
],
|
||||
])
|
||||
->assertOk()
|
||||
->assertJsonPath('data.social_media.0.code', 'facebook')
|
||||
->assertJsonPath('data.social_media.1.code', 'instagram')
|
||||
->assertJsonMissingPath('data.social_media.0.orden')
|
||||
->assertJsonMissingPath('data.social_media.1.orden');
|
||||
}
|
||||
|
||||
public function test_tenant_store_synchronizes_social_media(): void
|
||||
{
|
||||
$instagram = $this->createSocialMedia('instagram', 'Instagram');
|
||||
$headerLogo = $this->createAttachment('new-header.png');
|
||||
$footerLogo = $this->createAttachment('new-footer.png');
|
||||
|
||||
$this->postJson('/api/tenants', [
|
||||
'codigo' => 'new-acme',
|
||||
'nombre' => 'New Acme',
|
||||
'dominio' => 'new-acme.com',
|
||||
'primary_color' => '#111111',
|
||||
'secondary_color' => '#222222',
|
||||
'danger_color' => '#333333',
|
||||
'success_color' => '#444444',
|
||||
'header_bg_color' => '#ffffff',
|
||||
'footer_bg_color' => '#ffffff',
|
||||
'header_logo' => $headerLogo->key,
|
||||
'footer_logo' => $footerLogo->key,
|
||||
'social_media' => [
|
||||
[
|
||||
'code' => $instagram->code,
|
||||
'url' => 'https://instagram.com/new-acme',
|
||||
],
|
||||
],
|
||||
])
|
||||
->assertCreated()
|
||||
->assertJsonPath('data.social_media.0.code', 'instagram')
|
||||
->assertJsonPath('data.social_media.0.url', 'https://instagram.com/new-acme');
|
||||
|
||||
$this->assertDatabaseHas('tenant_social_media', [
|
||||
'tenant_code' => 'new-acme',
|
||||
'social_media_code' => $instagram->code,
|
||||
'url' => 'https://instagram.com/new-acme',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_tenant_update_can_clear_social_media(): void
|
||||
{
|
||||
$tenant = $this->createTenant();
|
||||
$instagram = $this->createSocialMedia('instagram', 'Instagram');
|
||||
$tenant->socialMedia()->attach($instagram->code, [
|
||||
'url' => 'https://instagram.com/acme',
|
||||
]);
|
||||
|
||||
$this->putJson("/api/tenants/{$tenant->codigo}", [
|
||||
'social_media' => [],
|
||||
])
|
||||
->assertOk()
|
||||
->assertJsonPath('data.social_media', []);
|
||||
|
||||
$this->assertDatabaseMissing('tenant_social_media', [
|
||||
'tenant_code' => $tenant->codigo,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_tenant_update_validates_social_media_codes_and_urls(): void
|
||||
{
|
||||
$tenant = $this->createTenant();
|
||||
|
||||
$this->putJson("/api/tenants/{$tenant->codigo}", [
|
||||
'social_media' => [
|
||||
[
|
||||
'code' => 'unknown',
|
||||
'url' => 'not-a-url',
|
||||
],
|
||||
],
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors([
|
||||
'social_media.0.code',
|
||||
'social_media.0.url',
|
||||
]);
|
||||
}
|
||||
|
||||
private function createTenant(): Tenant
|
||||
{
|
||||
$headerLogo = $this->createAttachment('header.png');
|
||||
@@ -105,4 +247,13 @@ class TenantSocialMediaTest extends TestCase
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
}
|
||||
|
||||
private function createSocialMedia(string $code, string $name): SocialMedia
|
||||
{
|
||||
return SocialMedia::query()->create([
|
||||
'code' => $code,
|
||||
'icon' => $code,
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user