<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Snapshot of human-entered creator demographic labels for reversibility.
*
* The demographic columns on `creator` (ethnicity, age_group, category_gender,
* category_parent, attractiveness_level, tier) are populated by humans via the
* backend CreatorCategoryForm. A later automated data-classification job is
* allowed to overwrite those same columns, which would otherwise destroy the
* manually-curated labels with no way back.
*
* This migration creates `creator_classification_human_backup` and, in the same
* deploy step, copies every creator that currently has at least one
* human-entered label into it (source = 'human'). It runs once at deploy time,
* BEFORE the classifier overwrites creator.*, so each environment snapshots its
* own human labels and a restore back into creator.* stays possible.
*
* Columns mirror creator.* types exactly so a restore is a clean column-for-column
* copy. The populate step uses INSERT IGNORE so that a re-run after a partial
* failure (the CREATE TABLE above is auto-committed and cannot be rolled back)
* simply skips creators that were already backed up instead of erroring on the
* creator_id primary key.
*
* Depends on MR-1's doctrine.yaml schema_filter ('~^(?!creator_classification)~'),
* which excludes the unmapped classifier tables from schema tooling. That filter
* also covers this creator_classification_human_backup table, so migrations:diff
* will not emit a spurious DROP for it.
*/
final class Version20260626120100 extends AbstractMigration
{
public function getDescription(): string
{
return 'Create creator_classification_human_backup and snapshot human-entered demographic labels before the classifier overwrites creator.*';
}
public function up(Schema $schema): void
{
$this->addSql(<<<'SQL'
CREATE TABLE creator_classification_human_backup (
creator_id BIGINT UNSIGNED NOT NULL,
ethnicity BIGINT UNSIGNED DEFAULT NULL,
age_group BIGINT UNSIGNED DEFAULT NULL,
category_gender BIGINT UNSIGNED DEFAULT NULL,
category_parent BIGINT UNSIGNED DEFAULT NULL,
attractiveness_level BIGINT UNSIGNED DEFAULT NULL,
tier BIGINT UNSIGNED DEFAULT NULL,
source VARCHAR(16) NOT NULL DEFAULT 'human',
backed_up_at DATETIME DEFAULT NULL,
PRIMARY KEY(creator_id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB
SQL);
$this->addSql(<<<'SQL'
INSERT IGNORE INTO creator_classification_human_backup
(creator_id, ethnicity, age_group, category_gender, category_parent, attractiveness_level, tier, source, backed_up_at)
SELECT id, ethnicity, age_group, category_gender, category_parent, attractiveness_level, tier, 'human', NOW()
FROM creator
WHERE ethnicity IS NOT NULL
OR age_group IS NOT NULL
OR category_gender IS NOT NULL
OR category_parent IS NOT NULL
OR attractiveness_level IS NOT NULL
OR tier IS NOT NULL
SQL);
}
public function down(Schema $schema): void
{
$this->addSql(<<<'SQL'
DROP TABLE creator_classification_human_backup
SQL);
}
}