migrations/Version20260626120100.php line 1

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace DoctrineMigrations;
  4. use Doctrine\DBAL\Schema\Schema;
  5. use Doctrine\Migrations\AbstractMigration;
  6. /**
  7.  * Snapshot of human-entered creator demographic labels for reversibility.
  8.  *
  9.  * The demographic columns on `creator` (ethnicity, age_group, category_gender,
  10.  * category_parent, attractiveness_level, tier) are populated by humans via the
  11.  * backend CreatorCategoryForm. A later automated data-classification job is
  12.  * allowed to overwrite those same columns, which would otherwise destroy the
  13.  * manually-curated labels with no way back.
  14.  *
  15.  * This migration creates `creator_classification_human_backup` and, in the same
  16.  * deploy step, copies every creator that currently has at least one
  17.  * human-entered label into it (source = 'human'). It runs once at deploy time,
  18.  * BEFORE the classifier overwrites creator.*, so each environment snapshots its
  19.  * own human labels and a restore back into creator.* stays possible.
  20.  *
  21.  * Columns mirror creator.* types exactly so a restore is a clean column-for-column
  22.  * copy. The populate step uses INSERT IGNORE so that a re-run after a partial
  23.  * failure (the CREATE TABLE above is auto-committed and cannot be rolled back)
  24.  * simply skips creators that were already backed up instead of erroring on the
  25.  * creator_id primary key.
  26.  *
  27.  * Depends on MR-1's doctrine.yaml schema_filter ('~^(?!creator_classification)~'),
  28.  * which excludes the unmapped classifier tables from schema tooling. That filter
  29.  * also covers this creator_classification_human_backup table, so migrations:diff
  30.  * will not emit a spurious DROP for it.
  31.  */
  32. final class Version20260626120100 extends AbstractMigration
  33. {
  34.     public function getDescription(): string
  35.     {
  36.         return 'Create creator_classification_human_backup and snapshot human-entered demographic labels before the classifier overwrites creator.*';
  37.     }
  38.     public function up(Schema $schema): void
  39.     {
  40.         $this->addSql(<<<'SQL'
  41.             CREATE TABLE creator_classification_human_backup (
  42.                 creator_id BIGINT UNSIGNED NOT NULL,
  43.                 ethnicity BIGINT UNSIGNED DEFAULT NULL,
  44.                 age_group BIGINT UNSIGNED DEFAULT NULL,
  45.                 category_gender BIGINT UNSIGNED DEFAULT NULL,
  46.                 category_parent BIGINT UNSIGNED DEFAULT NULL,
  47.                 attractiveness_level BIGINT UNSIGNED DEFAULT NULL,
  48.                 tier BIGINT UNSIGNED DEFAULT NULL,
  49.                 source VARCHAR(16) NOT NULL DEFAULT 'human',
  50.                 backed_up_at DATETIME DEFAULT NULL,
  51.                 PRIMARY KEY(creator_id)
  52.             ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB
  53.             SQL);
  54.         $this->addSql(<<<'SQL'
  55.             INSERT IGNORE INTO creator_classification_human_backup
  56.                 (creator_id, ethnicity, age_group, category_gender, category_parent, attractiveness_level, tier, source, backed_up_at)
  57.             SELECT id, ethnicity, age_group, category_gender, category_parent, attractiveness_level, tier, 'human', NOW()
  58.             FROM creator
  59.             WHERE ethnicity IS NOT NULL
  60.                 OR age_group IS NOT NULL
  61.                 OR category_gender IS NOT NULL
  62.                 OR category_parent IS NOT NULL
  63.                 OR attractiveness_level IS NOT NULL
  64.                 OR tier IS NOT NULL
  65.             SQL);
  66.     }
  67.     public function down(Schema $schema): void
  68.     {
  69.         $this->addSql(<<<'SQL'
  70.             DROP TABLE creator_classification_human_backup
  71.             SQL);
  72.     }
  73. }