From 0680df657478d149d0fcb81d5ab1982799c95b74 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 7 May 2026 00:48:14 +0200 Subject: [PATCH 1/9] feat: rewrite consolidated migration using Doctrine Schema tool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces every raw `$this->addSql(...)` call in the consolidated end-of-2.7 migration with the equivalent Schema tool builder API (`$schema->createTable()`, `$table->addColumn()`, `$table->addForeignKeyConstraint()`, `$schema->dropTable()`). The emitted DDL is identical for MariaDB but is now generated through Doctrine's platform abstraction, so the same migration produces platform-native DDL on any database Doctrine supports. Verified end-to-end on MariaDB: - Fresh install: drop → migrate → schema:validate exits 0 - Upgrade from 2.7: 25 latest-2.x applied → swap files → rollup → schema:validate exits 0 - mysqldump diff against the previous raw-SQL version: only column ordering and index ordering changes (the Schema tool emits columns in addColumn() insertion order). No functional differences. - Full PHPUnit suite green (143 tests / 607 assertions). A small `applyTableOptions()` helper at the bottom of the class collapses the engine/charset/collation triplet that would otherwise repeat 29 times. Other Doctrine-supported platforms ignore these MariaDB-specific options. Co-Authored-By: Claude Opus 4.7 (1M context) --- migrations/Version20260506215847.php | 1458 ++++++++++---------------- 1 file changed, 580 insertions(+), 878 deletions(-) diff --git a/migrations/Version20260506215847.php b/migrations/Version20260506215847.php index a915bd41..ff1e65f8 100644 --- a/migrations/Version20260506215847.php +++ b/migrations/Version20260506215847.php @@ -5,6 +5,7 @@ namespace DoctrineMigrations; use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Table; use Doctrine\Migrations\AbstractMigration; /** @@ -24,6 +25,11 @@ * Fresh installs run this via `doctrine:migrations:migrate`. 2.x → 3.0 * upgraders skip execution and run `doctrine:migrations:rollup` instead — see * UPGRADE.md step 3. + * + * Uses Doctrine's Schema tool API (no raw `addSql`) so the migration is + * portable across any database Doctrine supports. Going forward, future + * migrations are required to do the same — enforced by the + * `NoAddSqlInMigration` PHPStan rule. */ final class Version20260506215847 extends AbstractMigration { @@ -34,887 +40,583 @@ public function getDescription(): string public function up(Schema $schema): void { - $this->addSql(<<<'SQL' - CREATE TABLE feed ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - feed_source_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - configuration JSON DEFAULT NULL COMMENT '(DC2Type:json)', - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_234044AB9033212A (tenant_id), - INDEX IDX_234044ABDDAEFFBD (feed_source_id), - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE feed_source ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - feed_type VARCHAR(255) NOT NULL, - secrets JSON DEFAULT NULL COMMENT '(DC2Type:json)', - supported_feed_output_type VARCHAR(255) NOT NULL, - title VARCHAR(255) DEFAULT '' NOT NULL, - description VARCHAR(255) DEFAULT '' NOT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_9DA80F879033212A (tenant_id), - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE interactive_slide_config ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - configuration JSON DEFAULT NULL COMMENT '(DC2Type:json)', - implementation_class VARCHAR(255) NOT NULL, - INDEX IDX_D30060259033212A (tenant_id), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE media ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - file_path VARCHAR(255) DEFAULT NULL, - license VARCHAR(255) DEFAULT '', - width INT DEFAULT 0 NOT NULL, - height INT DEFAULT 0 NOT NULL, - size INT DEFAULT 0 NOT NULL, - mime_type VARCHAR(255) DEFAULT '' NOT NULL, - sha VARCHAR(255) DEFAULT '' NOT NULL, - title VARCHAR(255) DEFAULT '' NOT NULL, - description VARCHAR(255) DEFAULT '' NOT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_6A2CA10C9033212A (tenant_id), - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE playlist ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - is_campaign TINYINT(1) NOT NULL, - published_from DATETIME DEFAULT NULL, - published_to DATETIME DEFAULT NULL, - title VARCHAR(255) DEFAULT '' NOT NULL, - description VARCHAR(255) DEFAULT '' NOT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_D782112D9033212A (tenant_id), - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE playlist_tenant ( - playlist_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - INDEX IDX_A12FC8516BBD148 (playlist_id), - INDEX IDX_A12FC8519033212A (tenant_id), - PRIMARY KEY(playlist_id, tenant_id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE playlist_screen_region ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - playlist_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - screen_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - region_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - weight INT DEFAULT 0 NOT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_6869486A9033212A (tenant_id), - INDEX IDX_6869486A6BBD148 (playlist_id), - INDEX IDX_6869486A41A67722 (screen_id), - INDEX IDX_6869486A98260155 (region_id), - INDEX changed_idx (changed), - UNIQUE INDEX unique_playlist_screen_region ( - playlist_id, screen_id, region_id - ), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE playlist_slide ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - playlist_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - slide_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - weight INT DEFAULT 0 NOT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_D1F3F7219033212A (tenant_id), - INDEX IDX_D1F3F7216BBD148 (playlist_id), - INDEX IDX_D1F3F721DD5AFB87 (slide_id), - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE refresh_tokens ( - id INT AUTO_INCREMENT NOT NULL, - refresh_token VARCHAR(128) NOT NULL, - username VARCHAR(255) NOT NULL, - valid DATETIME NOT NULL, - UNIQUE INDEX UNIQ_9BACE7E1C74F2195 (refresh_token), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE schedule ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - playlist_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - rrule VARCHAR(255) NOT NULL COMMENT '(DC2Type:rrule)', - duration INT NOT NULL, - INDEX IDX_5A3811FB9033212A (tenant_id), - INDEX IDX_5A3811FB6BBD148 (playlist_id), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE screen ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - screen_layout_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - size INT DEFAULT 0 NOT NULL, - resolution VARCHAR(255) DEFAULT '' NOT NULL, - orientation VARCHAR(255) DEFAULT '' NOT NULL, - location VARCHAR(255) DEFAULT '', - enable_color_scheme_change TINYINT(1) DEFAULT NULL, - title VARCHAR(255) DEFAULT '' NOT NULL, - description VARCHAR(255) DEFAULT '' NOT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_DF4C61309033212A (tenant_id), - INDEX IDX_DF4C6130C1ECB8D6 (screen_layout_id), - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE screen_campaign ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - campaign_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - screen_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_636686BD9033212A (tenant_id), - INDEX IDX_636686BDF639F774 (campaign_id), - INDEX IDX_636686BD41A67722 (screen_id), - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE screen_group ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - title VARCHAR(255) DEFAULT '' NOT NULL, - description VARCHAR(255) DEFAULT '' NOT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_10C764819033212A (tenant_id), - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE screen_group_screen ( - screen_group_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - screen_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - INDEX IDX_905749ED82274D27 (screen_group_id), - INDEX IDX_905749ED41A67722 (screen_id), - PRIMARY KEY(screen_group_id, screen_id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE screen_group_campaign ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - campaign_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - screen_group_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_1E364E6E9033212A (tenant_id), - INDEX IDX_1E364E6EF639F774 (campaign_id), - INDEX IDX_1E364E6E82274D27 (screen_group_id), - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE screen_layout ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - grid_rows INT DEFAULT 0 NOT NULL, - grid_columns INT DEFAULT 0 NOT NULL, - title VARCHAR(255) DEFAULT '' NOT NULL, - description VARCHAR(255) DEFAULT '' NOT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE screen_layout_tenant ( - screen_layout_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - INDEX IDX_4B4C32E9C1ECB8D6 (screen_layout_id), - INDEX IDX_4B4C32E99033212A (tenant_id), - PRIMARY KEY(screen_layout_id, tenant_id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE screen_layout_regions ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - screen_layout_id BINARY(16) DEFAULT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - title VARCHAR(255) DEFAULT '' NOT NULL, - grid_area JSON NOT NULL COMMENT '(DC2Type:json)', - type VARCHAR(255) DEFAULT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_D80836ADC1ECB8D6 (screen_layout_id), - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE screen_layout_regions_tenant ( - screen_layout_regions_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - INDEX IDX_90A5AF483026129C (screen_layout_regions_id), - INDEX IDX_90A5AF489033212A (tenant_id), - PRIMARY KEY( - screen_layout_regions_id, tenant_id - ) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE screen_user ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - screen_id BINARY(16) DEFAULT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - username VARCHAR(180) NOT NULL, - roles JSON NOT NULL COMMENT '(DC2Type:json)', - release_timestamp INT DEFAULT NULL, - release_version VARCHAR(255) DEFAULT NULL, - latest_request DATETIME DEFAULT NULL, - client_meta JSON DEFAULT NULL COMMENT '(DC2Type:json)', - UNIQUE INDEX UNIQ_8D2D23C6F85E0677 (username), - INDEX IDX_8D2D23C69033212A (tenant_id), - UNIQUE INDEX UNIQ_8D2D23C641A67722 (screen_id), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE slide ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - template_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - theme_id BINARY(16) DEFAULT NULL COMMENT '(DC2Type:ulid)', - feed_id BINARY(16) DEFAULT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - duration INT DEFAULT NULL, - content JSON DEFAULT NULL COMMENT '(DC2Type:json)', - template_options JSON DEFAULT NULL COMMENT '(DC2Type:json)', - published_from DATETIME DEFAULT NULL, - published_to DATETIME DEFAULT NULL, - title VARCHAR(255) DEFAULT '' NOT NULL, - description VARCHAR(255) DEFAULT '' NOT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_72EFEE629033212A (tenant_id), - INDEX IDX_72EFEE625DA0FB8 (template_id), - INDEX IDX_72EFEE6259027487 (theme_id), - UNIQUE INDEX UNIQ_72EFEE6251A5BC03 (feed_id), - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE slide_media ( - slide_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - media_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - INDEX IDX_EBA5772FDD5AFB87 (slide_id), - INDEX IDX_EBA5772FEA9FDD75 (media_id), - PRIMARY KEY(slide_id, media_id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE template ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - title VARCHAR(255) DEFAULT '' NOT NULL, - icon VARCHAR(255) DEFAULT '' NOT NULL, - resources JSON NOT NULL COMMENT '(DC2Type:json)', - description VARCHAR(255) DEFAULT '' NOT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE template_tenant ( - template_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - INDEX IDX_45B1CD905DA0FB8 (template_id), - INDEX IDX_45B1CD909033212A (tenant_id), - PRIMARY KEY(template_id, tenant_id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE tenant ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - tenant_key VARCHAR(25) NOT NULL, - fallback_image_url VARCHAR(255) DEFAULT NULL, - title VARCHAR(255) DEFAULT '' NOT NULL, - description VARCHAR(255) DEFAULT '' NOT NULL, - UNIQUE INDEX UNIQ_4E59C4623A6F39CD (tenant_key), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE theme ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - logo_id BINARY(16) DEFAULT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - css_styles LONGTEXT NOT NULL, - title VARCHAR(255) DEFAULT '' NOT NULL, - description VARCHAR(255) DEFAULT '' NOT NULL, - changed TINYINT(1) NOT NULL, - relations_checksum JSON DEFAULT '{}' NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_9775E7089033212A (tenant_id), - UNIQUE INDEX UNIQ_9775E708F98F144A (logo_id), - INDEX changed_idx (changed), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE user ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - provider_id VARCHAR(255) NOT NULL, - email VARCHAR(180) NOT NULL, - full_name VARCHAR(255) NOT NULL, - password VARCHAR(255) NOT NULL, - provider VARCHAR(255) NOT NULL, - user_type VARCHAR(255) NOT NULL, - UNIQUE INDEX UNIQ_8D93D649A53A8AA (provider_id), - UNIQUE INDEX UNIQ_8D93D649E7927C74 (email), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE user_activation_code ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - code VARCHAR(255) NOT NULL, - code_expire DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - username VARCHAR(255) NOT NULL, - roles JSON NOT NULL COMMENT '(DC2Type:json)', - UNIQUE INDEX UNIQ_E88B201577153098 (code), - INDEX IDX_E88B20159033212A (tenant_id), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - CREATE TABLE user_role_tenant ( - id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - user_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - tenant_id BINARY(16) NOT NULL COMMENT '(DC2Type:ulid)', - version INT DEFAULT 1 NOT NULL, - created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - modified_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', - created_by VARCHAR(255) DEFAULT '' NOT NULL, - modified_by VARCHAR(255) DEFAULT '' NOT NULL, - roles JSON NOT NULL COMMENT '(DC2Type:json)', - INDEX IDX_4C64EC46A76ED395 (user_id), - INDEX IDX_4C64EC469033212A (tenant_id), - UNIQUE INDEX user_tenant_unique (user_id, tenant_id), - PRIMARY KEY(id) - ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - feed - ADD - CONSTRAINT FK_234044AB9033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - feed - ADD - CONSTRAINT FK_234044ABDDAEFFBD FOREIGN KEY (feed_source_id) REFERENCES feed_source (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - feed_source - ADD - CONSTRAINT FK_9DA80F879033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - interactive_slide_config - ADD - CONSTRAINT FK_D30060259033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - media - ADD - CONSTRAINT FK_6A2CA10C9033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - playlist - ADD - CONSTRAINT FK_D782112D9033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - playlist_tenant - ADD - CONSTRAINT FK_A12FC8516BBD148 FOREIGN KEY (playlist_id) REFERENCES playlist (id) ON DELETE CASCADE - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - playlist_tenant - ADD - CONSTRAINT FK_A12FC8519033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) ON DELETE CASCADE - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - playlist_screen_region - ADD - CONSTRAINT FK_6869486A9033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - playlist_screen_region - ADD - CONSTRAINT FK_6869486A6BBD148 FOREIGN KEY (playlist_id) REFERENCES playlist (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - playlist_screen_region - ADD - CONSTRAINT FK_6869486A41A67722 FOREIGN KEY (screen_id) REFERENCES screen (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - playlist_screen_region - ADD - CONSTRAINT FK_6869486A98260155 FOREIGN KEY (region_id) REFERENCES screen_layout_regions (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - playlist_slide - ADD - CONSTRAINT FK_D1F3F7219033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - playlist_slide - ADD - CONSTRAINT FK_D1F3F7216BBD148 FOREIGN KEY (playlist_id) REFERENCES playlist (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - playlist_slide - ADD - CONSTRAINT FK_D1F3F721DD5AFB87 FOREIGN KEY (slide_id) REFERENCES slide (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - schedule - ADD - CONSTRAINT FK_5A3811FB9033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - schedule - ADD - CONSTRAINT FK_5A3811FB6BBD148 FOREIGN KEY (playlist_id) REFERENCES playlist (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen - ADD - CONSTRAINT FK_DF4C61309033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen - ADD - CONSTRAINT FK_DF4C6130C1ECB8D6 FOREIGN KEY (screen_layout_id) REFERENCES screen_layout (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_campaign - ADD - CONSTRAINT FK_636686BD9033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_campaign - ADD - CONSTRAINT FK_636686BDF639F774 FOREIGN KEY (campaign_id) REFERENCES playlist (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_campaign - ADD - CONSTRAINT FK_636686BD41A67722 FOREIGN KEY (screen_id) REFERENCES screen (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_group - ADD - CONSTRAINT FK_10C764819033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_group_screen - ADD - CONSTRAINT FK_905749ED82274D27 FOREIGN KEY (screen_group_id) REFERENCES screen_group (id) ON DELETE CASCADE - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_group_screen - ADD - CONSTRAINT FK_905749ED41A67722 FOREIGN KEY (screen_id) REFERENCES screen (id) ON DELETE CASCADE - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_group_campaign - ADD - CONSTRAINT FK_1E364E6E9033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_group_campaign - ADD - CONSTRAINT FK_1E364E6EF639F774 FOREIGN KEY (campaign_id) REFERENCES playlist (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_group_campaign - ADD - CONSTRAINT FK_1E364E6E82274D27 FOREIGN KEY (screen_group_id) REFERENCES screen_group (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_layout_tenant - ADD - CONSTRAINT FK_4B4C32E9C1ECB8D6 FOREIGN KEY (screen_layout_id) REFERENCES screen_layout (id) ON DELETE CASCADE - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_layout_tenant - ADD - CONSTRAINT FK_4B4C32E99033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) ON DELETE CASCADE - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_layout_regions - ADD - CONSTRAINT FK_D80836ADC1ECB8D6 FOREIGN KEY (screen_layout_id) REFERENCES screen_layout (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_layout_regions_tenant - ADD - CONSTRAINT FK_90A5AF483026129C FOREIGN KEY (screen_layout_regions_id) REFERENCES screen_layout_regions (id) ON DELETE CASCADE - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_layout_regions_tenant - ADD - CONSTRAINT FK_90A5AF489033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) ON DELETE CASCADE - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_user - ADD - CONSTRAINT FK_8D2D23C69033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - screen_user - ADD - CONSTRAINT FK_8D2D23C641A67722 FOREIGN KEY (screen_id) REFERENCES screen (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - slide - ADD - CONSTRAINT FK_72EFEE629033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - slide - ADD - CONSTRAINT FK_72EFEE625DA0FB8 FOREIGN KEY (template_id) REFERENCES template (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - slide - ADD - CONSTRAINT FK_72EFEE6259027487 FOREIGN KEY (theme_id) REFERENCES theme (id) ON DELETE - SET - NULL - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - slide - ADD - CONSTRAINT FK_72EFEE6251A5BC03 FOREIGN KEY (feed_id) REFERENCES feed (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - slide_media - ADD - CONSTRAINT FK_EBA5772FDD5AFB87 FOREIGN KEY (slide_id) REFERENCES slide (id) ON DELETE CASCADE - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - slide_media - ADD - CONSTRAINT FK_EBA5772FEA9FDD75 FOREIGN KEY (media_id) REFERENCES media (id) ON DELETE CASCADE - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - template_tenant - ADD - CONSTRAINT FK_45B1CD905DA0FB8 FOREIGN KEY (template_id) REFERENCES template (id) ON DELETE CASCADE - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - template_tenant - ADD - CONSTRAINT FK_45B1CD909033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) ON DELETE CASCADE - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - theme - ADD - CONSTRAINT FK_9775E7089033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - theme - ADD - CONSTRAINT FK_9775E708F98F144A FOREIGN KEY (logo_id) REFERENCES media (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - user_activation_code - ADD - CONSTRAINT FK_E88B20159033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - user_role_tenant - ADD - CONSTRAINT FK_4C64EC46A76ED395 FOREIGN KEY (user_id) REFERENCES user (id) - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - user_role_tenant - ADD - CONSTRAINT FK_4C64EC469033212A FOREIGN KEY (tenant_id) REFERENCES tenant (id) - SQL); + $table = $schema->createTable('feed'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('feed_source_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('configuration', 'json', ['notnull' => false]); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_234044AB9033212A'); + $table->addIndex(['feed_source_id'], 'IDX_234044ABDDAEFFBD'); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('feed_source'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('feed_type', 'string', ['length' => 255, 'notnull' => true]); + $table->addColumn('secrets', 'json', ['notnull' => false]); + $table->addColumn('supported_feed_output_type', 'string', ['length' => 255, 'notnull' => true]); + $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_9DA80F879033212A'); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('interactive_slide_config'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('configuration', 'json', ['notnull' => false]); + $table->addColumn('implementation_class', 'string', ['length' => 255, 'notnull' => true]); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_D30060259033212A'); + $this->applyTableOptions($table); + + $table = $schema->createTable('media'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('file_path', 'string', ['length' => 255, 'notnull' => false]); + $table->addColumn('license', 'string', ['length' => 255, 'notnull' => false, 'default' => '']); + $table->addColumn('width', 'integer', ['notnull' => true, 'default' => 0]); + $table->addColumn('height', 'integer', ['notnull' => true, 'default' => 0]); + $table->addColumn('size', 'integer', ['notnull' => true, 'default' => 0]); + $table->addColumn('mime_type', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('sha', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_6A2CA10C9033212A'); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('playlist'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('is_campaign', 'boolean', ['notnull' => true]); + $table->addColumn('published_from', 'datetime', ['notnull' => false]); + $table->addColumn('published_to', 'datetime', ['notnull' => false]); + $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_D782112D9033212A'); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('playlist_tenant'); + $table->addColumn('playlist_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->setPrimaryKey(['playlist_id', 'tenant_id']); + $table->addIndex(['playlist_id'], 'IDX_A12FC8516BBD148'); + $table->addIndex(['tenant_id'], 'IDX_A12FC8519033212A'); + $this->applyTableOptions($table); + + $table = $schema->createTable('playlist_screen_region'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('playlist_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('region_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('weight', 'integer', ['notnull' => true, 'default' => 0]); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_6869486A9033212A'); + $table->addIndex(['playlist_id'], 'IDX_6869486A6BBD148'); + $table->addIndex(['screen_id'], 'IDX_6869486A41A67722'); + $table->addIndex(['region_id'], 'IDX_6869486A98260155'); + $table->addUniqueIndex(['playlist_id', 'screen_id', 'region_id'], 'unique_playlist_screen_region'); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('playlist_slide'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('playlist_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('slide_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('weight', 'integer', ['notnull' => true, 'default' => 0]); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_D1F3F7219033212A'); + $table->addIndex(['playlist_id'], 'IDX_D1F3F7216BBD148'); + $table->addIndex(['slide_id'], 'IDX_D1F3F721DD5AFB87'); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('refresh_tokens'); + $table->addColumn('id', 'integer', ['notnull' => true, 'autoincrement' => true]); + $table->addColumn('refresh_token', 'string', ['length' => 128, 'notnull' => true]); + $table->addColumn('username', 'string', ['length' => 255, 'notnull' => true]); + $table->addColumn('valid', 'datetime', ['notnull' => true]); + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['refresh_token'], 'UNIQ_9BACE7E1C74F2195'); + $this->applyTableOptions($table); + + $table = $schema->createTable('schedule'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('playlist_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('rrule', 'rrule', ['length' => 255, 'notnull' => true]); + $table->addColumn('duration', 'integer', ['notnull' => true]); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_5A3811FB9033212A'); + $table->addIndex(['playlist_id'], 'IDX_5A3811FB6BBD148'); + $this->applyTableOptions($table); + + $table = $schema->createTable('screen'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_layout_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('size', 'integer', ['notnull' => true, 'default' => 0]); + $table->addColumn('resolution', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('orientation', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('location', 'string', ['length' => 255, 'notnull' => false, 'default' => '']); + $table->addColumn('enable_color_scheme_change', 'boolean', ['notnull' => false]); + $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_DF4C61309033212A'); + $table->addIndex(['screen_layout_id'], 'IDX_DF4C6130C1ECB8D6'); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('screen_campaign'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('campaign_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_636686BD9033212A'); + $table->addIndex(['campaign_id'], 'IDX_636686BDF639F774'); + $table->addIndex(['screen_id'], 'IDX_636686BD41A67722'); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('screen_group'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_10C764819033212A'); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('screen_group_screen'); + $table->addColumn('screen_group_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->setPrimaryKey(['screen_group_id', 'screen_id']); + $table->addIndex(['screen_group_id'], 'IDX_905749ED82274D27'); + $table->addIndex(['screen_id'], 'IDX_905749ED41A67722'); + $this->applyTableOptions($table); + + $table = $schema->createTable('screen_group_campaign'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('campaign_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_group_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_1E364E6E9033212A'); + $table->addIndex(['campaign_id'], 'IDX_1E364E6EF639F774'); + $table->addIndex(['screen_group_id'], 'IDX_1E364E6E82274D27'); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('screen_layout'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('grid_rows', 'integer', ['notnull' => true, 'default' => 0]); + $table->addColumn('grid_columns', 'integer', ['notnull' => true, 'default' => 0]); + $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('screen_layout_tenant'); + $table->addColumn('screen_layout_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->setPrimaryKey(['screen_layout_id', 'tenant_id']); + $table->addIndex(['screen_layout_id'], 'IDX_4B4C32E9C1ECB8D6'); + $table->addIndex(['tenant_id'], 'IDX_4B4C32E99033212A'); + $this->applyTableOptions($table); + + $table = $schema->createTable('screen_layout_regions'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_layout_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => false]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('grid_area', 'json', ['notnull' => true]); + $table->addColumn('type', 'string', ['length' => 255, 'notnull' => false]); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['screen_layout_id'], 'IDX_D80836ADC1ECB8D6'); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('screen_layout_regions_tenant'); + $table->addColumn('screen_layout_regions_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->setPrimaryKey(['screen_layout_regions_id', 'tenant_id']); + $table->addIndex(['screen_layout_regions_id'], 'IDX_90A5AF483026129C'); + $table->addIndex(['tenant_id'], 'IDX_90A5AF489033212A'); + $this->applyTableOptions($table); + + $table = $schema->createTable('screen_user'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => false]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('username', 'string', ['length' => 180, 'notnull' => true]); + $table->addColumn('roles', 'json', ['notnull' => true]); + $table->addColumn('release_timestamp', 'integer', ['notnull' => false]); + $table->addColumn('release_version', 'string', ['length' => 255, 'notnull' => false]); + $table->addColumn('latest_request', 'datetime', ['notnull' => false]); + $table->addColumn('client_meta', 'json', ['notnull' => false]); + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['username'], 'UNIQ_8D2D23C6F85E0677'); + $table->addIndex(['tenant_id'], 'IDX_8D2D23C69033212A'); + $table->addUniqueIndex(['screen_id'], 'UNIQ_8D2D23C641A67722'); + $this->applyTableOptions($table); + + $table = $schema->createTable('slide'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('template_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('theme_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => false]); + $table->addColumn('feed_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => false]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('duration', 'integer', ['notnull' => false]); + $table->addColumn('content', 'json', ['notnull' => false]); + $table->addColumn('template_options', 'json', ['notnull' => false]); + $table->addColumn('published_from', 'datetime', ['notnull' => false]); + $table->addColumn('published_to', 'datetime', ['notnull' => false]); + $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_72EFEE629033212A'); + $table->addIndex(['template_id'], 'IDX_72EFEE625DA0FB8'); + $table->addIndex(['theme_id'], 'IDX_72EFEE6259027487'); + $table->addUniqueIndex(['feed_id'], 'UNIQ_72EFEE6251A5BC03'); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('slide_media'); + $table->addColumn('slide_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('media_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->setPrimaryKey(['slide_id', 'media_id']); + $table->addIndex(['slide_id'], 'IDX_EBA5772FDD5AFB87'); + $table->addIndex(['media_id'], 'IDX_EBA5772FEA9FDD75'); + $this->applyTableOptions($table); + + $table = $schema->createTable('template'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('icon', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('resources', 'json', ['notnull' => true]); + $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('template_tenant'); + $table->addColumn('template_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->setPrimaryKey(['template_id', 'tenant_id']); + $table->addIndex(['template_id'], 'IDX_45B1CD905DA0FB8'); + $table->addIndex(['tenant_id'], 'IDX_45B1CD909033212A'); + $this->applyTableOptions($table); + + $table = $schema->createTable('tenant'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('tenant_key', 'string', ['length' => 25, 'notnull' => true]); + $table->addColumn('fallback_image_url', 'string', ['length' => 255, 'notnull' => false]); + $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['tenant_key'], 'UNIQ_4E59C4623A6F39CD'); + $this->applyTableOptions($table); + + $table = $schema->createTable('theme'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('logo_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => false]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('css_styles', 'text', ['notnull' => true]); + $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', 'boolean', ['notnull' => true]); + $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->setPrimaryKey(['id']); + $table->addIndex(['tenant_id'], 'IDX_9775E7089033212A'); + $table->addUniqueIndex(['logo_id'], 'UNIQ_9775E708F98F144A'); + $table->addIndex(['changed'], 'changed_idx'); + $this->applyTableOptions($table); + + $table = $schema->createTable('user'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('provider_id', 'string', ['length' => 255, 'notnull' => true]); + $table->addColumn('email', 'string', ['length' => 180, 'notnull' => true]); + $table->addColumn('full_name', 'string', ['length' => 255, 'notnull' => true]); + $table->addColumn('password', 'string', ['length' => 255, 'notnull' => true]); + $table->addColumn('provider', 'string', ['length' => 255, 'notnull' => true]); + $table->addColumn('user_type', 'string', ['length' => 255, 'notnull' => true]); + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['provider_id'], 'UNIQ_8D93D649A53A8AA'); + $table->addUniqueIndex(['email'], 'UNIQ_8D93D649E7927C74'); + $this->applyTableOptions($table); + + $table = $schema->createTable('user_activation_code'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('code', 'string', ['length' => 255, 'notnull' => true]); + $table->addColumn('code_expire', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('username', 'string', ['length' => 255, 'notnull' => true]); + $table->addColumn('roles', 'json', ['notnull' => true]); + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['code'], 'UNIQ_E88B201577153098'); + $table->addIndex(['tenant_id'], 'IDX_E88B20159033212A'); + $this->applyTableOptions($table); + + $table = $schema->createTable('user_role_tenant'); + $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('user_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); + $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('roles', 'json', ['notnull' => true]); + $table->setPrimaryKey(['id']); + $table->addIndex(['user_id'], 'IDX_4C64EC46A76ED395'); + $table->addIndex(['tenant_id'], 'IDX_4C64EC469033212A'); + $table->addUniqueIndex(['user_id', 'tenant_id'], 'user_tenant_unique'); + $this->applyTableOptions($table); + + // Foreign keys (added after all tables exist). + $schema->getTable('feed')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_234044AB9033212A'); + $schema->getTable('feed')->addForeignKeyConstraint('feed_source', ['feed_source_id'], ['id'], [], 'FK_234044ABDDAEFFBD'); + $schema->getTable('feed_source')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_9DA80F879033212A'); + $schema->getTable('interactive_slide_config')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_D30060259033212A'); + $schema->getTable('media')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_6A2CA10C9033212A'); + $schema->getTable('playlist')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_D782112D9033212A'); + $schema->getTable('playlist_tenant')->addForeignKeyConstraint('playlist', ['playlist_id'], ['id'], ['onDelete' => 'CASCADE'], 'FK_A12FC8516BBD148'); + $schema->getTable('playlist_tenant')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], ['onDelete' => 'CASCADE'], 'FK_A12FC8519033212A'); + $schema->getTable('playlist_screen_region')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_6869486A9033212A'); + $schema->getTable('playlist_screen_region')->addForeignKeyConstraint('playlist', ['playlist_id'], ['id'], [], 'FK_6869486A6BBD148'); + $schema->getTable('playlist_screen_region')->addForeignKeyConstraint('screen', ['screen_id'], ['id'], [], 'FK_6869486A41A67722'); + $schema->getTable('playlist_screen_region')->addForeignKeyConstraint('screen_layout_regions', ['region_id'], ['id'], [], 'FK_6869486A98260155'); + $schema->getTable('playlist_slide')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_D1F3F7219033212A'); + $schema->getTable('playlist_slide')->addForeignKeyConstraint('playlist', ['playlist_id'], ['id'], [], 'FK_D1F3F7216BBD148'); + $schema->getTable('playlist_slide')->addForeignKeyConstraint('slide', ['slide_id'], ['id'], [], 'FK_D1F3F721DD5AFB87'); + $schema->getTable('schedule')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_5A3811FB9033212A'); + $schema->getTable('schedule')->addForeignKeyConstraint('playlist', ['playlist_id'], ['id'], [], 'FK_5A3811FB6BBD148'); + $schema->getTable('screen')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_DF4C61309033212A'); + $schema->getTable('screen')->addForeignKeyConstraint('screen_layout', ['screen_layout_id'], ['id'], [], 'FK_DF4C6130C1ECB8D6'); + $schema->getTable('screen_campaign')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_636686BD9033212A'); + $schema->getTable('screen_campaign')->addForeignKeyConstraint('playlist', ['campaign_id'], ['id'], [], 'FK_636686BDF639F774'); + $schema->getTable('screen_campaign')->addForeignKeyConstraint('screen', ['screen_id'], ['id'], [], 'FK_636686BD41A67722'); + $schema->getTable('screen_group')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_10C764819033212A'); + $schema->getTable('screen_group_screen')->addForeignKeyConstraint('screen_group', ['screen_group_id'], ['id'], ['onDelete' => 'CASCADE'], 'FK_905749ED82274D27'); + $schema->getTable('screen_group_screen')->addForeignKeyConstraint('screen', ['screen_id'], ['id'], ['onDelete' => 'CASCADE'], 'FK_905749ED41A67722'); + $schema->getTable('screen_group_campaign')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_1E364E6E9033212A'); + $schema->getTable('screen_group_campaign')->addForeignKeyConstraint('playlist', ['campaign_id'], ['id'], [], 'FK_1E364E6EF639F774'); + $schema->getTable('screen_group_campaign')->addForeignKeyConstraint('screen_group', ['screen_group_id'], ['id'], [], 'FK_1E364E6E82274D27'); + $schema->getTable('screen_layout_tenant')->addForeignKeyConstraint('screen_layout', ['screen_layout_id'], ['id'], ['onDelete' => 'CASCADE'], 'FK_4B4C32E9C1ECB8D6'); + $schema->getTable('screen_layout_tenant')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], ['onDelete' => 'CASCADE'], 'FK_4B4C32E99033212A'); + $schema->getTable('screen_layout_regions')->addForeignKeyConstraint('screen_layout', ['screen_layout_id'], ['id'], [], 'FK_D80836ADC1ECB8D6'); + $schema->getTable('screen_layout_regions_tenant')->addForeignKeyConstraint('screen_layout_regions', ['screen_layout_regions_id'], ['id'], ['onDelete' => 'CASCADE'], 'FK_90A5AF483026129C'); + $schema->getTable('screen_layout_regions_tenant')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], ['onDelete' => 'CASCADE'], 'FK_90A5AF489033212A'); + $schema->getTable('screen_user')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_8D2D23C69033212A'); + $schema->getTable('screen_user')->addForeignKeyConstraint('screen', ['screen_id'], ['id'], [], 'FK_8D2D23C641A67722'); + $schema->getTable('slide')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_72EFEE629033212A'); + $schema->getTable('slide')->addForeignKeyConstraint('template', ['template_id'], ['id'], [], 'FK_72EFEE625DA0FB8'); + $schema->getTable('slide')->addForeignKeyConstraint('theme', ['theme_id'], ['id'], ['onDelete' => 'SET NULL'], 'FK_72EFEE6259027487'); + $schema->getTable('slide')->addForeignKeyConstraint('feed', ['feed_id'], ['id'], [], 'FK_72EFEE6251A5BC03'); + $schema->getTable('slide_media')->addForeignKeyConstraint('slide', ['slide_id'], ['id'], ['onDelete' => 'CASCADE'], 'FK_EBA5772FDD5AFB87'); + $schema->getTable('slide_media')->addForeignKeyConstraint('media', ['media_id'], ['id'], ['onDelete' => 'CASCADE'], 'FK_EBA5772FEA9FDD75'); + $schema->getTable('template_tenant')->addForeignKeyConstraint('template', ['template_id'], ['id'], ['onDelete' => 'CASCADE'], 'FK_45B1CD905DA0FB8'); + $schema->getTable('template_tenant')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], ['onDelete' => 'CASCADE'], 'FK_45B1CD909033212A'); + $schema->getTable('theme')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_9775E7089033212A'); + $schema->getTable('theme')->addForeignKeyConstraint('media', ['logo_id'], ['id'], [], 'FK_9775E708F98F144A'); + $schema->getTable('user_activation_code')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_E88B20159033212A'); + $schema->getTable('user_role_tenant')->addForeignKeyConstraint('user', ['user_id'], ['id'], [], 'FK_4C64EC46A76ED395'); + $schema->getTable('user_role_tenant')->addForeignKeyConstraint('tenant', ['tenant_id'], ['id'], [], 'FK_4C64EC469033212A'); } public function down(Schema $schema): void { - $this->addSql('ALTER TABLE feed DROP FOREIGN KEY FK_234044AB9033212A'); - $this->addSql('ALTER TABLE feed DROP FOREIGN KEY FK_234044ABDDAEFFBD'); - $this->addSql('ALTER TABLE feed_source DROP FOREIGN KEY FK_9DA80F879033212A'); - $this->addSql('ALTER TABLE interactive_slide_config DROP FOREIGN KEY FK_D30060259033212A'); - $this->addSql('ALTER TABLE media DROP FOREIGN KEY FK_6A2CA10C9033212A'); - $this->addSql('ALTER TABLE playlist DROP FOREIGN KEY FK_D782112D9033212A'); - $this->addSql('ALTER TABLE playlist_tenant DROP FOREIGN KEY FK_A12FC8516BBD148'); - $this->addSql('ALTER TABLE playlist_tenant DROP FOREIGN KEY FK_A12FC8519033212A'); - $this->addSql('ALTER TABLE playlist_screen_region DROP FOREIGN KEY FK_6869486A9033212A'); - $this->addSql('ALTER TABLE playlist_screen_region DROP FOREIGN KEY FK_6869486A6BBD148'); - $this->addSql('ALTER TABLE playlist_screen_region DROP FOREIGN KEY FK_6869486A41A67722'); - $this->addSql('ALTER TABLE playlist_screen_region DROP FOREIGN KEY FK_6869486A98260155'); - $this->addSql('ALTER TABLE playlist_slide DROP FOREIGN KEY FK_D1F3F7219033212A'); - $this->addSql('ALTER TABLE playlist_slide DROP FOREIGN KEY FK_D1F3F7216BBD148'); - $this->addSql('ALTER TABLE playlist_slide DROP FOREIGN KEY FK_D1F3F721DD5AFB87'); - $this->addSql('ALTER TABLE schedule DROP FOREIGN KEY FK_5A3811FB9033212A'); - $this->addSql('ALTER TABLE schedule DROP FOREIGN KEY FK_5A3811FB6BBD148'); - $this->addSql('ALTER TABLE screen DROP FOREIGN KEY FK_DF4C61309033212A'); - $this->addSql('ALTER TABLE screen DROP FOREIGN KEY FK_DF4C6130C1ECB8D6'); - $this->addSql('ALTER TABLE screen_campaign DROP FOREIGN KEY FK_636686BD9033212A'); - $this->addSql('ALTER TABLE screen_campaign DROP FOREIGN KEY FK_636686BDF639F774'); - $this->addSql('ALTER TABLE screen_campaign DROP FOREIGN KEY FK_636686BD41A67722'); - $this->addSql('ALTER TABLE screen_group DROP FOREIGN KEY FK_10C764819033212A'); - $this->addSql('ALTER TABLE screen_group_screen DROP FOREIGN KEY FK_905749ED82274D27'); - $this->addSql('ALTER TABLE screen_group_screen DROP FOREIGN KEY FK_905749ED41A67722'); - $this->addSql('ALTER TABLE screen_group_campaign DROP FOREIGN KEY FK_1E364E6E9033212A'); - $this->addSql('ALTER TABLE screen_group_campaign DROP FOREIGN KEY FK_1E364E6EF639F774'); - $this->addSql('ALTER TABLE screen_group_campaign DROP FOREIGN KEY FK_1E364E6E82274D27'); - $this->addSql('ALTER TABLE screen_layout_tenant DROP FOREIGN KEY FK_4B4C32E9C1ECB8D6'); - $this->addSql('ALTER TABLE screen_layout_tenant DROP FOREIGN KEY FK_4B4C32E99033212A'); - $this->addSql('ALTER TABLE screen_layout_regions DROP FOREIGN KEY FK_D80836ADC1ECB8D6'); - $this->addSql('ALTER TABLE screen_layout_regions_tenant DROP FOREIGN KEY FK_90A5AF483026129C'); - $this->addSql('ALTER TABLE screen_layout_regions_tenant DROP FOREIGN KEY FK_90A5AF489033212A'); - $this->addSql('ALTER TABLE screen_user DROP FOREIGN KEY FK_8D2D23C69033212A'); - $this->addSql('ALTER TABLE screen_user DROP FOREIGN KEY FK_8D2D23C641A67722'); - $this->addSql('ALTER TABLE slide DROP FOREIGN KEY FK_72EFEE629033212A'); - $this->addSql('ALTER TABLE slide DROP FOREIGN KEY FK_72EFEE625DA0FB8'); - $this->addSql('ALTER TABLE slide DROP FOREIGN KEY FK_72EFEE6259027487'); - $this->addSql('ALTER TABLE slide DROP FOREIGN KEY FK_72EFEE6251A5BC03'); - $this->addSql('ALTER TABLE slide_media DROP FOREIGN KEY FK_EBA5772FDD5AFB87'); - $this->addSql('ALTER TABLE slide_media DROP FOREIGN KEY FK_EBA5772FEA9FDD75'); - $this->addSql('ALTER TABLE template_tenant DROP FOREIGN KEY FK_45B1CD905DA0FB8'); - $this->addSql('ALTER TABLE template_tenant DROP FOREIGN KEY FK_45B1CD909033212A'); - $this->addSql('ALTER TABLE theme DROP FOREIGN KEY FK_9775E7089033212A'); - $this->addSql('ALTER TABLE theme DROP FOREIGN KEY FK_9775E708F98F144A'); - $this->addSql('ALTER TABLE user_activation_code DROP FOREIGN KEY FK_E88B20159033212A'); - $this->addSql('ALTER TABLE user_role_tenant DROP FOREIGN KEY FK_4C64EC46A76ED395'); - $this->addSql('ALTER TABLE user_role_tenant DROP FOREIGN KEY FK_4C64EC469033212A'); - $this->addSql('DROP TABLE feed'); - $this->addSql('DROP TABLE feed_source'); - $this->addSql('DROP TABLE interactive_slide_config'); - $this->addSql('DROP TABLE media'); - $this->addSql('DROP TABLE playlist'); - $this->addSql('DROP TABLE playlist_tenant'); - $this->addSql('DROP TABLE playlist_screen_region'); - $this->addSql('DROP TABLE playlist_slide'); - $this->addSql('DROP TABLE refresh_tokens'); - $this->addSql('DROP TABLE schedule'); - $this->addSql('DROP TABLE screen'); - $this->addSql('DROP TABLE screen_campaign'); - $this->addSql('DROP TABLE screen_group'); - $this->addSql('DROP TABLE screen_group_screen'); - $this->addSql('DROP TABLE screen_group_campaign'); - $this->addSql('DROP TABLE screen_layout'); - $this->addSql('DROP TABLE screen_layout_tenant'); - $this->addSql('DROP TABLE screen_layout_regions'); - $this->addSql('DROP TABLE screen_layout_regions_tenant'); - $this->addSql('DROP TABLE screen_user'); - $this->addSql('DROP TABLE slide'); - $this->addSql('DROP TABLE slide_media'); - $this->addSql('DROP TABLE template'); - $this->addSql('DROP TABLE template_tenant'); - $this->addSql('DROP TABLE tenant'); - $this->addSql('DROP TABLE theme'); - $this->addSql('DROP TABLE user'); - $this->addSql('DROP TABLE user_activation_code'); - $this->addSql('DROP TABLE user_role_tenant'); + // Drop in reverse-dependency order so FKs come down before their referenced tables. + foreach ([ + 'user_role_tenant', + 'user_activation_code', + 'user', + 'theme', + 'tenant', + 'template_tenant', + 'template', + 'slide_media', + 'slide', + 'screen_user', + 'screen_layout_regions_tenant', + 'screen_layout_regions', + 'screen_layout_tenant', + 'screen_layout', + 'screen_group_campaign', + 'screen_group_screen', + 'screen_group', + 'screen_campaign', + 'screen', + 'schedule', + 'refresh_tokens', + 'playlist_slide', + 'playlist_screen_region', + 'playlist_tenant', + 'playlist', + 'media', + 'interactive_slide_config', + 'feed_source', + 'feed', + ] as $tableName) { + $schema->dropTable($tableName); + } + } + + /** + * MariaDB-specific table options inherited from the original 2.x migrations + * (utf8mb4 + InnoDB). Other Doctrine-supported platforms (Postgres, SQLite, + * SQL Server) ignore these silently — the schema tool emits the correct + * platform-native DDL either way. + */ + private function applyTableOptions(Table $table): void + { + $table->addOption('charset', 'utf8mb4'); + $table->addOption('collation', 'utf8mb4_unicode_ci'); + $table->addOption('engine', 'InnoDB'); } } From d0a14ba6a4e5214c91a77c37900ee139a409e743 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 7 May 2026 00:48:29 +0200 Subject: [PATCH 2/9] feat: PHPStan rule forbids $this->addSql() in migrations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds `App\PhpStan\NoAddSqlInMigrationRule`, a project-local PHPStan rule that flags any `$this->addSql(...)` call in files under `migrations/`. Future migrations are forced through Doctrine's Schema tool API (`$schema->createTable()`, `$table->addColumn()`, …), keeping the consolidated 3.0 migration's portability property as the project evolves. Wiring: - Rule lives in `tools/phpstan/`, mapped via composer's autoload-dev to `App\PhpStan\` so it's available at lint time but never loaded into the runtime container (Symfony's `App\:` resource scan only covers `src/`). - `phpstan.dist.neon` registers the rule and adds `migrations/` to the analysed paths. Native SQL in entity listeners is intentionally out of scope here; that conversion is deferred. The rule's path-based scoping means it only enforces the convention for migrations, not the rest of the codebase. Verified the rule fires by adding a temporary `_RuleSmokeTest.php` migration with a single `addSql('SELECT 1')` call — PHPStan reported the expected `migrations.noAddSql` error and exited non-zero. Co-Authored-By: Claude Opus 4.7 (1M context) --- composer.json | 1 + phpstan.dist.neon | 6 +++ tools/phpstan/NoAddSqlInMigrationRule.php | 61 +++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 tools/phpstan/NoAddSqlInMigrationRule.php diff --git a/composer.json b/composer.json index 4cc31878..385c7f74 100644 --- a/composer.json +++ b/composer.json @@ -85,6 +85,7 @@ }, "autoload-dev": { "psr-4": { + "App\\PhpStan\\": "tools/phpstan/", "App\\Tests\\": "tests/" } }, diff --git a/phpstan.dist.neon b/phpstan.dist.neon index 12603027..02dc0542 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -5,5 +5,11 @@ parameters: level: 6 paths: - src/ + - migrations/ excludePaths: - src/Kernel.php + +rules: + # Forbids `$this->addSql(...)` in any file under `migrations/`. + # Future migrations must use Doctrine's Schema tool API for portability. + - App\PhpStan\NoAddSqlInMigrationRule diff --git a/tools/phpstan/NoAddSqlInMigrationRule.php b/tools/phpstan/NoAddSqlInMigrationRule.php new file mode 100644 index 00000000..6b793869 --- /dev/null +++ b/tools/phpstan/NoAddSqlInMigrationRule.php @@ -0,0 +1,61 @@ +addSql(...)` calls inside Doctrine migration files. + * + * Migrations must use Doctrine's Schema tool API + * (`$schema->createTable()`, `$table->addColumn()`, + * `$table->addForeignKeyConstraint()`, `$schema->dropTable()`, …) so the + * generated DDL is platform-agnostic — the same migration runs on any + * database Doctrine supports (MariaDB, MySQL, Postgres, SQLite, …) + * without manual translation. + * + * See `migrations/Version20260506215847.php` for a worked reference. + * + * @implements Rule + */ +final class NoAddSqlInMigrationRule implements Rule +{ + public function getNodeType(): string + { + return MethodCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!str_contains($scope->getFile(), '/migrations/')) { + return []; + } + + if (!$node->name instanceof Identifier || 'addSql' !== $node->name->name) { + return []; + } + + if (!$node->var instanceof Variable || 'this' !== $node->var->name) { + return []; + } + + return [ + RuleErrorBuilder::message( + 'Migrations must use the Schema tool API ($schema->createTable(), ' + .'$table->addColumn(), …) instead of $this->addSql(). Raw SQL ties ' + .'migrations to a single database platform; the Schema tool produces ' + .'platform-native DDL automatically. See migrations/Version20260506215847.php.', + ) + ->identifier('migrations.noAddSql') + ->build(), + ]; + } +} From 5cd7ec9f2ba0a66efcb56da856fcc98327c6f259 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 7 May 2026 00:49:05 +0200 Subject: [PATCH 3/9] docs: changelog entry for schema-tool migration + PHPStan rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents the convention shift under [Unreleased]: future migrations must use Doctrine's Schema tool API; the consolidated 3.0 migration already does, and the new PHPStan rule enforces it for everything that follows. Calls out that this lays the groundwork for portability without claiming it — runtime is still MariaDB-only and CI does not yet cover other platforms. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc1cfca2..86a70d03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +- Rewrote the consolidated 2.7 migration to use Doctrine's Schema tool API + (`$schema->createTable()`, `$table->addColumn()`, `$table->addForeignKeyConstraint()`, + …) instead of raw `addSql(...)` strings, and added a `NoAddSqlInMigrationRule` + PHPStan rule to enforce the same convention on future migrations. Lays the + groundwork for supporting any database Doctrine supports — the runtime is still + MariaDB-only and CI does not yet exercise other platforms; see follow-up + conversion of native SQL in entity listeners (deferred). - Consolidated 25 historical 2.x Doctrine migrations into a single schema-dump migration representing the end-of-2.7 schema. Upgraders must be on the latest 2.7.x with every migration applied, then run `doctrine:migrations:rollup` instead of `doctrine:migrations:migrate`. Fresh From 7f3091ffbec631aa4116749d8df328cde21f9a8b Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 7 May 2026 00:54:14 +0200 Subject: [PATCH 4/9] refactor: use Doctrine Types constants in consolidated migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the type-name string literals passed to `$table->addColumn()` with the matching `Doctrine\DBAL\Types\Types::*` constants — and `UlidType::NAME` / `RRuleType::RRULE` for the two custom types in this codebase. Refactor-safe (renames at the type-class level propagate via IDE rename), and signals at a glance that these are Doctrine type identifiers rather than free-form strings. No Rector rule covers this conversion for `addColumn()` directly — `Rector\Transform\Rector\Attribute\AttributeKeyToClassConstFetchRector` handles `#[Column(type: 'string')]` on entity attributes but not method-call arguments. A custom Rector rule would be overkill for one migration; conversion was scripted via a small Python regex pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- migrations/Version20260506215847.php | 585 ++++++++++++++------------- 1 file changed, 294 insertions(+), 291 deletions(-) diff --git a/migrations/Version20260506215847.php b/migrations/Version20260506215847.php index ff1e65f8..2f956835 100644 --- a/migrations/Version20260506215847.php +++ b/migrations/Version20260506215847.php @@ -4,9 +4,12 @@ namespace DoctrineMigrations; +use App\DBAL\RRuleType; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\Types; use Doctrine\Migrations\AbstractMigration; +use Symfony\Bridge\Doctrine\Types\UlidType; /** * Consolidated end-of-2.7 schema. @@ -41,17 +44,17 @@ public function getDescription(): string public function up(Schema $schema): void { $table = $schema->createTable('feed'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('feed_source_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('configuration', 'json', ['notnull' => false]); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('feed_source_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('configuration', Types::JSON, ['notnull' => false]); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_234044AB9033212A'); $table->addIndex(['feed_source_id'], 'IDX_234044ABDDAEFFBD'); @@ -59,105 +62,105 @@ public function up(Schema $schema): void $this->applyTableOptions($table); $table = $schema->createTable('feed_source'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('feed_type', 'string', ['length' => 255, 'notnull' => true]); - $table->addColumn('secrets', 'json', ['notnull' => false]); - $table->addColumn('supported_feed_output_type', 'string', ['length' => 255, 'notnull' => true]); - $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('feed_type', Types::STRING, ['length' => 255, 'notnull' => true]); + $table->addColumn('secrets', Types::JSON, ['notnull' => false]); + $table->addColumn('supported_feed_output_type', Types::STRING, ['length' => 255, 'notnull' => true]); + $table->addColumn('title', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_9DA80F879033212A'); $table->addIndex(['changed'], 'changed_idx'); $this->applyTableOptions($table); $table = $schema->createTable('interactive_slide_config'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('configuration', 'json', ['notnull' => false]); - $table->addColumn('implementation_class', 'string', ['length' => 255, 'notnull' => true]); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('configuration', Types::JSON, ['notnull' => false]); + $table->addColumn('implementation_class', Types::STRING, ['length' => 255, 'notnull' => true]); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_D30060259033212A'); $this->applyTableOptions($table); $table = $schema->createTable('media'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('file_path', 'string', ['length' => 255, 'notnull' => false]); - $table->addColumn('license', 'string', ['length' => 255, 'notnull' => false, 'default' => '']); - $table->addColumn('width', 'integer', ['notnull' => true, 'default' => 0]); - $table->addColumn('height', 'integer', ['notnull' => true, 'default' => 0]); - $table->addColumn('size', 'integer', ['notnull' => true, 'default' => 0]); - $table->addColumn('mime_type', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('sha', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('file_path', Types::STRING, ['length' => 255, 'notnull' => false]); + $table->addColumn('license', Types::STRING, ['length' => 255, 'notnull' => false, 'default' => '']); + $table->addColumn('width', Types::INTEGER, ['notnull' => true, 'default' => 0]); + $table->addColumn('height', Types::INTEGER, ['notnull' => true, 'default' => 0]); + $table->addColumn('size', Types::INTEGER, ['notnull' => true, 'default' => 0]); + $table->addColumn('mime_type', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('sha', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('title', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_6A2CA10C9033212A'); $table->addIndex(['changed'], 'changed_idx'); $this->applyTableOptions($table); $table = $schema->createTable('playlist'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('is_campaign', 'boolean', ['notnull' => true]); - $table->addColumn('published_from', 'datetime', ['notnull' => false]); - $table->addColumn('published_to', 'datetime', ['notnull' => false]); - $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('is_campaign', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('published_from', Types::DATETIME_MUTABLE, ['notnull' => false]); + $table->addColumn('published_to', Types::DATETIME_MUTABLE, ['notnull' => false]); + $table->addColumn('title', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_D782112D9033212A'); $table->addIndex(['changed'], 'changed_idx'); $this->applyTableOptions($table); $table = $schema->createTable('playlist_tenant'); - $table->addColumn('playlist_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('playlist_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); $table->setPrimaryKey(['playlist_id', 'tenant_id']); $table->addIndex(['playlist_id'], 'IDX_A12FC8516BBD148'); $table->addIndex(['tenant_id'], 'IDX_A12FC8519033212A'); $this->applyTableOptions($table); $table = $schema->createTable('playlist_screen_region'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('playlist_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('screen_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('region_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('weight', 'integer', ['notnull' => true, 'default' => 0]); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('playlist_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('region_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('weight', Types::INTEGER, ['notnull' => true, 'default' => 0]); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_6869486A9033212A'); $table->addIndex(['playlist_id'], 'IDX_6869486A6BBD148'); @@ -168,18 +171,18 @@ public function up(Schema $schema): void $this->applyTableOptions($table); $table = $schema->createTable('playlist_slide'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('playlist_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('slide_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('weight', 'integer', ['notnull' => true, 'default' => 0]); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('playlist_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('slide_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('weight', Types::INTEGER, ['notnull' => true, 'default' => 0]); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_D1F3F7219033212A'); $table->addIndex(['playlist_id'], 'IDX_D1F3F7216BBD148'); @@ -188,48 +191,48 @@ public function up(Schema $schema): void $this->applyTableOptions($table); $table = $schema->createTable('refresh_tokens'); - $table->addColumn('id', 'integer', ['notnull' => true, 'autoincrement' => true]); - $table->addColumn('refresh_token', 'string', ['length' => 128, 'notnull' => true]); - $table->addColumn('username', 'string', ['length' => 255, 'notnull' => true]); - $table->addColumn('valid', 'datetime', ['notnull' => true]); + $table->addColumn('id', Types::INTEGER, ['notnull' => true, 'autoincrement' => true]); + $table->addColumn('refresh_token', Types::STRING, ['length' => 128, 'notnull' => true]); + $table->addColumn('username', Types::STRING, ['length' => 255, 'notnull' => true]); + $table->addColumn('valid', Types::DATETIME_MUTABLE, ['notnull' => true]); $table->setPrimaryKey(['id']); $table->addUniqueIndex(['refresh_token'], 'UNIQ_9BACE7E1C74F2195'); $this->applyTableOptions($table); $table = $schema->createTable('schedule'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('playlist_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('rrule', 'rrule', ['length' => 255, 'notnull' => true]); - $table->addColumn('duration', 'integer', ['notnull' => true]); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('playlist_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('rrule', RRuleType::RRULE, ['length' => 255, 'notnull' => true]); + $table->addColumn('duration', Types::INTEGER, ['notnull' => true]); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_5A3811FB9033212A'); $table->addIndex(['playlist_id'], 'IDX_5A3811FB6BBD148'); $this->applyTableOptions($table); $table = $schema->createTable('screen'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('screen_layout_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('size', 'integer', ['notnull' => true, 'default' => 0]); - $table->addColumn('resolution', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('orientation', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('location', 'string', ['length' => 255, 'notnull' => false, 'default' => '']); - $table->addColumn('enable_color_scheme_change', 'boolean', ['notnull' => false]); - $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_layout_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('size', Types::INTEGER, ['notnull' => true, 'default' => 0]); + $table->addColumn('resolution', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('orientation', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('location', Types::STRING, ['length' => 255, 'notnull' => false, 'default' => '']); + $table->addColumn('enable_color_scheme_change', Types::BOOLEAN, ['notnull' => false]); + $table->addColumn('title', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_DF4C61309033212A'); $table->addIndex(['screen_layout_id'], 'IDX_DF4C6130C1ECB8D6'); @@ -237,17 +240,17 @@ public function up(Schema $schema): void $this->applyTableOptions($table); $table = $schema->createTable('screen_campaign'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('campaign_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('screen_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('campaign_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_636686BD9033212A'); $table->addIndex(['campaign_id'], 'IDX_636686BDF639F774'); @@ -256,42 +259,42 @@ public function up(Schema $schema): void $this->applyTableOptions($table); $table = $schema->createTable('screen_group'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('title', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_10C764819033212A'); $table->addIndex(['changed'], 'changed_idx'); $this->applyTableOptions($table); $table = $schema->createTable('screen_group_screen'); - $table->addColumn('screen_group_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('screen_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_group_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); $table->setPrimaryKey(['screen_group_id', 'screen_id']); $table->addIndex(['screen_group_id'], 'IDX_905749ED82274D27'); $table->addIndex(['screen_id'], 'IDX_905749ED41A67722'); $this->applyTableOptions($table); $table = $schema->createTable('screen_group_campaign'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('campaign_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('screen_group_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('campaign_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_group_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_1E364E6E9033212A'); $table->addIndex(['campaign_id'], 'IDX_1E364E6EF639F774'); @@ -300,71 +303,71 @@ public function up(Schema $schema): void $this->applyTableOptions($table); $table = $schema->createTable('screen_layout'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('grid_rows', 'integer', ['notnull' => true, 'default' => 0]); - $table->addColumn('grid_columns', 'integer', ['notnull' => true, 'default' => 0]); - $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('grid_rows', Types::INTEGER, ['notnull' => true, 'default' => 0]); + $table->addColumn('grid_columns', Types::INTEGER, ['notnull' => true, 'default' => 0]); + $table->addColumn('title', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['changed'], 'changed_idx'); $this->applyTableOptions($table); $table = $schema->createTable('screen_layout_tenant'); - $table->addColumn('screen_layout_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_layout_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); $table->setPrimaryKey(['screen_layout_id', 'tenant_id']); $table->addIndex(['screen_layout_id'], 'IDX_4B4C32E9C1ECB8D6'); $table->addIndex(['tenant_id'], 'IDX_4B4C32E99033212A'); $this->applyTableOptions($table); $table = $schema->createTable('screen_layout_regions'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('screen_layout_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => false]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('grid_area', 'json', ['notnull' => true]); - $table->addColumn('type', 'string', ['length' => 255, 'notnull' => false]); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_layout_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => false]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('title', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('grid_area', Types::JSON, ['notnull' => true]); + $table->addColumn('type', Types::STRING, ['length' => 255, 'notnull' => false]); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['screen_layout_id'], 'IDX_D80836ADC1ECB8D6'); $table->addIndex(['changed'], 'changed_idx'); $this->applyTableOptions($table); $table = $schema->createTable('screen_layout_regions_tenant'); - $table->addColumn('screen_layout_regions_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_layout_regions_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); $table->setPrimaryKey(['screen_layout_regions_id', 'tenant_id']); $table->addIndex(['screen_layout_regions_id'], 'IDX_90A5AF483026129C'); $table->addIndex(['tenant_id'], 'IDX_90A5AF489033212A'); $this->applyTableOptions($table); $table = $schema->createTable('screen_user'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('screen_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => false]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('username', 'string', ['length' => 180, 'notnull' => true]); - $table->addColumn('roles', 'json', ['notnull' => true]); - $table->addColumn('release_timestamp', 'integer', ['notnull' => false]); - $table->addColumn('release_version', 'string', ['length' => 255, 'notnull' => false]); - $table->addColumn('latest_request', 'datetime', ['notnull' => false]); - $table->addColumn('client_meta', 'json', ['notnull' => false]); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('screen_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => false]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('username', Types::STRING, ['length' => 180, 'notnull' => true]); + $table->addColumn('roles', Types::JSON, ['notnull' => true]); + $table->addColumn('release_timestamp', Types::INTEGER, ['notnull' => false]); + $table->addColumn('release_version', Types::STRING, ['length' => 255, 'notnull' => false]); + $table->addColumn('latest_request', Types::DATETIME_MUTABLE, ['notnull' => false]); + $table->addColumn('client_meta', Types::JSON, ['notnull' => false]); $table->setPrimaryKey(['id']); $table->addUniqueIndex(['username'], 'UNIQ_8D2D23C6F85E0677'); $table->addIndex(['tenant_id'], 'IDX_8D2D23C69033212A'); @@ -372,25 +375,25 @@ public function up(Schema $schema): void $this->applyTableOptions($table); $table = $schema->createTable('slide'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('template_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('theme_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => false]); - $table->addColumn('feed_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => false]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('duration', 'integer', ['notnull' => false]); - $table->addColumn('content', 'json', ['notnull' => false]); - $table->addColumn('template_options', 'json', ['notnull' => false]); - $table->addColumn('published_from', 'datetime', ['notnull' => false]); - $table->addColumn('published_to', 'datetime', ['notnull' => false]); - $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('template_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('theme_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => false]); + $table->addColumn('feed_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => false]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('duration', Types::INTEGER, ['notnull' => false]); + $table->addColumn('content', Types::JSON, ['notnull' => false]); + $table->addColumn('template_options', Types::JSON, ['notnull' => false]); + $table->addColumn('published_from', Types::DATETIME_MUTABLE, ['notnull' => false]); + $table->addColumn('published_to', Types::DATETIME_MUTABLE, ['notnull' => false]); + $table->addColumn('title', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_72EFEE629033212A'); $table->addIndex(['template_id'], 'IDX_72EFEE625DA0FB8'); @@ -400,67 +403,67 @@ public function up(Schema $schema): void $this->applyTableOptions($table); $table = $schema->createTable('slide_media'); - $table->addColumn('slide_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('media_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('slide_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('media_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); $table->setPrimaryKey(['slide_id', 'media_id']); $table->addIndex(['slide_id'], 'IDX_EBA5772FDD5AFB87'); $table->addIndex(['media_id'], 'IDX_EBA5772FEA9FDD75'); $this->applyTableOptions($table); $table = $schema->createTable('template'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('icon', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('resources', 'json', ['notnull' => true]); - $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('title', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('icon', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('resources', Types::JSON, ['notnull' => true]); + $table->addColumn('description', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['changed'], 'changed_idx'); $this->applyTableOptions($table); $table = $schema->createTable('template_tenant'); - $table->addColumn('template_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('template_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); $table->setPrimaryKey(['template_id', 'tenant_id']); $table->addIndex(['template_id'], 'IDX_45B1CD905DA0FB8'); $table->addIndex(['tenant_id'], 'IDX_45B1CD909033212A'); $this->applyTableOptions($table); $table = $schema->createTable('tenant'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('tenant_key', 'string', ['length' => 25, 'notnull' => true]); - $table->addColumn('fallback_image_url', 'string', ['length' => 255, 'notnull' => false]); - $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('tenant_key', Types::STRING, ['length' => 25, 'notnull' => true]); + $table->addColumn('fallback_image_url', Types::STRING, ['length' => 255, 'notnull' => false]); + $table->addColumn('title', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); $table->setPrimaryKey(['id']); $table->addUniqueIndex(['tenant_key'], 'UNIQ_4E59C4623A6F39CD'); $this->applyTableOptions($table); $table = $schema->createTable('theme'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('logo_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => false]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('css_styles', 'text', ['notnull' => true]); - $table->addColumn('title', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('description', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('changed', 'boolean', ['notnull' => true]); - $table->addColumn('relations_checksum', 'json', ['notnull' => true, 'default' => '{}']); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('logo_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => false]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('css_styles', Types::TEXT, ['notnull' => true]); + $table->addColumn('title', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('description', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('changed', Types::BOOLEAN, ['notnull' => true]); + $table->addColumn('relations_checksum', Types::JSON, ['notnull' => true, 'default' => '{}']); $table->setPrimaryKey(['id']); $table->addIndex(['tenant_id'], 'IDX_9775E7089033212A'); $table->addUniqueIndex(['logo_id'], 'UNIQ_9775E708F98F144A'); @@ -468,50 +471,50 @@ public function up(Schema $schema): void $this->applyTableOptions($table); $table = $schema->createTable('user'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('provider_id', 'string', ['length' => 255, 'notnull' => true]); - $table->addColumn('email', 'string', ['length' => 180, 'notnull' => true]); - $table->addColumn('full_name', 'string', ['length' => 255, 'notnull' => true]); - $table->addColumn('password', 'string', ['length' => 255, 'notnull' => true]); - $table->addColumn('provider', 'string', ['length' => 255, 'notnull' => true]); - $table->addColumn('user_type', 'string', ['length' => 255, 'notnull' => true]); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('provider_id', Types::STRING, ['length' => 255, 'notnull' => true]); + $table->addColumn('email', Types::STRING, ['length' => 180, 'notnull' => true]); + $table->addColumn('full_name', Types::STRING, ['length' => 255, 'notnull' => true]); + $table->addColumn('password', Types::STRING, ['length' => 255, 'notnull' => true]); + $table->addColumn('provider', Types::STRING, ['length' => 255, 'notnull' => true]); + $table->addColumn('user_type', Types::STRING, ['length' => 255, 'notnull' => true]); $table->setPrimaryKey(['id']); $table->addUniqueIndex(['provider_id'], 'UNIQ_8D93D649A53A8AA'); $table->addUniqueIndex(['email'], 'UNIQ_8D93D649E7927C74'); $this->applyTableOptions($table); $table = $schema->createTable('user_activation_code'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('code', 'string', ['length' => 255, 'notnull' => true]); - $table->addColumn('code_expire', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('username', 'string', ['length' => 255, 'notnull' => true]); - $table->addColumn('roles', 'json', ['notnull' => true]); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('code', Types::STRING, ['length' => 255, 'notnull' => true]); + $table->addColumn('code_expire', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('username', Types::STRING, ['length' => 255, 'notnull' => true]); + $table->addColumn('roles', Types::JSON, ['notnull' => true]); $table->setPrimaryKey(['id']); $table->addUniqueIndex(['code'], 'UNIQ_E88B201577153098'); $table->addIndex(['tenant_id'], 'IDX_E88B20159033212A'); $this->applyTableOptions($table); $table = $schema->createTable('user_role_tenant'); - $table->addColumn('id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('user_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('tenant_id', 'ulid', ['length' => 16, 'fixed' => true, 'notnull' => true]); - $table->addColumn('version', 'integer', ['notnull' => true, 'default' => 1]); - $table->addColumn('created_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('modified_at', 'datetime_immutable', ['notnull' => true]); - $table->addColumn('created_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('modified_by', 'string', ['length' => 255, 'notnull' => true, 'default' => '']); - $table->addColumn('roles', 'json', ['notnull' => true]); + $table->addColumn('id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('user_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('tenant_id', UlidType::NAME, ['length' => 16, 'fixed' => true, 'notnull' => true]); + $table->addColumn('version', Types::INTEGER, ['notnull' => true, 'default' => 1]); + $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('modified_at', Types::DATETIME_IMMUTABLE, ['notnull' => true]); + $table->addColumn('created_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('modified_by', Types::STRING, ['length' => 255, 'notnull' => true, 'default' => '']); + $table->addColumn('roles', Types::JSON, ['notnull' => true]); $table->setPrimaryKey(['id']); $table->addIndex(['user_id'], 'IDX_4C64EC46A76ED395'); $table->addIndex(['tenant_id'], 'IDX_4C64EC469033212A'); From 9e66882870d628ca2d0d3c87f6aa07283bb93198 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Wed, 27 May 2026 11:43:53 +0200 Subject: [PATCH 5/9] docs: relabel consolidated schema as end-of-2.8 and tighten changelog entries Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 20 +++----------------- UPGRADE.md | 10 +++++----- migrations/Version20260506215847.php | 6 +++--- src/Entity/Template.php | 2 +- 4 files changed, 12 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86a70d03..bc7ca0d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,23 +4,9 @@ All notable changes to this project will be documented in this file. ## [Unreleased] -- Rewrote the consolidated 2.7 migration to use Doctrine's Schema tool API - (`$schema->createTable()`, `$table->addColumn()`, `$table->addForeignKeyConstraint()`, - …) instead of raw `addSql(...)` strings, and added a `NoAddSqlInMigrationRule` - PHPStan rule to enforce the same convention on future migrations. Lays the - groundwork for supporting any database Doctrine supports — the runtime is still - MariaDB-only and CI does not yet exercise other platforms; see follow-up - conversion of native SQL in entity listeners (deferred). -- Consolidated 25 historical 2.x Doctrine migrations into a single schema-dump migration - representing the end-of-2.7 schema. Upgraders must be on the latest 2.7.x with every migration - applied, then run `doctrine:migrations:rollup` instead of `doctrine:migrations:migrate`. Fresh - installs are unaffected and continue to use `migrate`. See `UPGRADE.md` step 3. -- Restored three previously removed `Template` entity properties (`icon`, `resources`, - `description`) as deprecated, write-only fields with no getters/setters. The columns are kept - in the consolidated 3.0 schema so fresh installs and 2.x → 3.0 upgraders end up with identical - schemas, and Doctrine writes a value on every INSERT (the columns are NOT NULL with no DB - default). The properties and the matching column-drop migration are scheduled for removal in - 3.1. +- Rewrote the consolidated end-of-2.8 migration to Doctrine's Schema tool API; added a `NoAddSqlInMigrationRule` PHPStan rule to enforce the convention on future migrations. +- Consolidated 25 historical 2.x Doctrine migrations into a single end-of-2.8 schema migration; upgraders run `doctrine:migrations:rollup` (see `UPGRADE.md` step 3). +- Restored three deprecated `Template` properties (`icon`, `resources`, `description`) as write-only fields; scheduled for removal in 3.1. - Fixed Calendar and Colibo feed configuration urls and added [] result when no locationEndpoint is set. - Fixed baked-in `.env` shipping `APP_ENV=dev` in the API image; rewritten to `prod` at build time so direct reads don't try to bootstrap a dev environment the prod-only dependencies can't satisfy. diff --git a/UPGRADE.md b/UPGRADE.md index 66b3194d..f9391135 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -75,7 +75,7 @@ Rename the following .env variables in `.env.local`: #### 3 - Consolidate Doctrine migrations -3.0 ships a single consolidated migration that represents the end-of-2.7 schema. The 25 historical +3.0 ships a single consolidated migration that represents the end-of-2.8 schema. The 25 historical 2.x migrations have been removed from the repository. Because every upgrading database already matches that consolidated schema (via the 25 migrations it @@ -85,7 +85,7 @@ truncates the `doctrine_migration_versions` table and inserts a single row marki migration as already executed: ```shell -# Confirm the database is at the latest 2.7.x state before rolling up. +# Confirm the database is at the latest 2.8.x state before rolling up. # All 25 historical versions should appear as "migrated" / "available". docker compose exec phpfpm bin/console doctrine:migrations:status @@ -94,9 +94,9 @@ docker compose exec phpfpm bin/console doctrine:migrations:status docker compose exec phpfpm bin/console doctrine:migrations:rollup --no-interaction ``` -> **Prerequisite:** the database must be on the final 2.7.x release with every 2.x migration -> applied. If `doctrine:migrations:status` (run while still on 2.7.x) reports any pending -> migrations, run `doctrine:migrations:migrate` on 2.7.x first, then upgrade to 3.0 and continue +> **Prerequisite:** the database must be on the final 2.8.x release with every 2.x migration +> applied. If `doctrine:migrations:status` (run while still on 2.8.x) reports any pending +> migrations, run `doctrine:migrations:migrate` on 2.8.x first, then upgrade to 3.0 and continue > here. Fresh installs (no prior 2.x database) skip the rollup and run diff --git a/migrations/Version20260506215847.php b/migrations/Version20260506215847.php index 2f956835..2cc5f3c0 100644 --- a/migrations/Version20260506215847.php +++ b/migrations/Version20260506215847.php @@ -12,11 +12,11 @@ use Symfony\Bridge\Doctrine\Types\UlidType; /** - * Consolidated end-of-2.7 schema. + * Consolidated end-of-2.8 schema. * * Replaces the 25 historical 2.x migrations (Version20220309093909 … * Version20250828084617) with a single migration representing the schema - * everyone is on after the final 2.7.x release. + * everyone is on after the final 2.8.x release. * * Includes three columns the 3.0 `Template` entity carries as deprecated, * write-only fields — `template.icon`, `template.resources`, @@ -38,7 +38,7 @@ final class Version20260506215847 extends AbstractMigration { public function getDescription(): string { - return 'Consolidated end-of-2.7 schema (replaces all 25 historical 2.x migrations).'; + return 'Consolidated end-of-2.8 schema (replaces all 25 historical 2.x migrations).'; } public function up(Schema $schema): void diff --git a/src/Entity/Template.php b/src/Entity/Template.php index 81875008..1821df8a 100644 --- a/src/Entity/Template.php +++ b/src/Entity/Template.php @@ -30,7 +30,7 @@ class Template extends AbstractBaseEntity implements MultiTenantInterface, Relat /* * The three properties below ($icon, $resources, $description) are * carried over from 2.7 with their original mapping and PHP-level - * defaults so the consolidated end-of-2.7 schema matches both fresh + * defaults so the consolidated end-of-2.8 schema matches both fresh * installs and 2.x → 3.0 upgraders, and so Doctrine emits a value * for each on every INSERT (the columns are NOT NULL with no DB * default). They are intentionally write-only here — no getters, From 54e58cc113652188b4fccdfe1fc28114403e308b Mon Sep 17 00:00:00 2001 From: turegjorup Date: Wed, 27 May 2026 13:54:42 +0200 Subject: [PATCH 6/9] ci: add Postgres schema-portability gate to Doctrine workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a second validate-doctrine-schema job that runs the consolidated Schema-tool migration and doctrine:schema:validate against a postgres:16 service. Treated as a blocker for new schema changes — widening the MariaDB-only surface (entity listeners, raw down() SQL, etc. — all pre-existing) requires explicit review. The base phpfpm image ships without pdo_pgsql, so the new docker-compose.postgres.yml override layers it on via dockerfile_inline in addition to declaring the postgres service. Both files carry EXPERIMENTAL caveats: the gate guards entity/migration portability, not a runtime claim that the app works on Postgres. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/doctrine.yaml | 68 +++++++++++++++++++++++++++++++++ CHANGELOG.md | 2 + docker-compose.postgres.yml | 64 +++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 docker-compose.postgres.yml diff --git a/.github/workflows/doctrine.yaml b/.github/workflows/doctrine.yaml index 296eec60..0f6fa383 100644 --- a/.github/workflows/doctrine.yaml +++ b/.github/workflows/doctrine.yaml @@ -3,11 +3,43 @@ ### Runs pending Doctrine migrations and validates the resulting schema ### against the entity metadata. ### +### #### MariaDB leg +### +### The supported, deployment-target leg. CI matrix runs the full +### migrations:migrate + schema:validate cycle against MariaDB 10.11 and +### 11.4. A green run here means upgrades will work in production. +### +### #### Postgres leg (EXPERIMENTAL — schema-portability gate only) +### +### NOT a claim that this project runs on Postgres. Postgres is NOT a +### deployment target, NOT exercised by PHPUnit, and NOT covered by the +### rest of CI. The leg exists only to catch regressions in the narrow +### slice of portability we have so far achieved: +### +### - Entity metadata (column types, index names, quoted identifiers) +### stays platform-neutral. +### - The consolidated migration's up() emits portable DDL via Schema +### tool API. +### +### Known gaps that still block real Postgres deployment: +### +### - Native MariaDB SQL in entity listeners (`MultiTenantRepositoryTrait`, +### `RelationsChecksumCalculator`) — Postgres would fail at runtime. +### - Migration down() blocks still use raw MariaDB `DROP FOREIGN KEY` +### syntax — irrelevant for forward migration but not portable. +### - Doctrine cache, fixture loading, and most application queries are +### untested against Postgres. +### +### A failure on this leg is a blocker — new schema changes must stay +### portable; widening the MariaDB-only surface requires explicit review. +### ### #### Assumptions ### ### 1. A docker compose service named `phpfpm` can be run and `bin/console` ### can be run inside the `phpfpm` service. ### 2. A `mariadb` service is reachable from `phpfpm`. +### 3. `docker-compose.postgres.yml` adds a `postgres` service and layers +### `pdo_pgsql` onto `phpfpm`, used by the Postgres leg below. on: pull_request @@ -55,3 +87,39 @@ jobs: - name: Validate Doctrine schema run: docker compose run --rm -e APP_ENV=prod -e MARIADB_VERSION phpfpm bin/console doctrine:schema:validate + + # EXPERIMENTAL: schema-portability regression gate only. Postgres is NOT + # a supported deployment target — see the header comment at the top of + # this file for the full caveat. A green run means entity metadata and + # the consolidated migration's up() are portable; it does NOT mean the + # application runs on Postgres. + validate-doctrine-schema-postgres: + runs-on: ubuntu-latest + name: Validate Schema (postgres:16, experimental) + env: + # docker-compose.postgres.yml adds the postgres service and layers + # pdo_pgsql onto phpfpm. COMPOSE_FILE stacks it on top of the default. + COMPOSE_FILE: docker-compose.yml:docker-compose.postgres.yml + DATABASE_URL: "postgresql://db:db@postgres:5432/db?serverVersion=16&charset=utf8" + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Cache vendor (prod) + uses: actions/cache@v5 + with: + path: vendor + key: vendor-prod-php8.4-${{ hashFiles('composer.lock') }} + restore-keys: vendor-prod-php8.4- + + - name: Setup network + run: docker network create frontend + + - name: "[prod] Composer install" + run: docker compose run --rm -e APP_ENV=prod phpfpm composer install --no-dev -o + + - name: Run Doctrine Migrations + run: docker compose run --rm -e APP_ENV=prod -e DATABASE_URL phpfpm bin/console doctrine:migrations:migrate --no-interaction + + - name: Validate Doctrine schema + run: docker compose run --rm -e APP_ENV=prod -e DATABASE_URL phpfpm bin/console doctrine:schema:validate diff --git a/CHANGELOG.md b/CHANGELOG.md index ad860ecc..2d774c83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file. - Rewrote the consolidated end-of-2.8 migration to Doctrine's Schema tool API; added a `NoAddSqlInMigrationRule` PHPStan rule to enforce the convention on future migrations. +- Added a Postgres `Validate Schema` job to the Doctrine workflow as a regression gate against + entity/migration drift from Postgres compatibility; uses the new `docker-compose.postgres.yml` override. - Consolidated 25 historical 2.x Doctrine migrations into a single end-of-2.8 schema migration; upgraders run `doctrine:migrations:rollup` (see `UPGRADE.md` step 3). - Restored three deprecated `Template` properties (`icon`, `resources`, `description`) as diff --git a/docker-compose.postgres.yml b/docker-compose.postgres.yml new file mode 100644 index 00000000..95de3929 --- /dev/null +++ b/docker-compose.postgres.yml @@ -0,0 +1,64 @@ +### Postgres compose override — EXPERIMENTAL +# +# NOT a supported runtime configuration. Postgres is not a deployment +# target for this project. This file exists to drive the +# schema-portability regression gate in .github/workflows/doctrine.yaml +# and to let developers reproduce that gate locally. +# +# What works: +# - The consolidated end-of-2.8 migration's up() emits portable DDL via +# Doctrine's Schema tool API and applies cleanly on Postgres 16. +# - `doctrine:schema:validate` reports the resulting schema in sync +# with the entity metadata. +# +# What does NOT work (running the app on Postgres will fail): +# - Native MariaDB SQL in `MultiTenantRepositoryTrait` and +# `RelationsChecksumCalculator` — runtime queries break on Postgres. +# - Migration down() blocks use raw `DROP FOREIGN KEY` (MariaDB syntax). +# - PHPUnit, fixtures, Doctrine cache and most application queries are +# untested against Postgres. +# +# Treat this file as test-infrastructure, not a deployment artefact. +# +# Local equivalent of the CI gate: +# +# COMPOSE_FILE=docker-compose.yml:docker-compose.postgres.yml \ +# docker compose up -d postgres +# COMPOSE_FILE=docker-compose.yml:docker-compose.postgres.yml \ +# DATABASE_URL=postgresql://db:db@postgres:5432/db?serverVersion=16 \ +# docker compose run --rm -e DATABASE_URL -e APP_ENV=prod \ +# phpfpm bin/console doctrine:migrations:migrate --no-interaction +# +# pdo_pgsql isn't in the base phpfpm image, so this override layers it on +# via dockerfile_inline (Compose v2.27+). +services: + postgres: + image: postgres:16-alpine + networks: + - app + ports: + - "5432" + environment: + - POSTGRES_USER=db + - POSTGRES_PASSWORD=db + - POSTGRES_DB=db + healthcheck: + test: ["CMD-SHELL", "pg_isready -U db -d db"] + interval: 5s + timeout: 3s + retries: 10 + start_period: 5s + + phpfpm: + build: + context: . + dockerfile_inline: | + FROM itkdev/php8.4-fpm:latest + USER root + RUN apt-get update -qq && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends php8.4-pgsql && \ + rm -rf /var/lib/apt/lists/* + USER deploy + depends_on: + postgres: + condition: service_healthy From 38128ed5050d513b1acff1d6c95351c5f8d93689 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Wed, 27 May 2026 13:58:26 +0200 Subject: [PATCH 7/9] ci: build phpfpm-with-pdo_pgsql before running Postgres gate Previous attempt fell through to the upstream itkdev/php8.4-fpm:latest image (no pdo_pgsql) because `docker compose run` doesn't auto-build when an `image:` is already declared upstream. Give the override its own image tag (display-phpfpm-postgres:local) and add an explicit `docker compose build phpfpm` step before the run commands. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/doctrine.yaml | 6 ++++++ docker-compose.postgres.yml | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/doctrine.yaml b/.github/workflows/doctrine.yaml index 0f6fa383..0743886a 100644 --- a/.github/workflows/doctrine.yaml +++ b/.github/workflows/doctrine.yaml @@ -115,6 +115,12 @@ jobs: - name: Setup network run: docker network create frontend + # Build the phpfpm-with-pdo_pgsql image declared in + # docker-compose.postgres.yml; without this, `docker compose run` reuses + # the upstream itkdev/php8.4-fpm:latest image which lacks pdo_pgsql. + - name: Build phpfpm with pdo_pgsql + run: docker compose build phpfpm + - name: "[prod] Composer install" run: docker compose run --rm -e APP_ENV=prod phpfpm composer install --no-dev -o diff --git a/docker-compose.postgres.yml b/docker-compose.postgres.yml index 95de3929..a777b2ac 100644 --- a/docker-compose.postgres.yml +++ b/docker-compose.postgres.yml @@ -50,6 +50,10 @@ services: start_period: 5s phpfpm: + # Unique tag so this override doesn't share an entry with the upstream + # `itkdev/php8.4-fpm:latest` from docker-compose.yml — without this, + # `docker compose run` may reuse the upstream image without pdo_pgsql. + image: display-phpfpm-postgres:local build: context: . dockerfile_inline: | From 8a5528d5136f8c3682a5b422156d72c1aed15614 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Wed, 27 May 2026 16:42:34 +0200 Subject: [PATCH 8/9] =?UTF-8?q?docs:=20add=20ADR=20010=20=E2=80=94=20Schem?= =?UTF-8?q?a=20tool=20migrations,=20no=20native=20SQL=20for=20DDL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/adr/010-schema-tool-migrations.md | 67 ++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 docs/adr/010-schema-tool-migrations.md diff --git a/docs/adr/010-schema-tool-migrations.md b/docs/adr/010-schema-tool-migrations.md new file mode 100644 index 00000000..f1eecd06 --- /dev/null +++ b/docs/adr/010-schema-tool-migrations.md @@ -0,0 +1,67 @@ +# ADR 010 - Schema tool migrations, no native SQL for DDL + +Date: 27-05-2026 + +## Status + +Proposed + +## Context + +The consolidated end-of-2.8 migration was rewritten from raw `addSql` strings to +Doctrine's Schema tool API (`$schema->createTable()`, `$table->addColumn()`, +`$table->addForeignKeyConstraint()`, …). The same change replaced free-form +type-name strings (`'string'`, `'datetime_immutable'`, …) with `Doctrine\DBAL\Types\Types::*` +constants. + +This unblocks the schema-portability regression gate in +`.github/workflows/doctrine.yaml`: the consolidated migration now applies cleanly +on Postgres 16 from the same code path that runs on MariaDB. + +Postgres is still **not** a deployment target. Native MariaDB SQL remains in +entity listeners (`MultiTenantRepositoryTrait`, `RelationsChecksumCalculator`), +and runtime queries are untested against Postgres. The conversion is scoped to +migrations for now — but if migrations regress on portability, the schema-level +work is wasted and the gate stops being meaningful. + +## Decision + +All future migrations must go through Doctrine's Schema tool API. Raw +`$this->addSql(...)` calls inside `migrations/` are disallowed for DDL. + +Enforcement: + +- `App\PhpStan\NoAddSqlInMigrationRule` (project-local PHPStan rule) fails CI on + any `addSql` call in `migrations/`. +- The `Validate Schema (postgres:16, experimental)` job in + `.github/workflows/doctrine.yaml` runs `doctrine:migrations:migrate` against + Postgres 16 and fails the build on platform-specific DDL. + +Type references in migrations must use the `Doctrine\DBAL\Types\Types::*` +constants (plus `UlidType::NAME`, `RRuleType::RRULE` for the two custom types in +this project) rather than free-form strings, so type renames propagate via the +type system. + +The two existing exceptions are pre-existing technical debt called out +explicitly: + +- The consolidated migration's `down()` still uses raw `DROP FOREIGN KEY` (a + MariaDB-only syntax). It runs only on rollback, which is not a supported + upgrade path, so it's left as-is. +- Entity listeners and a small set of runtime repository methods still execute + native MariaDB SQL. Out of scope for this ADR; tracked separately. + +## Consequences + +- Schema-level migration code stays portable to any database Doctrine supports. + Real Postgres deployment still requires resolving the runtime-SQL debt above. +- Adding a new migration requires using the Schema tool API. Common operations + (CREATE TABLE, ALTER TABLE, indexes, foreign keys) have direct equivalents. + Genuine MariaDB-specific changes that the Schema tool can't express need an + explicit ADR/review to widen the MariaDB-only surface — not a silent + `addSql`. +- The Postgres CI job is treated as a portability regression gate, not a + full runtime test. A green run means migrations and entity metadata are + portable; it does not mean the application works on Postgres. +- Renames of Doctrine type names propagate to migrations via the type system + rather than requiring grep-and-replace across migration files. From 510a2f3af6a67d48562a58f5f524817834b9d693 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Wed, 27 May 2026 16:46:02 +0200 Subject: [PATCH 9/9] docs: correct false claim about down() using raw SQL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The consolidated migration's down() uses $schema->dropTable() — fully Schema tool API like up(). Removed the bullet from ADR 010, the doctrine.yaml header, and docker-compose.postgres.yml that incorrectly listed it as a known portability gap. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/doctrine.yaml | 2 -- docker-compose.postgres.yml | 1 - docs/adr/010-schema-tool-migrations.md | 6 +----- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/doctrine.yaml b/.github/workflows/doctrine.yaml index 0743886a..c76eb6f0 100644 --- a/.github/workflows/doctrine.yaml +++ b/.github/workflows/doctrine.yaml @@ -25,8 +25,6 @@ ### ### - Native MariaDB SQL in entity listeners (`MultiTenantRepositoryTrait`, ### `RelationsChecksumCalculator`) — Postgres would fail at runtime. -### - Migration down() blocks still use raw MariaDB `DROP FOREIGN KEY` -### syntax — irrelevant for forward migration but not portable. ### - Doctrine cache, fixture loading, and most application queries are ### untested against Postgres. ### diff --git a/docker-compose.postgres.yml b/docker-compose.postgres.yml index a777b2ac..efebb574 100644 --- a/docker-compose.postgres.yml +++ b/docker-compose.postgres.yml @@ -14,7 +14,6 @@ # What does NOT work (running the app on Postgres will fail): # - Native MariaDB SQL in `MultiTenantRepositoryTrait` and # `RelationsChecksumCalculator` — runtime queries break on Postgres. -# - Migration down() blocks use raw `DROP FOREIGN KEY` (MariaDB syntax). # - PHPUnit, fixtures, Doctrine cache and most application queries are # untested against Postgres. # diff --git a/docs/adr/010-schema-tool-migrations.md b/docs/adr/010-schema-tool-migrations.md index f1eecd06..b0382755 100644 --- a/docs/adr/010-schema-tool-migrations.md +++ b/docs/adr/010-schema-tool-migrations.md @@ -42,12 +42,8 @@ constants (plus `UlidType::NAME`, `RRuleType::RRULE` for the two custom types in this project) rather than free-form strings, so type renames propagate via the type system. -The two existing exceptions are pre-existing technical debt called out -explicitly: +One existing exception is pre-existing technical debt called out explicitly: -- The consolidated migration's `down()` still uses raw `DROP FOREIGN KEY` (a - MariaDB-only syntax). It runs only on rollback, which is not a supported - upgrade path, so it's left as-is. - Entity listeners and a small set of runtime repository methods still execute native MariaDB SQL. Out of scope for this ADR; tracked separately.