diff --git a/MEMORY.md b/MEMORY.md index 6f0688f9..ef92eb17 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -578,3 +578,27 @@ if (!empty($contatto['individuo_id'])) { create } - Test IMAP OAuth: sync email via connessione OAuth - Verificare refresh token automatico allo scadere - Verificare revoca e riconnessione + +## 2026-06-17 — Fix: tutte le migration + seeders idempotenti + +### Problema +`php artisan migrate` falliva su tabelle già esistenti (`tenants`, `tipologie_documenti`, ecc.). +`php artisan db:seed` creava duplicati su re-run (admin user, diocesi, comuni). +Migrations con `insert` dopo il guard `hasTable` violavano unique constraint al re-run. + +### Migration: hasTable guard su 28 CREATE mancanti +Aggiunto `if (!Schema::hasTable('table_name')) { ... }` wrapper a 28 migration file (45 `Schema::create()` calls) via script Node.js, più `2026_06_17_000001_create_google_oauth_connections_table.php` manualmente. + +### Fix: insert fuori dai guard (4 file) +- `2026_05_10_000001_create_tipologie_documenti_table.php` — spostato insert dentro l'if +- `2026_05_26_000001_create_tipologie_eventi_table.php` — spostato insert dentro l'if +- `2026_05_12_000001_create_ruoli_table.php` — spostato insert dentro l'if +- `2026_05_11_000006_add_email_attachment_tipologia.php` — `insert()` → `updateOrInsert()` + +### Seeder: firstOrCreate +- `DiocesiSeeder.php`: `Diocesi::create()` → `::firstOrCreate(['nome' => ...])` +- `ComuniSeeder.php`: `Comune::create()` → `::firstOrCreate(['codice_istat' => ...])` +- `DatabaseSeeder.php`: `User::create()` → `::firstOrCreate(['email' => ...])` + guardia `role_preset_id` + +### Risultato +`php artisan migrate --seed` è ora completamente idempotente. Zero errori su 64 migration + 6 seeder. Re-run è un no-op. diff --git a/database/migrations/2026_05_10_000001_create_tipologie_documenti_table.php b/database/migrations/2026_05_10_000001_create_tipologie_documenti_table.php index 9f0ae060..145c5db1 100644 --- a/database/migrations/2026_05_10_000001_create_tipologie_documenti_table.php +++ b/database/migrations/2026_05_10_000001_create_tipologie_documenti_table.php @@ -17,18 +17,18 @@ return new class extends Migration $table->boolean('attiva')->default(true); $table->timestamps(); }); - } - $defaultTipologie = ['avatar', 'galleria', 'documento', 'statuto', 'altro']; - foreach ($defaultTipologie as $i => $nome) { - DB::table('tipologie_documenti')->insert([ - 'nome' => $nome, - 'descrizione' => ucfirst($nome), - 'ordine' => $i, - 'attiva' => true, - 'created_at' => now(), - 'updated_at' => now(), - ]); + $defaultTipologie = ['avatar', 'galleria', 'documento', 'statuto', 'altro']; + foreach ($defaultTipologie as $i => $nome) { + DB::table('tipologie_documenti')->insert([ + 'nome' => $nome, + 'descrizione' => ucfirst($nome), + 'ordine' => $i, + 'attiva' => true, + 'created_at' => now(), + 'updated_at' => now(), + ]); + } } } diff --git a/database/migrations/2026_05_11_000006_add_email_attachment_tipologia.php b/database/migrations/2026_05_11_000006_add_email_attachment_tipologia.php index 17f79ceb..b510abd4 100644 --- a/database/migrations/2026_05_11_000006_add_email_attachment_tipologia.php +++ b/database/migrations/2026_05_11_000006_add_email_attachment_tipologia.php @@ -7,14 +7,15 @@ return new class extends Migration { public function up(): void { - DB::table('tipologie_documenti')->insert([ - 'nome' => 'email_attachment', - 'descrizione' => 'Allegato email', - 'ordine' => 100, - 'attiva' => true, - 'created_at' => now(), - 'updated_at' => now(), - ]); + DB::table('tipologie_documenti')->updateOrInsert( + ['nome' => 'email_attachment'], + [ + 'descrizione' => 'Allegato email', + 'ordine' => 100, + 'attiva' => true, + 'updated_at' => now(), + ] + ); } public function down(): void diff --git a/database/migrations/2026_05_12_000001_create_ruoli_table.php b/database/migrations/2026_05_12_000001_create_ruoli_table.php index 04875a4a..bba50130 100644 --- a/database/migrations/2026_05_12_000001_create_ruoli_table.php +++ b/database/migrations/2026_05_12_000001_create_ruoli_table.php @@ -17,27 +17,27 @@ return new class extends Migration $table->boolean('attiva')->default(true); $table->timestamps(); }); - } - $defaultRuoli = [ - ['nome' => 'Membro', 'descrizione' => 'Membro del gruppo'], - ['nome' => 'Catechista', 'descrizione' => 'Catechista del gruppo'], - ['nome' => 'Animatore', 'descrizione' => 'Animatore del gruppo'], - ['nome' => 'Responsabile', 'descrizione' => 'Responsabile del gruppo'], - ['nome' => 'Vice Responsabile', 'descrizione' => 'Vice responsabile del gruppo'], - ['nome' => 'Segretario', 'descrizione' => 'Segretario del gruppo'], - ['nome' => 'Tesoriere', 'descrizione' => 'Tesoriere del gruppo'], - ]; + $defaultRuoli = [ + ['nome' => 'Membro', 'descrizione' => 'Membro del gruppo'], + ['nome' => 'Catechista', 'descrizione' => 'Catechista del gruppo'], + ['nome' => 'Animatore', 'descrizione' => 'Animatore del gruppo'], + ['nome' => 'Responsabile', 'descrizione' => 'Responsabile del gruppo'], + ['nome' => 'Vice Responsabile', 'descrizione' => 'Vice responsabile del gruppo'], + ['nome' => 'Segretario', 'descrizione' => 'Segretario del gruppo'], + ['nome' => 'Tesoriere', 'descrizione' => 'Tesoriere del gruppo'], + ]; - foreach ($defaultRuoli as $i => $ruolo) { - DB::table('ruoli')->insert([ - 'nome' => $ruolo['nome'], - 'descrizione' => $ruolo['descrizione'], - 'ordine' => $i, - 'attiva' => true, - 'created_at' => now(), - 'updated_at' => now(), - ]); + foreach ($defaultRuoli as $i => $ruolo) { + DB::table('ruoli')->insert([ + 'nome' => $ruolo['nome'], + 'descrizione' => $ruolo['descrizione'], + 'ordine' => $i, + 'attiva' => true, + 'created_at' => now(), + 'updated_at' => now(), + ]); + } } if (!Schema::hasColumn('gruppo_individuo', 'ruolo_id')) { diff --git a/database/migrations/2026_05_26_000001_create_tipologie_eventi_table.php b/database/migrations/2026_05_26_000001_create_tipologie_eventi_table.php index ae791f23..2a3d3ae8 100644 --- a/database/migrations/2026_05_26_000001_create_tipologie_eventi_table.php +++ b/database/migrations/2026_05_26_000001_create_tipologie_eventi_table.php @@ -19,18 +19,18 @@ return new class extends Migration $table->boolean('attiva')->default(true); $table->timestamps(); }); - } - DB::table('tipologie_eventi')->insert([ - ['nome' => 'catechesi', 'descrizione' => 'Catechesi', 'ordine' => 0, 'attiva' => true], - ['nome' => 'liturgia', 'descrizione' => 'Liturgia', 'ordine' => 1, 'attiva' => true], - ['nome' => 'animazione', 'descrizione' => 'Animazione', 'ordine' => 2, 'attiva' => true], - ['nome' => 'formazione', 'descrizione' => 'Formazione', 'ordine' => 3, 'attiva' => true], - ['nome' => 'incontro', 'descrizione' => 'Incontro', 'ordine' => 4, 'attiva' => true], - ['nome' => 'riunione', 'descrizione' => 'Riunione', 'ordine' => 5, 'attiva' => true], - ['nome' => 'festa', 'descrizione' => 'Festa / Sagra', 'ordine' => 6, 'attiva' => true], - ['nome' => 'altro', 'descrizione' => 'Altro', 'ordine' => 7, 'attiva' => true], - ]); + DB::table('tipologie_eventi')->insert([ + ['nome' => 'catechesi', 'descrizione' => 'Catechesi', 'ordine' => 0, 'attiva' => true], + ['nome' => 'liturgia', 'descrizione' => 'Liturgia', 'ordine' => 1, 'attiva' => true], + ['nome' => 'animazione', 'descrizione' => 'Animazione', 'ordine' => 2, 'attiva' => true], + ['nome' => 'formazione', 'descrizione' => 'Formazione', 'ordine' => 3, 'attiva' => true], + ['nome' => 'incontro', 'descrizione' => 'Incontro', 'ordine' => 4, 'attiva' => true], + ['nome' => 'riunione', 'descrizione' => 'Riunione', 'ordine' => 5, 'attiva' => true], + ['nome' => 'festa', 'descrizione' => 'Festa / Sagra', 'ordine' => 6, 'attiva' => true], + ['nome' => 'altro', 'descrizione' => 'Altro', 'ordine' => 7, 'attiva' => true], + ]); + } } public function down(): void diff --git a/database/migrations/2026_06_17_000001_create_google_oauth_connections_table.php b/database/migrations/2026_06_17_000001_create_google_oauth_connections_table.php index 39ba0093..1094fd95 100644 --- a/database/migrations/2026_06_17_000001_create_google_oauth_connections_table.php +++ b/database/migrations/2026_06_17_000001_create_google_oauth_connections_table.php @@ -10,6 +10,10 @@ return new class extends Migration { public function up(): void { + if (Schema::hasTable('google_oauth_connections')) { + return; + } + Schema::create('google_oauth_connections', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); diff --git a/vendor/autoload.php b/vendor/autoload.php index 816daef4..b6ebe27a 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -14,7 +14,10 @@ if (PHP_VERSION_ID < 50600) { echo $err; } } - throw new RuntimeException($err); + trigger_error( + $err, + E_USER_ERROR + ); } require_once __DIR__ . '/composer/autoload_real.php'; diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php index 87766a22..51e734a7 100644 --- a/vendor/composer/InstalledVersions.php +++ b/vendor/composer/InstalledVersions.php @@ -26,12 +26,6 @@ use Composer\Semver\VersionParser; */ class InstalledVersions { - /** - * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to - * @internal - */ - private static $selfDir = null; - /** * @var mixed[]|null * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null @@ -317,18 +311,6 @@ class InstalledVersions self::$installedByVendor = array(); } - /** - * @return string - */ - private static function getSelfDir() - { - if (self::$selfDir === null) { - self::$selfDir = strtr(__DIR__, '\\', '/'); - } - - return self::$selfDir; - } - /** * @return array[] * @psalm-return list}> @@ -340,7 +322,6 @@ class InstalledVersions } $installed = array(); - $copiedLocalDir = false; if (self::$canGetVendors) { foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { @@ -349,11 +330,9 @@ class InstalledVersions } elseif (is_file($vendorDir.'/composer/installed.php')) { /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ $required = require $vendorDir.'/composer/installed.php'; - self::$installedByVendor[$vendorDir] = $required; - $installed[] = $required; - if (strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { - self::$installed = $required; - $copiedLocalDir = true; + $installed[] = self::$installedByVendor[$vendorDir] = $required; + if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + self::$installed = $installed[count($installed) - 1]; } } } @@ -371,7 +350,7 @@ class InstalledVersions } } - if (self::$installed !== array() && !$copiedLocalDir) { + if (self::$installed !== array()) { $installed[] = self::$installed; } diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 31a8de62..3acefcce 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'laravel/laravel', 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '6909e21b677ae5b7bfe8d8ef3f31b61982b5436e', + 'reference' => '92ba47e99f4839d49d5f002915c30a88bb842c6a', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -427,7 +427,7 @@ 'laravel/laravel' => array( 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '6909e21b677ae5b7bfe8d8ef3f31b61982b5436e', + 'reference' => '92ba47e99f4839d49d5f002915c30a88bb842c6a', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -732,8 +732,8 @@ 'dev_requirement' => false, 'provided' => array( 0 => '1.0 || 2.0 || 3.0', - 1 => '1.0|2.0|3.0', - 2 => '3.0.0', + 1 => '3.0.0', + 2 => '1.0|2.0|3.0', ), ), 'psr/simple-cache' => array(