feat(address): implement Address and TenantAddress models with relationships
feat(migration): create addresses and tenant_addresses tables feat(migration): add data migration for copying contact menu addresses test: add ContactAddressesTest for address and tenant address functionality
This commit is contained in:
29
app/Domains/Core/Address/Models/Address.php
Normal file
29
app/Domains/Core/Address/Models/Address.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Core\Address\Models;
|
||||
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class Address extends Model
|
||||
{
|
||||
protected $fillable = ['label', 'address_text', 'latitude', 'longitude'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'latitude' => 'decimal:7',
|
||||
'longitude' => 'decimal:7',
|
||||
];
|
||||
}
|
||||
|
||||
/** @return BelongsToMany<Tenant, $this> */
|
||||
public function tenants(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Tenant::class, 'tenant_addresses', 'address_id', 'tenant_codigo', 'id', 'codigo')
|
||||
->using(TenantAddress::class)
|
||||
->withPivot(['id', 'is_main'])
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
33
app/Domains/Core/Address/Models/TenantAddress.php
Normal file
33
app/Domains/Core/Address/Models/TenantAddress.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Core\Address\Models;
|
||||
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
class TenantAddress extends Pivot
|
||||
{
|
||||
protected $table = 'tenant_addresses';
|
||||
|
||||
public $incrementing = true;
|
||||
|
||||
protected $fillable = ['tenant_codigo', 'address_id', 'is_main'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return ['is_main' => 'boolean'];
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Tenant, $this> */
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class, 'tenant_codigo', 'codigo');
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Address, $this> */
|
||||
public function address(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Address::class);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ use App\Domains\Commerce\Catalog\Enums\GroupLayout;
|
||||
use App\Domains\Commerce\Catalog\Enums\ProductLayout;
|
||||
use App\Domains\Commerce\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Commerce\Catalog\Models\Category;
|
||||
use App\Domains\Core\Address\Models\Address;
|
||||
use App\Domains\Core\Address\Models\TenantAddress;
|
||||
use App\Domains\Core\Client\Models\Client;
|
||||
use App\Domains\Core\Menu\Models\Menu;
|
||||
use App\Domains\Core\Menu\Models\TenantMenu;
|
||||
@@ -22,6 +24,7 @@ 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\Database\Eloquent\Relations\HasOneThrough;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
#[Fillable([
|
||||
@@ -308,6 +311,22 @@ class Tenant extends Model
|
||||
return $this->hasMany(WebsiteExtra::class, 'website_code', 'codigo');
|
||||
}
|
||||
|
||||
/** @return BelongsToMany<Address, $this> */
|
||||
public function addresses(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Address::class, 'tenant_addresses', 'tenant_codigo', 'address_id', 'codigo', 'id')
|
||||
->using(TenantAddress::class)
|
||||
->withPivot(['id', 'is_main'])
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
/** @return HasOneThrough<Address, TenantAddress, $this> */
|
||||
public function mainAddress(): HasOneThrough
|
||||
{
|
||||
return $this->hasOneThrough(Address::class, TenantAddress::class, 'tenant_codigo', 'id', 'codigo', 'address_id')
|
||||
->where('tenant_addresses.is_main', true);
|
||||
}
|
||||
|
||||
public function menues(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
|
||||
26
app/Domains/Core/Tenant/Services/TenantAddressService.php
Normal file
26
app/Domains/Core/Tenant/Services/TenantAddressService.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Core\Tenant\Services;
|
||||
|
||||
use App\Domains\Core\Address\Models\Address;
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TenantAddressService
|
||||
{
|
||||
public function setMainAddress(Tenant $tenant, Address $address): void
|
||||
{
|
||||
DB::transaction(function () use ($tenant, $address): void {
|
||||
// Serialize changes for this tenant, including when it has no principal address yet.
|
||||
Tenant::query()->where('codigo', $tenant->codigo)->lockForUpdate()->firstOrFail();
|
||||
$tenant->addresses()->where('addresses.id', $address->id)->firstOrFail();
|
||||
|
||||
DB::table('tenant_addresses')->where('tenant_codigo', $tenant->codigo)
|
||||
->where('is_main', true)->update(['is_main' => false, 'updated_at' => now()]);
|
||||
$tenant->addresses()->updateExistingPivot($address->id, ['is_main' => true]);
|
||||
});
|
||||
|
||||
$tenant->unsetRelation('addresses');
|
||||
$tenant->unsetRelation('mainAddress');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?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
|
||||
{
|
||||
$driver = DB::connection()->getDriverName();
|
||||
|
||||
if (! in_array($driver, ['mysql', 'mariadb', 'sqlite', 'pgsql', 'sqlsrv'], true)) {
|
||||
throw new RuntimeException("Unsupported address database driver: {$driver}");
|
||||
}
|
||||
|
||||
Schema::create('addresses', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->string('label');
|
||||
$table->text('address_text');
|
||||
$table->decimal('latitude', 10, 7)->nullable();
|
||||
$table->decimal('longitude', 10, 7)->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('tenant_addresses', function (Blueprint $table) use ($driver): void {
|
||||
$table->id();
|
||||
$table->string('tenant_codigo');
|
||||
$table->foreignId('address_id')->constrained('addresses')->cascadeOnDelete();
|
||||
$table->boolean('is_main')->default(false);
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('tenant_codigo')->references('codigo')->on('tenants')->cascadeOnDelete();
|
||||
$table->unique(['tenant_codigo', 'address_id']);
|
||||
|
||||
if (in_array($driver, ['mysql', 'mariadb'], true)) {
|
||||
// NULL allows many secondary addresses, while a tenant code can occur only once.
|
||||
$table->string('main_tenant_codigo')->nullable()
|
||||
->virtualAs('CASE WHEN is_main = 1 THEN tenant_codigo ELSE NULL END');
|
||||
$table->unique('main_tenant_codigo', 'tenant_addresses_one_main_per_tenant');
|
||||
}
|
||||
});
|
||||
|
||||
if (! in_array($driver, ['mysql', 'mariadb'], true)) {
|
||||
$condition = $driver === 'pgsql' ? 'is_main = true' : 'is_main = 1';
|
||||
DB::statement('CREATE UNIQUE INDEX tenant_addresses_one_main_per_tenant '
|
||||
."ON tenant_addresses (tenant_codigo) WHERE {$condition}");
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('tenant_addresses');
|
||||
Schema::dropIfExists('addresses');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
DB::transaction(function (): void {
|
||||
DB::table('tenants_menues')
|
||||
->where('menu_code', 'help.contact')
|
||||
->whereNotNull('static_content')
|
||||
->orderBy('id')
|
||||
->chunkById(100, function ($menus): void {
|
||||
foreach ($menus as $menu) {
|
||||
$content = json_decode($menu->static_content, true, flags: JSON_THROW_ON_ERROR);
|
||||
|
||||
if ($content === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_array($content) || ! is_array($content['locations'] ?? [])) {
|
||||
throw new RuntimeException("Invalid contact locations for tenant {$menu->tenant_code}");
|
||||
}
|
||||
|
||||
foreach ($content['locations'] ?? [] as $location) {
|
||||
if (! is_array($location) || ! is_array($location['addresses'] ?? [])) {
|
||||
throw new RuntimeException("Invalid contact addresses for tenant {$menu->tenant_code}");
|
||||
}
|
||||
|
||||
foreach ($location['addresses'] ?? [] as $address) {
|
||||
$this->copyAddress($menu->tenant_code, $location, $address);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private function copyAddress(string $tenantCode, array $location, mixed $address): void
|
||||
{
|
||||
if (! is_array($address) || ! is_string($address['address'] ?? null)
|
||||
|| trim($address['address']) === '') {
|
||||
throw new RuntimeException("Invalid contact address text for tenant {$tenantCode}");
|
||||
}
|
||||
|
||||
$coordinates = $address['coordinates'] ?? null;
|
||||
|
||||
if ($coordinates !== null && (! is_array($coordinates) || count($coordinates) !== 2
|
||||
|| ! is_numeric($coordinates[0] ?? null) || ! is_numeric($coordinates[1] ?? null)
|
||||
|| ! is_finite((float) $coordinates[0]) || ! is_finite((float) $coordinates[1])
|
||||
|| abs((float) $coordinates[0]) > 90 || abs((float) $coordinates[1]) > 180)) {
|
||||
throw new RuntimeException("Invalid contact coordinates for tenant {$tenantCode}");
|
||||
}
|
||||
|
||||
$label = $address['label'] ?? $location['label'] ?? 'Dirección';
|
||||
|
||||
if (! is_string($label) || trim($label) === '' || mb_strlen($label) > 255) {
|
||||
throw new RuntimeException("Invalid contact address label for tenant {$tenantCode}");
|
||||
}
|
||||
|
||||
$values = [
|
||||
'label' => $label,
|
||||
'address_text' => $address['address'],
|
||||
'latitude' => $coordinates === null ? null : number_format((float) $coordinates[0], 7, '.', ''),
|
||||
'longitude' => $coordinates === null ? null : number_format((float) $coordinates[1], 7, '.', ''),
|
||||
];
|
||||
|
||||
// Reuse only within this tenant; equal legacy addresses in different tenants stay independent.
|
||||
$existing = DB::table('addresses')
|
||||
->join('tenant_addresses', 'tenant_addresses.address_id', '=', 'addresses.id')
|
||||
->where('tenant_addresses.tenant_codigo', $tenantCode)
|
||||
->where(array_combine(
|
||||
array_map(fn (string $field): string => 'addresses.'.$field, array_keys($values)),
|
||||
array_values($values),
|
||||
))
|
||||
->first(['addresses.id', 'tenant_addresses.id as link_id']);
|
||||
|
||||
if ($existing !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$now = now();
|
||||
$isMain = ! DB::table('tenant_addresses')
|
||||
->where('tenant_codigo', $tenantCode)
|
||||
->where('is_main', true)
|
||||
->exists();
|
||||
$addressId = DB::table('addresses')->insertGetId([
|
||||
...$values,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
DB::table('tenant_addresses')->insert([
|
||||
'tenant_codigo' => $tenantCode,
|
||||
'address_id' => $addressId,
|
||||
'is_main' => $isMain,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
// Keep copies: they may already have been edited or reused. Legacy menus were never changed.
|
||||
}
|
||||
};
|
||||
229
tests/Feature/Migrations/ContactAddressesTest.php
Normal file
229
tests/Feature/Migrations/ContactAddressesTest.php
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Migrations;
|
||||
|
||||
use App\Domains\Core\Address\Models\Address;
|
||||
use App\Domains\Core\Address\Models\TenantAddress;
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use App\Domains\Core\Tenant\Services\TenantAddressService;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ContactAddressesTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Schema::create('tenants', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->string('codigo')->unique();
|
||||
$table->string('address')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
Schema::create('tenants_menues', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->string('tenant_code');
|
||||
$table->string('menu_code');
|
||||
$table->json('static_content')->nullable();
|
||||
$table->boolean('is_enabled')->default(true);
|
||||
});
|
||||
DB::table('tenants')->insert([
|
||||
['codigo' => 'alpha', 'address' => 'Dirección anterior'],
|
||||
['codigo' => 'beta', 'address' => null],
|
||||
['codigo' => 'empty', 'address' => null],
|
||||
]);
|
||||
|
||||
$this->schemaMigration()->up();
|
||||
}
|
||||
|
||||
public function test_it_copies_all_contact_locations_without_changing_legacy_data_and_can_run_again(): void
|
||||
{
|
||||
$sharedAddress = [
|
||||
'label' => 'Sede principal',
|
||||
'address' => 'San Lorenzo 1543, Rosario',
|
||||
'coordinates' => [-32.9431184, -60.6437991],
|
||||
];
|
||||
$this->contact('alpha', [
|
||||
'rosario' => ['label' => 'Rosario', 'addresses' => [$sharedAddress]],
|
||||
'santa_fe' => ['label' => 'Santa Fe', 'addresses' => [
|
||||
['label' => 'Sucursal', 'address' => 'San Martín 100', 'coordinates' => [0, 0]],
|
||||
['label' => 'Sin mapa', 'address' => 'San Martín 200'],
|
||||
]],
|
||||
], enabled: false);
|
||||
$this->contact('beta', ['rosario' => ['addresses' => [$sharedAddress]]]);
|
||||
$this->contact('empty', []);
|
||||
DB::table('tenants_menues')->insert([
|
||||
'tenant_code' => 'empty',
|
||||
'menu_code' => 'help.faq',
|
||||
'static_content' => json_encode(['locations' => [['addresses' => [$sharedAddress]]]]),
|
||||
]);
|
||||
$oldMenus = DB::table('tenants_menues')->orderBy('id')->get()->toArray();
|
||||
|
||||
$migration = $this->dataMigration();
|
||||
$migration->up();
|
||||
$migration->up();
|
||||
|
||||
$this->assertDatabaseCount('addresses', 4);
|
||||
$this->assertDatabaseCount('tenant_addresses', 4);
|
||||
$this->assertDatabaseHas('addresses', [
|
||||
'label' => 'Sede principal',
|
||||
'address_text' => 'San Lorenzo 1543, Rosario',
|
||||
'latitude' => -32.9431184,
|
||||
'longitude' => -60.6437991,
|
||||
]);
|
||||
$this->assertDatabaseHas('addresses', ['label' => 'Sucursal', 'latitude' => 0, 'longitude' => 0]);
|
||||
$this->assertDatabaseHas('addresses', ['label' => 'Sin mapa', 'latitude' => null, 'longitude' => null]);
|
||||
$this->assertEquals($oldMenus, DB::table('tenants_menues')->orderBy('id')->get()->toArray());
|
||||
$this->assertDatabaseHas('tenants', ['codigo' => 'alpha', 'address' => 'Dirección anterior']);
|
||||
|
||||
$alpha = Tenant::query()->where('codigo', 'alpha')->firstOrFail();
|
||||
$beta = Tenant::query()->where('codigo', 'beta')->firstOrFail();
|
||||
$this->assertCount(3, $alpha->addresses);
|
||||
$this->assertTrue($alpha->addresses->first()->pivot->is_main);
|
||||
$this->assertInstanceOf(TenantAddress::class, $alpha->addresses->first()->pivot);
|
||||
$this->assertSame('Sede principal', $alpha->mainAddress->label);
|
||||
$this->assertSame('-32.9431184', $alpha->mainAddress->latitude);
|
||||
$this->assertNotSame($alpha->mainAddress->id, $beta->mainAddress->id);
|
||||
$this->assertSame('alpha', $alpha->mainAddress->tenants->sole()->codigo);
|
||||
$this->assertNull(Tenant::query()->where('codigo', 'empty')->firstOrFail()->mainAddress);
|
||||
$this->assertSame(2, DB::table('tenant_addresses')->where('is_main', true)->count());
|
||||
|
||||
$migration->down();
|
||||
$this->assertDatabaseCount('addresses', 4);
|
||||
$this->assertEquals($oldMenus, DB::table('tenants_menues')->orderBy('id')->get()->toArray());
|
||||
}
|
||||
|
||||
public function test_a_tenant_can_have_many_secondary_addresses_but_only_one_main(): void
|
||||
{
|
||||
$tenant = Tenant::query()->where('codigo', 'alpha')->firstOrFail();
|
||||
$addresses = $this->addresses(3);
|
||||
$tenant->addresses()->attach($addresses[0], ['is_main' => true]);
|
||||
$tenant->addresses()->attach([$addresses[1], $addresses[2]], ['is_main' => false]);
|
||||
$this->assertCount(3, $tenant->addresses);
|
||||
|
||||
$this->expectException(QueryException::class);
|
||||
$tenant->addresses()->updateExistingPivot($addresses[1], ['is_main' => true]);
|
||||
}
|
||||
|
||||
public function test_it_prevents_duplicate_links(): void
|
||||
{
|
||||
$tenant = Tenant::query()->where('codigo', 'alpha')->firstOrFail();
|
||||
$address = $this->addresses(1)[0];
|
||||
$tenant->addresses()->attach($address);
|
||||
|
||||
$this->expectException(QueryException::class);
|
||||
$tenant->addresses()->attach($address);
|
||||
}
|
||||
|
||||
public function test_main_address_is_specific_to_each_tenant_and_can_be_switched(): void
|
||||
{
|
||||
$alpha = Tenant::query()->where('codigo', 'alpha')->firstOrFail();
|
||||
$beta = Tenant::query()->where('codigo', 'beta')->firstOrFail();
|
||||
$addresses = $this->addresses(2);
|
||||
$alpha->addresses()->attach($addresses[0], ['is_main' => true]);
|
||||
$alpha->addresses()->attach($addresses[1]);
|
||||
$beta->addresses()->attach($addresses[0], ['is_main' => true]);
|
||||
$this->assertSame($addresses[0], $alpha->mainAddress->id);
|
||||
|
||||
app(TenantAddressService::class)->setMainAddress($alpha, Address::findOrFail($addresses[1]));
|
||||
|
||||
$this->assertSame($addresses[1], $alpha->mainAddress->id);
|
||||
$this->assertSame($addresses[0], $beta->mainAddress->id);
|
||||
$this->assertSame(2, DB::table('tenant_addresses')->where('is_main', true)->count());
|
||||
$loaded = Tenant::query()->with('mainAddress')->whereIn('codigo', ['alpha', 'beta'])->get()->keyBy('codigo');
|
||||
$this->assertSame($addresses[1], $loaded['alpha']->mainAddress->id);
|
||||
$this->assertSame($addresses[0], $loaded['beta']->mainAddress->id);
|
||||
}
|
||||
|
||||
public function test_switching_to_an_unlinked_address_preserves_the_previous_main(): void
|
||||
{
|
||||
$tenant = Tenant::query()->where('codigo', 'alpha')->firstOrFail();
|
||||
$addresses = $this->addresses(2);
|
||||
$tenant->addresses()->attach($addresses[0], ['is_main' => true]);
|
||||
|
||||
try {
|
||||
app(TenantAddressService::class)->setMainAddress($tenant, Address::findOrFail($addresses[1]));
|
||||
$this->fail('An unlinked address must be rejected.');
|
||||
} catch (ModelNotFoundException) {
|
||||
$this->assertSame($addresses[0], $tenant->fresh()->mainAddress->id);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_invalid_coordinates_roll_back_all_copies_and_preserve_source_data(): void
|
||||
{
|
||||
$this->contact('alpha', [['addresses' => [
|
||||
['label' => 'Válida', 'address' => 'Primera 100', 'coordinates' => [-32, -60]],
|
||||
['label' => 'Inválida', 'address' => 'Segunda 200', 'coordinates' => [-91, -60]],
|
||||
]]]);
|
||||
$oldContent = DB::table('tenants_menues')->value('static_content');
|
||||
|
||||
try {
|
||||
$this->dataMigration()->up();
|
||||
$this->fail('Invalid latitude must not be silently imported.');
|
||||
} catch (\RuntimeException $exception) {
|
||||
$this->assertStringContainsString('alpha', $exception->getMessage());
|
||||
$this->assertDatabaseCount('addresses', 0);
|
||||
$this->assertDatabaseCount('tenant_addresses', 0);
|
||||
$this->assertSame($oldContent, DB::table('tenants_menues')->value('static_content'));
|
||||
}
|
||||
}
|
||||
|
||||
public function test_deleting_a_tenant_removes_links_but_keeps_shared_addresses(): void
|
||||
{
|
||||
$address = $this->addresses(1)[0];
|
||||
foreach (['alpha', 'beta'] as $code) {
|
||||
Tenant::query()->where('codigo', $code)->firstOrFail()->addresses()->attach($address, ['is_main' => true]);
|
||||
}
|
||||
Tenant::query()->where('codigo', 'alpha')->delete();
|
||||
$this->assertDatabaseCount('tenant_addresses', 1);
|
||||
$this->assertDatabaseCount('addresses', 1);
|
||||
|
||||
Address::findOrFail($address)->delete();
|
||||
$this->assertDatabaseCount('tenant_addresses', 0);
|
||||
}
|
||||
|
||||
public function test_schema_can_be_rolled_back_without_removing_legacy_tables(): void
|
||||
{
|
||||
$this->schemaMigration()->down();
|
||||
$this->assertFalse(Schema::hasTable('addresses'));
|
||||
$this->assertFalse(Schema::hasTable('tenant_addresses'));
|
||||
$this->assertTrue(Schema::hasTable('tenants_menues'));
|
||||
$this->assertTrue(Schema::hasColumn('tenants', 'address'));
|
||||
}
|
||||
|
||||
private function contact(string $code, array $locations, bool $enabled = true): void
|
||||
{
|
||||
DB::table('tenants_menues')->insert([
|
||||
'tenant_code' => $code,
|
||||
'menu_code' => 'help.contact',
|
||||
'static_content' => json_encode(['phone' => '+54 123', 'locations' => $locations], JSON_THROW_ON_ERROR),
|
||||
'is_enabled' => $enabled,
|
||||
]);
|
||||
}
|
||||
|
||||
private function addresses(int $count): array
|
||||
{
|
||||
$ids = [];
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$ids[] = Address::create(['label' => "Sucursal {$i}", 'address_text' => "Calle {$i}"])->id;
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
private function schemaMigration(): Migration
|
||||
{
|
||||
return require database_path('migrations/2026_09_30_000000_create_addresses_and_tenant_addresses_tables.php');
|
||||
}
|
||||
|
||||
private function dataMigration(): Migration
|
||||
{
|
||||
return require database_path('migrations/2026_09_30_000100_copy_contact_menu_addresses.php');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user