feat(integration): add IntegrationInstance and WebsiteTypeIntegration models with migrations; refactor ClientIntegration and Integration models

This commit is contained in:
2026-09-04 12:17:30 -03:00
parent 1e7a9b6876
commit f11ba5a470
9 changed files with 402 additions and 9 deletions

View File

@@ -0,0 +1,28 @@
<?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::create('integration_instances', function (Blueprint $table): void {
$table->id();
$table->string('integration_code');
$table->string('name');
$table->longText('integration_data')->nullable(); // Encrypted JSON.
$table->timestamps();
$table->foreign('integration_code')->references('integration_code')->on('integrations')->restrictOnDelete();
// Allows associations to enforce one instance per integration and owner.
$table->unique(['id', 'integration_code'], 'integration_instances_id_code_unique');
});
}
public function down(): void
{
Schema::dropIfExists('integration_instances');
}
};

View File

@@ -0,0 +1,81 @@
<?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
{
// MySQL commits DDL separately; allow retrying after a partially applied migration.
if (! Schema::hasColumn('client_integrations', 'integration_instance_id')) {
Schema::table('client_integrations', function (Blueprint $table): void {
$table->unsignedBigInteger('integration_instance_id')->nullable();
});
}
DB::table('client_integrations')->whereNull('integration_instance_id')->orderBy('id')->chunkById(100, function ($associations): void {
foreach ($associations as $association) {
DB::transaction(function () use ($association): void {
$instanceId = DB::table('integration_instances')->insertGetId([
'integration_code' => $association->integration_code,
'name' => $association->integration_code.' / client '.$association->client_id,
// Copy ciphertext verbatim: no decryption or re-encryption during migration.
'integration_data' => $association->integration_data,
'created_at' => $association->created_at,
'updated_at' => $association->updated_at,
]);
DB::table('client_integrations')->where('id', $association->id)->update([
'integration_instance_id' => $instanceId,
]);
});
}
});
Schema::table('client_integrations', function (Blueprint $table): void {
$table->unsignedBigInteger('integration_instance_id')->nullable(false)->change();
});
$hasInstanceForeignKey = collect(Schema::getForeignKeys('client_integrations'))
->contains(fn (array $key): bool => $key['columns'] === ['integration_instance_id', 'integration_code']);
if (! $hasInstanceForeignKey) {
Schema::table('client_integrations', function (Blueprint $table): void {
$table->foreign(['integration_instance_id', 'integration_code'], 'client_integrations_instance_code_fk')
->references(['id', 'integration_code'])->on('integration_instances')->restrictOnDelete();
});
}
if (Schema::hasColumn('client_integrations', 'integration_data')) {
Schema::table('client_integrations', function (Blueprint $table): void {
$table->dropColumn('integration_data');
});
}
}
public function down(): void
{
Schema::table('client_integrations', function (Blueprint $table): void {
$table->longText('integration_data')->nullable();
});
DB::table('client_integrations')->orderBy('id')->chunkById(100, function ($associations): void {
foreach ($associations as $association) {
DB::table('client_integrations')->where('id', $association->id)->update([
'integration_data' => DB::table('integration_instances')
->where('id', $association->integration_instance_id)->value('integration_data'),
]);
}
});
Schema::table('client_integrations', function (Blueprint $table): void {
// MySQL uses the short name; SQLite needs the columns to rebuild the table.
$table->dropForeign('client_integrations_instance_code_fk')
->columns(['integration_instance_id', 'integration_code']);
$table->dropColumn('integration_instance_id');
});
}
};

View File

@@ -0,0 +1,29 @@
<?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::create('website_type_integrations', function (Blueprint $table): void {
$table->id();
$table->string('website_type_code');
$table->string('integration_code');
$table->unsignedBigInteger('integration_instance_id');
$table->timestamps();
$table->foreign('website_type_code')->references('codigo')->on('website_type')->cascadeOnDelete();
$table->foreign(['integration_instance_id', 'integration_code'], 'website_type_integrations_instance_code_fk')
->references(['id', 'integration_code'])->on('integration_instances')->restrictOnDelete();
$table->unique(['website_type_code', 'integration_code'], 'website_type_integrations_owner_code_unique');
});
}
public function down(): void
{
Schema::dropIfExists('website_type_integrations');
}
};