get_charset_collate()` (per §17.2) and relies on * `InnoDB` as the storage engine (§17.1). * * Scope explicitly NOT covered here: * * - `CHECK (type IN ('single','grid'))` constraint. §6.2 describes * the allowed type values as the initial-version enum, but * `dbDelta()` does not reliably round-trip `CHECK` constraints * across MySQL / MariaDB versions (per §13.4's "avoid * environment-sensitive clauses" rule). Application-layer * validation (REST, embed write path) is the canonical enforcement * point, matching the approach used for the `items.length` * constraint described in §7.4. * * - Foreign keys to `wp_users.ID` for `created_by` / `updated_by`. * Per §11.2 and §13.4, AFFILUN does not declare foreign keys at the * DB level; referential integrity is enforced by the application * layer on embed-write paths. * * - `DEFAULT CURRENT_TIMESTAMP` on `created_at` / `updated_at`. Per * §13.4, environment-sensitive default clauses are avoided; * timestamps are supplied by the application layer on insert / * update. * * - Any DML. This migration creates the empty table only; seed data * and backfills belong in later migrations or runtime code paths. * * The shared DDL-assembly and installer-dispatch skeleton lives in * {@see AbstractCreateTableMigration}; this subclass only declares the * bits that actually differ between tables (target version, * description, logical name, column manifest, index manifest). See the * base class's file-level docblock for the idempotency and * failure-propagation guarantees inherited here. * * @package Affilun\Infrastructure\Persistence\Migrations */ declare(strict_types=1); namespace Affilun\Infrastructure\Persistence\Migrations; use Affilun\Infrastructure\Persistence\SchemaVersion; /** * Creates the `affilun_embeds` table at schema version 3. * * Concrete {@see AbstractCreateTableMigration} subclass. See the * file-level docblock for the scope of this migration and the columns * and indexes it installs; see the base class for the formatting rules * and failure semantics that apply to every AFFILUN table-creation * migration. */ final class CreateEmbedsTable extends AbstractCreateTableMigration { /** * Logical, unprefixed AFFILUN table name installed by this migration. * * Kept as a class-level constant so tests can reference the exact * name without duplicating the string literal, and so a future * rename has a single location to update. */ public const TABLE_LOGICAL_NAME = 'affilun_embeds'; /** * Target schema version installed by this migration. * * Returns version 3 — the revision that adds the reusable * embed-definition table on top of `affilun_products` (v1) and * `affilun_product_meta` (v2). See `database-schema.md` §13.2 for the * canonical version numbering. */ public function targetVersion(): SchemaVersion { return SchemaVersion::of( 3 ); } /** * Short, human-readable description of this migration. * * Used by the diagnostics surface and migration logs. Kept terse * and operator-friendly per the * {@see \Affilun\Infrastructure\Persistence\Migration::description()} * contract. */ public function description(): string { return 'create affilun_embeds table'; } /** * Logical, unprefixed AFFILUN table name installed by this migration. * * Consumed by {@see AbstractCreateTableMigration::up()} and * {@see AbstractCreateTableMigration::tableName()} to resolve the * physical, prefix-applied name through the injected * {@see \Affilun\Infrastructure\Persistence\TableNameResolver}. */ protected function logicalTableName(): string { return self::TABLE_LOGICAL_NAME; } /** * Column definitions for `affilun_embeds`, in the canonical order * defined by `database-schema.md` §6.2. * * The `uuid` column is sized `varchar(32)` to accommodate the * prefixed-ULID format `emb_<26 chars Crockford base32>` defined in * §6.2 (4-character prefix `emb_` + 26 base32 characters = 30 * characters, with headroom for format evolution). * * The `type` column is sized `varchar(16)` so the initial enum * (`single` / `grid`) fits comfortably alongside the future * `comparison` extension tracked in §6.2's accompanying note. * * Each entry is a single column definition without its trailing * comma; the base class adds the indent and inter-column commas. * * @return list */ protected function columnDefinitions(): array { return array( 'id bigint unsigned NOT NULL AUTO_INCREMENT', 'uuid varchar(32) NOT NULL', 'name varchar(191) NULL', 'type varchar(16) NOT NULL', 'config_json longtext NOT NULL', 'created_by bigint unsigned NULL', 'updated_by bigint unsigned NULL', 'created_at datetime NOT NULL', 'updated_at datetime NOT NULL', ); } /** * Index definitions for `affilun_embeds`, in the canonical order * defined by `database-schema.md` §6.3. * * The `PRIMARY KEY` entry uses the two-space separator that * `dbDelta()` relies on to detect the primary key line — this is * a WordPress-specific formatting quirk, not a typo. * * @return list */ protected function indexDefinitions(): array { return array( 'PRIMARY KEY (id)', 'UNIQUE KEY uq_uuid (uuid)', 'KEY idx_type (type)', 'KEY idx_created_by (created_by)', 'KEY idx_updated_at (updated_at)', ); } }