diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fe6ac664..5ef696f3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,6 +113,8 @@ - Chg #1002: Remove specific condition interfaces (@vjik) - Chg #1003, #1006: Refactor namespace of condition objects and use promoted properties instead of getters (@vjik) - Enh #1010: Improve `Quoter::getTableNameParts()` method (@Tigrov) +- Enh #1011: Refactor `TableSchemaInterface` and `AbstractSchema` (@Tigrov) +- Enh #1011: Remove `AbstractTableSchema` and add `TableSchema` instead (@Tigrov) ## 1.3.0 March 21, 2024 diff --git a/UPGRADE.md b/UPGRADE.md index c41acad0e..6a214e40f 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -167,6 +167,8 @@ Each table column has its own class in the `Yiisoft\Db\Schema\Column` namespace - `AbstractSchema::getResultColumnCacheKey()` - returns the cache key for the column metadata received from the query; - `AbstractSchema::loadResultColumn()` - creates a new column instance according to the column metadata from the query; - `DataReaderInterface::typecastColumns()` - sets columns for type casting the query results; +- `AbstractSchema::resolveFullName()` - resolves the full name of the table, view, index, etc.; +- `AbstractSchema::clarifyFullName()` - clarifies the full name of the table, view, index, etc.; ### Remove methods @@ -175,6 +177,7 @@ Each table column has its own class in the `Yiisoft\Db\Schema\Column` namespace - `TableSchemaInterface::compositeForeignKey()`; - `SchemaInterface::createColumn()` - use `ColumnBuilder` instead; - `SchemaInterface::isReadQuery()` - use `DbStringHelper::isReadQuery()` instead; +- `AbstractSchema::resolveTableName()` - use `QuoterInterface::getTableNameParts()` instead; - `SchemaInterface::getRawTableName()` - use `QuoterInterface::getRawTableName()` instead; - `AbstractSchema::isReadQuery()` - use `DbStringHelper::isReadQuery()` instead; - `AbstractSchema::getRawTableName()` - use `QuoterInterface::getRawTableName()` instead; @@ -262,3 +265,4 @@ Each table column has its own class in the `Yiisoft\Db\Schema\Column` namespace - Change namespace of condition and condition builder classes; - Remove `AbstractDsn` and `AbstractDsnSocket` classes and `DsnInterface` interface; - Remove `Hash` condition; +- Remove `AbstractTableSchema` and add `TableSchema` instead; diff --git a/src/Driver/Pdo/AbstractPdoSchema.php b/src/Driver/Pdo/AbstractPdoSchema.php index 1217a1226..af5da466a 100644 --- a/src/Driver/Pdo/AbstractPdoSchema.php +++ b/src/Driver/Pdo/AbstractPdoSchema.php @@ -35,7 +35,7 @@ protected function generateCacheKey(): array protected function getCacheKey(string $name): array { - return [static::class, ...$this->generateCacheKey(), $this->db->getQuoter()->getRawTableName($name)]; + return [static::class, ...$this->generateCacheKey(), $name]; } protected function getCacheTag(): string diff --git a/src/Expression/Expression.php b/src/Expression/Expression.php index a891c8a4f..d468b9b79 100644 --- a/src/Expression/Expression.php +++ b/src/Expression/Expression.php @@ -17,7 +17,7 @@ * * ```php * $expression = new Expression('NOW()'); - * $now = (new \Yiisoft\Db\Query\Query)->select($expression)->scalar(); // SELECT NOW(); + * $now = $db->select($expression)->scalar(); // SELECT NOW(); * echo $now; // prints the current date * ``` * diff --git a/src/Schema/AbstractSchema.php b/src/Schema/AbstractSchema.php index ce0532fdd..d65b60313 100644 --- a/src/Schema/AbstractSchema.php +++ b/src/Schema/AbstractSchema.php @@ -15,6 +15,7 @@ use Yiisoft\Db\Constant\GettypeResult; use Yiisoft\Db\Schema\Column\ColumnInterface; +use function array_filter; use function array_key_exists; use function gettype; use function is_array; @@ -34,9 +35,9 @@ abstract class AbstractSchema implements SchemaInterface protected const SCHEMA_CACHE_VERSION = 1; protected const CACHE_VERSION = 'cacheVersion'; /** - * @var string|null $defaultSchema The default schema name used for the current session. + * @var string $defaultSchema The default schema name used for the current session. */ - protected string|null $defaultSchema = null; + protected string $defaultSchema = ''; /** * @var (ColumnInterface|null)[] Saved columns from query results. * @psalm-var array @@ -48,7 +49,7 @@ abstract class AbstractSchema implements SchemaInterface private array $schemaNames = []; /** @var string[][] */ private array $tableNames = []; - /** @var ((Check|DefaultValue|ForeignKey|Index)[]|Index|TableSchemaInterface|null)[][] */ + /** @var (Check[]|DefaultValue[]|ForeignKey[]|Index|Index[]|TableSchemaInterface|null)[][] */ private array $tableMetadata = []; public function __construct(protected ConnectionInterface $db, private SchemaCache $schemaCache) @@ -106,7 +107,7 @@ abstract protected function loadTableDefaultValues(string $tableName): array; * * @param string $tableName The table name. * - * @return ForeignKey[] The foreign keys for the given table. + * @return ForeignKey[] The foreign keys for the given table, indexed by constraint name. */ abstract protected function loadTableForeignKeys(string $tableName): array; @@ -119,24 +120,6 @@ abstract protected function loadTableForeignKeys(string $tableName): array; */ abstract protected function loadTableIndexes(string $tableName): array; - /** - * Loads a primary key for the given table. - * - * @param string $tableName The table name. - * - * @return Index|null The primary key for the given table. `null` if the table has no primary key. - */ - abstract protected function loadTablePrimaryKey(string $tableName): Index|null; - - /** - * Loads all unique constraints for the given table. - * - * @param string $tableName The table name. - * - * @return Index[] The unique constraints for the given table. - */ - abstract protected function loadTableUniques(string $tableName): array; - /** * Loads the metadata for the specified table. * @@ -146,7 +129,7 @@ abstract protected function loadTableUniques(string $tableName): array; */ abstract protected function loadTableSchema(string $name): TableSchemaInterface|null; - public function getDefaultSchema(): string|null + public function getDefaultSchema(): string { return $this->defaultSchema; } @@ -244,25 +227,25 @@ public function getSchemaUniques(string $schema = '', bool $refresh = false): ar public function getTableChecks(string $name, bool $refresh = false): array { /** @var Check[] */ - return $this->getTableMetadata($name, SchemaInterface::CHECKS, $refresh); + return $this->getTableMetadata($this->clearFullName($name), SchemaInterface::CHECKS, $refresh); } public function getTableDefaultValues(string $name, bool $refresh = false): array { /** @var DefaultValue[] */ - return $this->getTableMetadata($name, SchemaInterface::DEFAULT_VALUES, $refresh); + return $this->getTableMetadata($this->clearFullName($name), SchemaInterface::DEFAULT_VALUES, $refresh); } public function getTableForeignKeys(string $name, bool $refresh = false): array { /** @var ForeignKey[] */ - return $this->getTableMetadata($name, SchemaInterface::FOREIGN_KEYS, $refresh); + return $this->getTableMetadata($this->clearFullName($name), SchemaInterface::FOREIGN_KEYS, $refresh); } public function getTableIndexes(string $name, bool $refresh = false): array { /** @var Index[] */ - return $this->getTableMetadata($name, SchemaInterface::INDEXES, $refresh); + return $this->getTableMetadata($this->clearFullName($name), SchemaInterface::INDEXES, $refresh); } public function getTableNames(string $schema = '', bool $refresh = false): array @@ -276,14 +259,31 @@ public function getTableNames(string $schema = '', bool $refresh = false): array public function getTablePrimaryKey(string $name, bool $refresh = false): Index|null { - /** @var Index|null */ - return $this->getTableMetadata($name, SchemaInterface::PRIMARY_KEY, $refresh); + foreach ($this->getTableIndexes($name, $refresh) as $index) { + if ($index->isPrimaryKey) { + return $index; + } + } + + return null; } public function getTableSchema(string $name, bool $refresh = false): TableSchemaInterface|null { + $rawName = $this->clearFullName($name); + + if ($refresh) { + // Some constraints are loading and caching together. + // Reset the table constraint cache to load them without refreshing. + $this->tableMetadata[$rawName] = []; + + if ($this->schemaCache->isEnabled()) { + $this->schemaCache->remove($this->getCacheKey($rawName)); + } + } + /** @var TableSchemaInterface|null */ - return $this->getTableMetadata($name, SchemaInterface::SCHEMA, $refresh); + return $this->getTableMetadata($rawName, SchemaInterface::SCHEMA, $refresh); } public function getTableSchemas(string $schema = '', bool $refresh = false): array @@ -294,8 +294,10 @@ public function getTableSchemas(string $schema = '', bool $refresh = false): arr public function getTableUniques(string $name, bool $refresh = false): array { - /** @var Index[] */ - return $this->getTableMetadata($name, SchemaInterface::UNIQUES, $refresh); + return array_filter( + $this->getTableIndexes($name, $refresh), + static fn (Index $index): bool => $index->isUnique + ); } public function refresh(): void @@ -310,7 +312,7 @@ public function refresh(): void public function refreshTableSchema(string $name): void { - $rawName = $this->db->getQuoter()->getRawTableName($name); + $rawName = $this->clearFullName($name); unset($this->tableMetadata[$rawName]); @@ -326,6 +328,23 @@ public function enableCache(bool $value): void $this->schemaCache->setEnabled($value); } + /** + * @deprecated Use {@see getTableUniques()}. Will be removed in version 3.0 + */ + public function findUniqueIndexes(TableSchemaInterface $table): array + { + $uniques = []; + $indexes = $this->getTableIndexes($table->getFullName()); + + foreach ($indexes as $index) { + if ($index->isUnique) { + $uniques[$index->name] = $index->columnNames; + } + } + + return $uniques; + } + /** * Returns all schema names in the database, including the default one but not system schemas. * @@ -367,7 +386,7 @@ protected function findTableNames(string $schema): array * @param bool $refresh Whether to fetch the latest available table metadata. If this is `false`, cached data may be * returned if available. * - * @return (Check|DefaultValue|ForeignKey|Index)[][]|Index[]|TableSchemaInterface[] The metadata of the given type for all + * @return Check[][]|DefaultValue[][]|ForeignKey[][]|Index[]|Index[][]|TableSchemaInterface[] The metadata of the given type for all * tables in the given schema. */ protected function getSchemaMetadata(string $schema, string $type, bool $refresh): array @@ -401,39 +420,44 @@ protected function getSchemaMetadata(string $schema, string $type, bool $refresh * @param string $type The metadata type. * @param bool $refresh whether to reload the table metadata even if it's found in the cache. * - * @return (Check|DefaultValue|ForeignKey|Index)[]|Index|TableSchemaInterface|null The metadata of the given type + * @return Check[]|DefaultValue[]|ForeignKey[]|Index|Index[]|TableSchemaInterface|null The metadata of the given type * for the given table. + * + * @psalm-return ( + * $type is SchemaInterface::CHECKS ? Check[] : + * $type is SchemaInterface::DEFAULT_VALUES ? DefaultValue[] : + * $type is SchemaInterface::FOREIGN_KEYS ? ForeignKey[] : + * $type is SchemaInterface::INDEXES ? Index[] : + * $type is SchemaInterface::PRIMARY_KEY ? Index|null : + * TableSchemaInterface + * ) */ protected function getTableMetadata( string $name, string $type, bool $refresh = false, ): array|Index|TableSchemaInterface|null { - $rawName = $this->db->getQuoter()->getRawTableName($name); - - if (!isset($this->tableMetadata[$rawName])) { - $this->loadTableMetadataFromCache($rawName); + if (!isset($this->tableMetadata[$name])) { + $this->loadTableMetadataFromCache($name); } - if ($refresh || !isset($this->tableMetadata[$rawName][$type])) { - $this->tableMetadata[$rawName][$type] = $this->loadTableTypeMetadata($type, $rawName); - $this->saveTableMetadataToCache($rawName); + if ($refresh || !isset($this->tableMetadata[$name][$type])) { + $this->tableMetadata[$name][$type] = $this->loadTableTypeMetadata($type, $name); + $this->saveTableMetadataToCache($name); } - return $this->tableMetadata[$rawName][$type]; + return $this->tableMetadata[$name][$type]; } /** * This method returns the desired metadata type for the table name. * - * @return (Check|DefaultValue|ForeignKey|Index)[]|Index|TableSchemaInterface|null + * @return Check[]|DefaultValue[]|ForeignKey[]|Index[]|TableSchemaInterface|null */ - protected function loadTableTypeMetadata(string $type, string $name): array|Index|TableSchemaInterface|null + protected function loadTableTypeMetadata(string $type, string $name): array|TableSchemaInterface|null { return match ($type) { SchemaInterface::SCHEMA => $this->loadTableSchema($name), - SchemaInterface::PRIMARY_KEY => $this->loadTablePrimaryKey($name), - SchemaInterface::UNIQUES => $this->loadTableUniques($name), SchemaInterface::FOREIGN_KEYS => $this->loadTableForeignKeys($name), SchemaInterface::INDEXES => $this->loadTableIndexes($name), SchemaInterface::DEFAULT_VALUES => $this->loadTableDefaultValues($name), @@ -445,7 +469,7 @@ protected function loadTableTypeMetadata(string $type, string $name): array|Inde /** * This method returns the desired metadata type for table name (with refresh if needed). * - * @return (Check|DefaultValue|ForeignKey|Index)[]|Index|TableSchemaInterface|null + * @return Check[]|DefaultValue[]|ForeignKey[]|Index|Index[]|TableSchemaInterface|null */ protected function getTableTypeMetadata( string $type, @@ -465,17 +489,42 @@ protected function getTableTypeMetadata( } /** - * Resolves the table name and schema name (if any). - * - * @param string $name The table name. - * - * @throws NotSupportedException If the DBMS doesn't support this method. + * Clears the full name. Removes the schema name if it is the default schema name, removes curly brackets + * from the name, and replaces the percentage character '%' with {@see ConnectionInterface::getTablePrefix()}. + */ + protected function clearFullName(string $fullName): string + { + return $this->resolveFullName(...$this->db->getQuoter()->getTableNameParts($fullName)); + } + + /** + * Find and initialize table constraints. * - * @return TableSchemaInterface The with resolved table, schema, etc. names. + * @param TableSchemaInterface $table The table metadata. + */ + protected function findConstraints(TableSchemaInterface $table): void + { + $tableName = $this->resolveFullName($table->getName(), $table->getSchemaName()); + + $table->checks(...$this->getTableMetadata($tableName, SchemaInterface::CHECKS)); + $table->defaultValues(...$this->getTableMetadata($tableName, SchemaInterface::DEFAULT_VALUES)); + $table->foreignKeys(...$this->getTableMetadata($tableName, SchemaInterface::FOREIGN_KEYS)); + $table->indexes(...$this->getTableMetadata($tableName, SchemaInterface::INDEXES)); + } + + /** + * Resolves the full table name, considering the default schema name. Removes curly brackets from the names, + * and replaces the percentage character '%' with {@see ConnectionInterface::getTablePrefix()}. */ - protected function resolveTableName(string $name): TableSchemaInterface + protected function resolveFullName(string $name, string $schemaName = ''): string { - throw new NotSupportedException(static::class . ' does not support resolving table names.'); + $quoter = $this->db->getQuoter(); + $rawName = $quoter->getRawTableName($name); + + return match ($schemaName) { + '', $this->defaultSchema => $rawName, + default => $quoter->getRawTableName($schemaName) . ".$rawName", + }; } /** @@ -483,7 +532,7 @@ protected function resolveTableName(string $name): TableSchemaInterface * * @param string $rawName The raw table name. * @param string $type The metadata type. - * @param (Check|DefaultValue|ForeignKey|Index)[]|Index|TableSchemaInterface|null $data The metadata to set. + * @param Check[]|DefaultValue[]|ForeignKey[]|Index|Index[]|TableSchemaInterface|null $data The metadata to set. */ protected function setTableMetadata( string $rawName, @@ -515,7 +564,7 @@ private function loadTableMetadataFromCache(string $rawName): void } unset($metadata[self::CACHE_VERSION]); - /** @var ((Check|DefaultValue|ForeignKey|Index)[]|Index|TableSchemaInterface|null)[] $metadata */ + /** @var (Check[]|DefaultValue[]|ForeignKey[]|Index|Index[]|TableSchemaInterface|null)[] $metadata */ $this->tableMetadata[$rawName] = $metadata; } diff --git a/src/Schema/AbstractTableSchema.php b/src/Schema/AbstractTableSchema.php deleted file mode 100644 index ad52b58fc..000000000 --- a/src/Schema/AbstractTableSchema.php +++ /dev/null @@ -1,155 +0,0 @@ - */ - private array $columns = []; - /** @psalm-var array */ - protected array $foreignKeys = []; - protected string|null $createSql = null; - private string|null $catalogName = null; - private string|null $serverName = null; - - public function getColumn(string $name): ColumnInterface|null - { - return $this->columns[$name] ?? null; - } - - public function getColumnNames(): array - { - return array_keys($this->columns); - } - - public function getSchemaName(): string|null - { - return $this->schemaName; - } - - public function getName(): string - { - return $this->name; - } - - public function getFullName(): string|null - { - return $this->fullName; - } - - public function getSequenceName(): string|null - { - return $this->sequenceName; - } - - public function getPrimaryKey(): array - { - return $this->primaryKey; - } - - public function getColumns(): array - { - return $this->columns; - } - - public function getComment(): string|null - { - return $this->comment; - } - - public function schemaName(string|null $value): void - { - $this->schemaName = $value; - } - - public function name(string $value): void - { - $this->name = $value; - } - - public function fullName(string|null $value): void - { - $this->fullName = $value; - } - - public function comment(string|null $value): void - { - $this->comment = $value; - } - - public function sequenceName(string|null $value): void - { - $this->sequenceName = $value; - } - - public function primaryKey(string $value): void - { - $this->primaryKey[] = $value; - } - - public function column(string $name, ColumnInterface $value): void - { - $this->columns[$name] = $value; - } - - public function getCatalogName(): string|null - { - return $this->catalogName; - } - - public function catalogName(string|null $value): void - { - $this->catalogName = $value; - } - - public function getServerName(): string|null - { - return $this->serverName; - } - - public function serverName(string|null $value): void - { - $this->serverName = $value; - } - - public function getCreateSql(): string|null - { - return $this->createSql; - } - - public function createSql(string $sql): void - { - $this->createSql = $sql; - } - - public function getForeignKeys(): array - { - return $this->foreignKeys; - } - - public function foreignKeys(array $value): void - { - $this->foreignKeys = $value; - } - - public function foreignKey(string|int $id, array $to): void - { - $this->foreignKeys[$id] = $to; - } -} diff --git a/src/Schema/SchemaInterface.php b/src/Schema/SchemaInterface.php index 196691cc4..5d1794227 100644 --- a/src/Schema/SchemaInterface.php +++ b/src/Schema/SchemaInterface.php @@ -296,9 +296,9 @@ interface SchemaInterface extends ConstraintSchemaInterface public const TYPE_JSON = 'json'; /** - * @return string|null The default schema name. + * @return string The default schema name. */ - public function getDefaultSchema(): string|null; + public function getDefaultSchema(): string; /** * Determines the SQL data type for the given PHP data value. @@ -356,6 +356,8 @@ public function getTableNames(string $schema = '', bool $refresh = false): array * @param TableSchemaInterface $table The table metadata. * * @return string[][] All unique indexes for the given table. + * + * @deprecated Use {@see getTableUniques()}. Will be removed in version 3.0 */ public function findUniqueIndexes(TableSchemaInterface $table): array; diff --git a/src/Schema/TableSchema.php b/src/Schema/TableSchema.php new file mode 100644 index 000000000..9ab096f86 --- /dev/null +++ b/src/Schema/TableSchema.php @@ -0,0 +1,212 @@ + + */ + private array $columns = []; + private string|null $comment = null; + private string|null $createSql = null; + /** @var DefaultValue[] */ + private array $defaultValues = []; + /** @var ForeignKey[] */ + private array $foreignKeys = []; + /** @var Index[] */ + private array $indexes = []; + /** @var string[] */ + private array $options = []; + private string|null $sequenceName = null; + + public function __construct( + private string $name = '', + private string $schemaName = '', + ) { + } + + public function checks(Check ...$checks): static + { + $this->checks = $checks; + return $this; + } + + public function column(string $name, ColumnInterface $column): static + { + $this->columns[$name] = $column; + return $this; + } + + public function columns(array $columns): static + { + $this->columns = $columns; + return $this; + } + + public function comment(string|null $comment): static + { + $this->comment = $comment; + return $this; + } + + public function createSql(string|null $sql): static + { + $this->createSql = $sql; + return $this; + } + + public function defaultValues(DefaultValue ...$defaultValues): static + { + $this->defaultValues = $defaultValues; + return $this; + } + + public function foreignKeys(ForeignKey ...$foreignKeys): static + { + $this->foreignKeys = $foreignKeys; + return $this; + } + + public function getChecks(): array + { + return $this->checks; + } + + public function getColumn(string $name): ColumnInterface|null + { + return $this->columns[$name] ?? null; + } + + public function getColumnNames(): array + { + return array_keys($this->columns); + } + + public function getColumns(): array + { + return $this->columns; + } + + public function getComment(): string|null + { + return $this->comment; + } + + public function getCreateSql(): string|null + { + return $this->createSql; + } + + public function getDefaultValues(): array + { + return $this->defaultValues; + } + + public function getForeignKeys(): array + { + return $this->foreignKeys; + } + + public function getFullName(): string + { + if ($this->schemaName === '') { + return $this->name; + } + + return "$this->schemaName.$this->name"; + } + + public function getIndexes(): array + { + return $this->indexes; + } + + public function getName(): string + { + return $this->name; + } + + public function getOptions(): array + { + return $this->options; + } + + public function getPrimaryKey(): array + { + foreach ($this->indexes as $index) { + if ($index->isPrimaryKey) { + return $index->columnNames; + } + } + + return array_keys( + array_filter( + $this->columns, + static fn (ColumnInterface $column) => $column->isPrimaryKey() + ) + ); + } + + public function getSchemaName(): string + { + return $this->schemaName; + } + + public function getSequenceName(): string|null + { + return $this->sequenceName; + } + + public function getUniques(): array + { + return array_filter($this->indexes, static fn (Index $index): bool => $index->isUnique); + } + + public function indexes(Index ...$indexes): static + { + $this->indexes = $indexes; + return $this; + } + + public function name(string $name): static + { + $this->name = $name; + return $this; + } + + public function options(string ...$options): static + { + $this->options = $options; + return $this; + } + + public function schemaName(string $schemaName): static + { + $this->schemaName = $schemaName; + return $this; + } + + public function sequenceName(string|null $sequenceName): static + { + $this->sequenceName = $sequenceName; + return $this; + } +} diff --git a/src/Schema/TableSchemaInterface.php b/src/Schema/TableSchemaInterface.php index 23def3f57..23bdbfb38 100644 --- a/src/Schema/TableSchemaInterface.php +++ b/src/Schema/TableSchemaInterface.php @@ -4,6 +4,10 @@ namespace Yiisoft\Db\Schema; +use Yiisoft\Db\Constraint\Check; +use Yiisoft\Db\Constraint\DefaultValue; +use Yiisoft\Db\Constraint\ForeignKey; +use Yiisoft\Db\Constraint\Index; use Yiisoft\Db\Schema\Column\ColumnInterface; /** @@ -16,54 +20,69 @@ interface TableSchemaInterface { /** - * Gets the named column metadata. + * Set check constraints of this table. * - * This is a convenient method for retrieving a named column even if it doesn't exist. + * @param Check ...$checks The check constraints. + */ + public function checks(Check ...$checks): static; + + /** + * Set metadata for a column specified. * * @param string $name The column name. - * - * @return ColumnInterface|null The named column metadata. Null if the named column doesn't exist. */ - public function getColumn(string $name): ColumnInterface|null; + public function column(string $name, ColumnInterface $column): static; /** - * @return string[] The names of all columns in this table. + * Set metadata for multiple columns. + * + * @param ColumnInterface[] $columns The columns metadata indexed by column names. + * @psalm-param array $columns */ - public function getColumnNames(): array; + public function columns(array $columns): static; /** - * @return string|null The comment of the table. Null if no comment. + * Set the comment of the table or `null` if there is no comment. + * Not all DBMS support this. */ - public function getComment(): string|null; + public function comment(string|null $comment): static; /** - * @return string|null The name of the schema that this table belongs to. + * Set SQL for creating this table, empty string if it is not found, or `null` if it is not initialized. + * Supported by MySQL and Oracle DBMS. */ - public function getSchemaName(): string|null; + public function createSql(string|null $sql): static; /** - * @return string The name of this table. The schema name isn't included. Use {@see fullName} to get the name with - * schema name prefix. + * Set default value constraints of this table. + * + * @param DefaultValue ...$defaultValues The default value constraints. */ - public function getName(): string; + public function defaultValues(DefaultValue ...$defaultValues): static; /** - * @return string|null The full name of this table, which includes the schema name prefix, if any. Note that if the - * schema name is the same as the {@see Schema::defaultSchema} schema name, the schema name won't be included. + * Set foreign key constraints of this table. + * + * @param ForeignKey ...$foreignKeys The foreign key constraints. */ - public function getFullName(): string|null; + public function foreignKeys(ForeignKey ...$foreignKeys): static; /** - * @return string|null The sequence name for the primary key. Null if no sequence. + * @return Check[] The check constraints of this table. */ - public function getSequenceName(): string|null; + public function getChecks(): array; /** - * @return array The primary key column names. + * Returns the named column metadata or `null` if the named column does not exist. * - * @psalm-return string[] + * @param string $name The column name. */ - public function getPrimaryKey(): array; + public function getColumn(string $name): ColumnInterface|null; + + /** + * @return string[] The names of all columns in this table. + */ + public function getColumnNames(): array; /** * @return ColumnInterface[] The column metadata of this table. @@ -74,131 +93,103 @@ public function getPrimaryKey(): array; public function getColumns(): array; /** - * Set the name of the schema that this table belongs to. - * - * @param string|null $value The name of the schema that this table belongs to. + * Returns the comment of the table or `null` if no comment. */ - public function schemaName(string|null $value): void; + public function getComment(): string|null; /** - * Set the name of this table. - * - * The schema name isn't included. Use {@see fullName} to set the name with schema name prefix. - * - * @param string $value The name of this table. + * Returns SQL for create this table, empty string if it is not found, or `null` if it is not initialized. + * Supported by MySQL and Oracle DBMS. */ - public function name(string $value): void; + public function getCreateSql(): string|null; /** - * Set the full name of this table, which includes the schema name prefix, if any. Note that if the schema name is - * the same as the {@see Schema::defaultSchema} schema name, the schema name won't be included. - * - * @param string|null $value The full name of this table. + * @return DefaultValue[] The default value constraints of this table. */ - public function fullName(string|null $value): void; + public function getDefaultValues(): array; /** - * Set the comment of the table. - * - * Null if no comment. This isn't supported by all DBMS. - * - * @param string|null $value The comment of the table. + * @return ForeignKey[] The foreign key constraints of this table. */ - public function comment(string|null $value): void; + public function getForeignKeys(): array; /** - * Set sequence name for the primary key. - * - * @param string|null $value The sequence name for the primary key. Null if no sequence. + * Returns the full name of this table including the schema name. */ - public function sequenceName(string|null $value): void; + public function getFullName(): string; /** - * Set primary keys of this table. + * Returns the index constraints of this table. * - * @param string $value The primary key column name. + * @return Index[] The index constraints of this table. */ - public function primaryKey(string $value): void; + public function getIndexes(): array; /** - * Set one column metadata of this table. - * - * @param string $name The column name. + * Returns the table name. The schema name is not included. Use {@see getFullName()} to get the table name with + * the schema name. */ - public function column(string $name, ColumnInterface $value): void; + public function getName(): string; /** - * @return string|null The name of the catalog (database) that this table belongs to. Defaults to null, meaning no - * catalog (or the current database). + * Returns the options of this table. * - * Specifically for MSSQL Server + * @return string[] The options of this table. */ - public function getCatalogName(): string|null; + public function getOptions(): array; /** - * Set name of the catalog (database) that this table belongs to. Defaults to null, meaning no catalog (or the - * current database). Specifically for MSSQL Server + * @return array The primary key column names. * - * @param string|null $value The name of the catalog (database) that this table belongs to. + * @psalm-return string[] */ - public function catalogName(string|null $value): void; + public function getPrimaryKey(): array; /** - * @return string|null The name of the server that this table belongs to. Defaults to null, meaning no server - * (or the current server). - * - * Specifically for MSSQL Server + * Returns the schema name that this table belongs to. */ - public function getServerName(): string|null; + public function getSchemaName(): string; /** - * Set name of the server that this table belongs to. Defaults to null, meaning no server (or the current server). - * Specifically for MSSQL Server - * - * @param string|null $value The name of the server that this table belongs to. + * Return the sequence name for the primary key or `null` if no sequence. */ - public function serverName(string|null $value): void; + public function getSequenceName(): string|null; /** - * @return string|null The SQL for create current table or `null` if a query not found/exists. Now supported only in - * MySQL and Oracle DBMS. + * Returns the unique indexes of this table. + * + * @return Index[] The unique indexes of this table. */ - public function getCreateSql(): string|null; + public function getUniques(): array; /** - * Set SQL for create current table or null if a query not found/exists. Now supported only in MySQL and Oracle DBMS. + * Set indexes of this table. * - * @param string $sql The SQL for create current table or `null` if a query not found/exists. + * @param Index ...$indexes The indexes. */ - public function createSql(string $sql): void; + public function indexes(Index ...$indexes): static; /** - * @return array The foreign keys of this table. Each array element is of the following structure: - * - * ```php - * [ - * 'ForeignTableName', - * 'fk1' => 'pk1', // pk1 is in foreign table - * 'fk2' => 'pk2', // if composite foreign key - * ] - * ``` + * Set the name of this table. * - * @psalm-return array + * The schema name must not be included. Use {@see schemaName()} to set the table schema name. */ - public function getForeignKeys(): array; + public function name(string $name): static; /** - * Set foreign keys of this table. + * Set the options of this table. * - * @psalm-param array $value The foreign keys of this table. + * @param string ...$options The options. */ - public function foreignKeys(array $value): void; + public function options(string ...$options): static; /** - * Set one foreignKey by index. - * - * @param int|string $id The index of foreign key. - * @param array $to The foreign key. + * Set the name of the schema that this table belongs to or empty string for the default schema. + */ + public function schemaName(string $schemaName): static; + + /** + * Set a sequence name for the primary key or `null` if no sequence. */ - public function foreignKey(string|int $id, array $to): void; + public function sequenceName(string|null $sequenceName): static; } diff --git a/tests/AbstractSchemaTest.php b/tests/AbstractSchemaTest.php index 68d7c87f9..3ab8340c9 100644 --- a/tests/AbstractSchemaTest.php +++ b/tests/AbstractSchemaTest.php @@ -23,7 +23,7 @@ public function testGetDefaultSchema(): void $schema = $db->getSchema(); - $this->assertNull($schema->getDefaultSchema()); + $this->assertSame('', $schema->getDefaultSchema()); } public function testGetDataType(): void diff --git a/tests/AbstractTableSchemaTest.php b/tests/AbstractTableSchemaTest.php index e7eb40dea..bf580ca7e 100644 --- a/tests/AbstractTableSchemaTest.php +++ b/tests/AbstractTableSchemaTest.php @@ -5,23 +5,53 @@ namespace Yiisoft\Db\Tests; use PHPUnit\Framework\TestCase; -use Yiisoft\Db\Tests\Support\Stub\Column; -use Yiisoft\Db\Tests\Support\Stub\TableSchema; +use Yiisoft\Db\Constraint\Check; +use Yiisoft\Db\Constraint\DefaultValue; +use Yiisoft\Db\Constraint\ForeignKey; +use Yiisoft\Db\Constraint\Index; +use Yiisoft\Db\Schema\Column\ColumnBuilder; +use Yiisoft\Db\Schema\TableSchema; use Yiisoft\Db\Tests\Support\TestTrait; abstract class AbstractTableSchemaTest extends TestCase { use TestTrait; - public function testGetCatalogName(): void + public function testConstructorEmpty(): void { $tableSchema = new TableSchema(); - $this->assertNull($tableSchema->getCatalogName()); + $this->assertSame('', $tableSchema->getName()); + $this->assertSame('', $tableSchema->getFullName()); + $this->assertSame('', $tableSchema->getSchemaName()); + $this->assertSame([], $tableSchema->getChecks()); + $this->assertSame([], $tableSchema->getColumns()); + $this->assertSame([], $tableSchema->getColumnNames()); + $this->assertNull($tableSchema->getComment()); + $this->assertNull($tableSchema->getCreateSql()); + $this->assertSame([], $tableSchema->getDefaultValues()); + $this->assertSame([], $tableSchema->getForeignKeys()); + $this->assertSame([], $tableSchema->getPrimaryKey()); + $this->assertNull($tableSchema->getSequenceName()); + $this->assertSame([], $tableSchema->getUniques()); + } - $tableSchema->catalogName('test'); + public function testConstructorWithTable(): void + { + $tableSchema = new TableSchema('test'); - $this->assertSame('test', $tableSchema->getCatalogName()); + $this->assertSame('test', $tableSchema->getName()); + $this->assertSame('test', $tableSchema->getFullName()); + $this->assertSame('', $tableSchema->getSchemaName()); + } + + public function testConstructorWithTableSchema(): void + { + $tableSchema = new TableSchema('test', 'yiisoft'); + + $this->assertSame('test', $tableSchema->getName()); + $this->assertSame('yiisoft.test', $tableSchema->getFullName()); + $this->assertSame('yiisoft', $tableSchema->getSchemaName()); } public function testGetComment(): void @@ -37,7 +67,7 @@ public function testGetComment(): void public function testGetColumn(): void { - $column = new Column('id'); + $column = ColumnBuilder::primaryKey(); $tableSchema = new TableSchema(); $this->assertNull($tableSchema->getColumn('id')); @@ -49,7 +79,7 @@ public function testGetColumn(): void public function testGetColumns(): void { - $column = new Column('id'); + $column = ColumnBuilder::primaryKey(); $tableSchema = new TableSchema(); $this->assertSame([], $tableSchema->getColumns()); @@ -61,7 +91,7 @@ public function testGetColumns(): void public function testGetColumnName(): void { - $column = new Column('id'); + $column = ColumnBuilder::primaryKey(); $tableSchema = new TableSchema(); $this->assertNull($tableSchema->getColumn('id')); @@ -91,37 +121,70 @@ public function testGetCreateSql(): void ); } - public function testGetForeignKeys(): void + public function testChecks(): void { $tableSchema = new TableSchema(); - $this->assertSame([], $tableSchema->getForeignKeys()); + $this->assertSame([], $tableSchema->getChecks()); + + $checks = ['check1' => new Check('check1')]; + $tableSchema->checks(...$checks); + + $this->assertSame($checks, $tableSchema->getChecks()); + } + + public function testDefaultValues(): void + { + $tableSchema = new TableSchema(); + + $this->assertSame([], $tableSchema->getDefaultValues()); - $tableSchema->foreignKeys(['id']); + $defaults = ['value1' => new DefaultValue('value1')]; + $tableSchema->defaultValues(...$defaults); - $this->assertSame(['id'], $tableSchema->getForeignKeys()); + $this->assertSame($defaults, $tableSchema->getDefaultValues()); } - public function testGetForeignKeysAndForeingKey(): void + public function testForeignKeys(): void { $tableSchema = new TableSchema(); $this->assertSame([], $tableSchema->getForeignKeys()); - $tableSchema->foreignKey('id', ['test', 'id']); + $foreignKeys = ['fk1' => new ForeignKey('fk1')]; + $tableSchema->foreignKeys(...$foreignKeys); - $this->assertSame(['id' => ['test', 'id']], $tableSchema->getForeignKeys()); + $this->assertSame($foreignKeys, $tableSchema->getForeignKeys()); } - public function testGetFullName(): void + public function testIndexes(): void { $tableSchema = new TableSchema(); - $this->assertEmpty($tableSchema->getFullName()); + $this->assertSame([], $tableSchema->getIndexes()); - $tableSchema->fullName('test'); + $indexes = [ + 'pk' => new Index('pk', ['id'], true, true), + 'index1' => new Index('index1'), + 'unique1' => new Index('unique1', ['unic_column'], true), + ]; + $tableSchema->indexes(...$indexes); - $this->assertSame('test', $tableSchema->getFullName()); + $this->assertSame($indexes, $tableSchema->getIndexes()); + $this->assertSame(['id'], $tableSchema->getPrimaryKey()); + $this->assertSame(['pk' => $indexes['pk'], 'unique1' => $indexes['unique1']], $tableSchema->getUniques()); + } + + public function testOptions(): void + { + $tableSchema = new TableSchema(); + + $this->assertSame([], $tableSchema->getOptions()); + + $options = ['ROW_FORMAT FIXED']; + $tableSchema->options(...$options); + + $this->assertSame($options, $tableSchema->getOptions()); } public function testGetName(): void @@ -141,38 +204,27 @@ public function testGetPrimaryKey(): void $this->assertSame([], $tableSchema->getPrimaryKey()); - $tableSchema->primaryKey('id'); + $tableSchema->column('id', ColumnBuilder::primaryKey()); $this->assertSame(['id'], $tableSchema->getPrimaryKey()); } - public function testGetSequencName(): void + public function testGetSequenceName(): void { $tableSchema = new TableSchema(); - $this->assertEmpty($tableSchema->getSequenceName()); + $this->assertNull($tableSchema->getSequenceName()); $tableSchema->sequenceName('test'); $this->assertSame('test', $tableSchema->getSequenceName()); } - public function testGetServerName(): void - { - $tableSchema = new TableSchema(); - - $this->assertEmpty($tableSchema->getServerName()); - - $tableSchema->serverName('test'); - - $this->assertSame('test', $tableSchema->getServerName()); - } - public function testGetSchemaName(): void { $tableSchema = new TableSchema(); - $this->assertNull($tableSchema->getSchemaName()); + $this->assertSame('', $tableSchema->getSchemaName()); $tableSchema->schemaName('test'); diff --git a/tests/Common/CommonCommandTest.php b/tests/Common/CommonCommandTest.php index b692a8c4d..dfcd0854b 100644 --- a/tests/Common/CommonCommandTest.php +++ b/tests/Common/CommonCommandTest.php @@ -16,8 +16,6 @@ use Yiisoft\Db\Exception\IntegrityException; use InvalidArgumentException; use Yiisoft\Db\Exception\InvalidCallException; -use Yiisoft\Db\Exception\InvalidConfigException; -use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\Expression\Expression; use Yiisoft\Db\Expression\ExpressionInterface; use Yiisoft\Db\Helper\DbUuidHelper; @@ -38,11 +36,6 @@ abstract class CommonCommandTest extends AbstractCommandTest { - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testAddCheck(): void { $db = $this->getConnection(); @@ -62,17 +55,12 @@ public function testAddCheck(): void $this->assertMatchesRegularExpression( '/^.*int1.*>.*1.*$/', - $schema->getTableChecks('{{test_ck}}', true)[0]->expression + $schema->getTableChecks('{{test_ck}}')['test_ck_constraint']->expression ); $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testAddColumn(): void { $db = $this->getConnection(true); @@ -89,11 +77,6 @@ public function testAddColumn(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testAddCommentOnColumn(): void { $db = $this->getConnection(true); @@ -111,11 +94,6 @@ public function testAddCommentOnColumn(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testAddCommentOnTable(): void { $db = $this->getConnection(true); @@ -145,11 +123,6 @@ public function testResetSequenceSql(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testAddDefaultValue(): void { $db = $this->getConnection(); @@ -163,25 +136,19 @@ public function testAddDefaultValue(): void $command->createTable('{{test_def}}', ['int1' => ColumnType::INTEGER])->execute(); - $this->assertEmpty($schema->getTableDefaultValues('{{test_def}}', true)); + $this->assertEmpty($schema->getTableDefaultValues('{{test_def}}')); $command->addDefaultValue('{{test_def}}', '{{test_def_constraint}}', 'int1', 41)->execute(); $this->assertMatchesRegularExpression( '/^.*41.*$/', - $schema->getTableDefaultValues('{{test_def}}', true)[0]->value, + $schema->getTableDefaultValues('{{test_def}}')['test_def_constraint']->value, ); $db->close(); } - /** - * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::addForeignKey - * - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ + #[DataProviderExternal(CommandProvider::class, 'addForeignKey')] public function testAddForeignKey( string $name, string $tableName, @@ -210,10 +177,10 @@ public function testAddForeignKey( ], )->execute(); - $this->assertEmpty($schema->getTableForeignKeys($tableName, true)); + $this->assertEmpty($schema->getTableForeignKeys($tableName)); $command->addForeignKey($tableName, $name, $column1, $tableName, $column2)->execute(); - $foreignKey = $schema->getTableForeignKeys($tableName, true)[0]; + $foreignKey = $schema->getTableForeignKeys($tableName)[$db->getQuoter()->getRawTableName($name)]; $this->assertSame($expectedName, $foreignKey->name); @@ -232,13 +199,7 @@ public function testAddForeignKey( $db->close(); } - /** - * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::addPrimaryKey - * - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ + #[DataProviderExternal(CommandProvider::class, 'addPrimaryKey')] public function testAddPrimaryKey(string $name, string $tableName, array|string $column): void { $db = $this->getConnection(); @@ -252,7 +213,7 @@ public function testAddPrimaryKey(string $name, string $tableName, array|string $command->createTable($tableName, ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute(); - $this->assertNull($schema->getTablePrimaryKey($tableName, true)); + $this->assertNull($schema->getTablePrimaryKey($tableName)); $db->createCommand()->addPrimaryKey($tableName, $name, $column)->execute(); @@ -260,18 +221,12 @@ public function testAddPrimaryKey(string $name, string $tableName, array|string $column = [$column]; } - $this->assertSame($column, $schema->getTablePrimaryKey($tableName, true)->columnNames); + $this->assertSame($column, $schema->getTablePrimaryKey($tableName)->columnNames); $db->close(); } - /** - * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::addUnique - * - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ + #[DataProviderExternal(CommandProvider::class, 'addUnique')] public function testAddUnique(string $name, string $tableName, array|string $column): void { $db = $this->getConnection(); @@ -285,7 +240,7 @@ public function testAddUnique(string $name, string $tableName, array|string $col $command->createTable($tableName, ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute(); - $this->assertEmpty($schema->getTableUniques($tableName, true)); + $this->assertEmpty($schema->getTableUniques($tableName)); $command->addUnique($tableName, $name, $column)->execute(); @@ -293,7 +248,9 @@ public function testAddUnique(string $name, string $tableName, array|string $col $column = [$column]; } - $this->assertSame($column, $schema->getTableUniques($tableName, true)[0]->columnNames); + $unique = $schema->getTableUniques($tableName)[$db->getQuoter()->getRawTableName($name)]; + + $this->assertSame($column, $unique->columnNames); $db->close(); } @@ -329,10 +286,6 @@ public function testBatchInsert( * Ensure double is inserted with `.` decimal separator. * * @link https://github.com/yiisoft/yii2/issues/6526 - * - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable */ public function testBatchInsertDataTypesLocale(): void { @@ -399,11 +352,6 @@ public function testBatchInsertDataTypesLocale(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testBatchInsertWithDuplicates(): void { $db = $this->getConnection(true); @@ -429,11 +377,6 @@ public function testBatchInsertWithDuplicates(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testBatchInsertWithManyData(): void { $db = $this->getConnection(true); @@ -457,11 +400,6 @@ public function testBatchInsertWithManyData(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testBatchInsertWithYield(): void { $db = $this->getConnection(true); @@ -501,7 +439,7 @@ public function testCreateIndex(array $columns, array $indexColumns, string|null $this->assertCount($count + 1, $schema->getTableIndexes($tableName)); - $index = array_filter($schema->getTableIndexes($tableName), static fn ($index) => !$index->isPrimaryKey)[0]; + $index = array_filter($schema->getTableIndexes($tableName), static fn ($index) => !$index->isPrimaryKey)[$indexName]; $this->assertSame($indexColumns, $index->columnNames); @@ -514,11 +452,6 @@ public function testCreateIndex(array $columns, array $indexColumns, string|null $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testCreateTable(): void { $db = $this->getConnection(); @@ -553,11 +486,6 @@ public function testCreateTable(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testCreateView(): void { $db = $this->getConnection(); @@ -594,11 +522,6 @@ public function testCreateView(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testDataReaderRewindException(): void { $db = $this->getConnection(true); @@ -650,11 +573,6 @@ public function testDataReaderIndexByAndResultCallback(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testDelete(): void { $db = $this->getConnection(true); @@ -676,11 +594,6 @@ public function testDelete(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testDropCheck(): void { $db = $this->getConnection(); @@ -694,27 +607,22 @@ public function testDropCheck(): void $command->createTable('{{test_ck}}', ['int1' => 'integer'])->execute(); - $this->assertEmpty($schema->getTableChecks('{{test_ck}}', true)); + $this->assertEmpty($schema->getTableChecks('{{test_ck}}')); $command->addCheck('{{test_ck}}', '{{test_ck_constraint}}', '[[int1]] > 1')->execute(); $this->assertMatchesRegularExpression( '/^.*int1.*>.*1.*$/', - $schema->getTableChecks('{{test_ck}}', true)[0]->expression, + $schema->getTableChecks('{{test_ck}}')['test_ck_constraint']->expression, ); $command->dropCheck('{{test_ck}}', '{{test_ck_constraint}}')->execute(); - $this->assertEmpty($schema->getTableChecks('{{test_ck}}', true)); + $this->assertEmpty($schema->getTableChecks('{{test_ck}}')); $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testDropColumn(): void { $db = $this->getConnection(); @@ -742,11 +650,6 @@ public function testDropColumn(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testDropCommentFromColumn(): void { $db = $this->getConnection(true); @@ -769,11 +672,6 @@ public function testDropCommentFromColumn(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testDropCommentFromTable(): void { $db = $this->getConnection(true); @@ -795,11 +693,6 @@ public function testDropCommentFromTable(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testDropDefaultValue(): void { $db = $this->getConnection(); @@ -819,21 +712,16 @@ public function testDropDefaultValue(): void $this->assertMatchesRegularExpression( '/^.*41.*$/', - $schema->getTableDefaultValues('{{test_def}}', true)[0]->value, + $schema->getTableDefaultValues('{{test_def}}')['test_def_constraint']->value, ); $command->dropDefaultValue('{{test_def}}', '{{test_def_constraint}}')->execute(); - $this->assertEmpty($schema->getTableDefaultValues('{{test_def}}', true)); + $this->assertEmpty($schema->getTableDefaultValues('{{test_def}}')); $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testDropForeignKey(): void { $db = $this->getConnection(); @@ -860,11 +748,6 @@ public function testDropForeignKey(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testDropIndex(): void { $db = $this->getConnection(); @@ -878,25 +761,21 @@ public function testDropIndex(): void $command->createTable('{{test_idx}}', ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute(); - $this->assertEmpty($schema->getTableIndexes('{[test_idx}}', true)); + $this->assertEmpty($schema->getTableIndexes('{[test_idx}}')); $command->createIndex('{{test_idx}}', '{{test_idx_constraint}}', ['int1', 'int2'], 'UNIQUE')->execute(); + $index = $schema->getTableIndexes('{{test_idx}}')['test_idx_constraint']; - $this->assertSame(['int1', 'int2'], $schema->getTableIndexes('{{test_idx}}', true)[0]->columnNames); - $this->assertTrue($schema->getTableIndexes('{{test_idx}}', true)[0]->isUnique); + $this->assertSame(['int1', 'int2'], $index->columnNames); + $this->assertTrue($index->isUnique); $command->dropIndex('{{test_idx}}', '{{test_idx_constraint}}')->execute(); - $this->assertEmpty($schema->getTableIndexes('{{test_idx}}', true)); + $this->assertEmpty($schema->getTableIndexes('{{test_idx}}')); $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testDropPrimaryKey(): void { $db = $this->getConnection(); @@ -1017,11 +896,6 @@ public function testDropTableCascade(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testDropUnique(): void { $db = $this->getConnection(); @@ -1035,24 +909,19 @@ public function testDropUnique(): void $command->createTable('{{test_uq}}', ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute(); - $this->assertEmpty($schema->getTableUniques('{{test_uq}}', true)); + $this->assertEmpty($schema->getTableUniques('{{test_uq}}')); $command->addUnique('{{test_uq}}', '{{test_uq_constraint}}', ['int1'])->execute(); - $this->assertSame(['int1'], $schema->getTableUniques('{{test_uq}}', true)[0]->columnNames); + $this->assertSame(['int1'], $schema->getTableUniques('{{test_uq}}')['test_uq_constraint']->columnNames); $command->dropUnique('{{test_uq}}', '{{test_uq_constraint}}')->execute(); - $this->assertEmpty($schema->getTableUniques('{{test_uq}}', true)); + $this->assertEmpty($schema->getTableUniques('{{test_uq}}')); $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testDropView(): void { $db = $this->getConnection(true); @@ -1071,11 +940,6 @@ public function testDropView(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testExecute(): void { $db = $this->getConnection(true); @@ -1110,11 +974,6 @@ public function testExecute(): void $command->execute(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testExecuteWithoutSql(): void { $db = $this->getConnection(); @@ -1230,12 +1089,6 @@ public function testInsertExpression(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws InvalidCallException - * @throws Throwable - */ public function testsInsertQueryAsColumnValue(): void { $db = $this->getConnection(true); @@ -1284,11 +1137,6 @@ public function testsInsertQueryAsColumnValue(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testInsertSelect(): void { $db = $this->getConnection(true); @@ -1335,11 +1183,6 @@ public function testInsertSelect(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testInsertSelectAlias(): void { $db = $this->getConnection(true); @@ -1388,12 +1231,8 @@ public function testInsertSelectAlias(): void /** * Test INSERT INTO ... SELECT SQL statement with wrong query object. - * - * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::invalidSelectColumns - * - * @throws Exception - * @throws Throwable */ + #[DataProviderExternal(CommandProvider::class, 'invalidSelectColumns')] public function testInsertSelectFailed(array|ExpressionInterface|string $invalidSelectColumns): void { $db = $this->getConnection(); @@ -1408,11 +1247,6 @@ public function testInsertSelectFailed(array|ExpressionInterface|string $invalid $command->insert('{{customer}}', $query)->execute(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testInsertToBlob(): void { $db = $this->getConnection(true); @@ -1511,11 +1345,6 @@ public function testInsertBatchWithoutTypecasting(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testIntegrityViolation(): void { $db = $this->getConnection(true); @@ -1531,12 +1360,6 @@ public function testIntegrityViolation(): void $command->execute(); } - /** - * @throws Exception - * @throws InvalidCallException - * @throws InvalidConfigException - * @throws Throwable - */ public function testNoTablenameReplacement(): void { $db = $this->getConnection(true); @@ -1581,11 +1404,6 @@ public function testNoTablenameReplacement(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testQuery(): void { $db = $this->getConnection(true); @@ -1617,11 +1435,6 @@ public function testQuery(): void $command->query(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testQueryAll(): void { $db = $this->getConnection(true); @@ -1658,11 +1471,6 @@ public function testQueryAll(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testQueryColumn(): void { $db = $this->getConnection(true); @@ -1698,11 +1506,6 @@ public function testQueryColumn(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testQueryOne(): void { $db = $this->getConnection(true); @@ -1734,11 +1537,6 @@ public function testQueryOne(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testQueryScalar(): void { $db = $this->getConnection(true); @@ -1768,11 +1566,6 @@ public function testQueryScalar(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testRenameColumn(): void { $db = $this->getConnection(true); @@ -1788,11 +1581,6 @@ public function testRenameColumn(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ public function testRenameTable(): void { $db = $this->getConnection(true); @@ -1898,12 +1686,7 @@ public function testTruncateTable(): void $db->close(); } - /** - * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::update - * - * @throws Exception - * @throws Throwable - */ + #[DataProviderExternal(CommandProvider::class, 'update')] public function testUpdate( string $table, array $columns, @@ -1964,14 +1747,7 @@ public function testUpdateWithoutTypecasting(): void ], $command->getParams()); } - /** - * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::upsert - * - * @throws Exception - * @throws InvalidConfigException - * @throws NotSupportedException - * @throws Throwable - */ + #[DataProviderExternal(CommandProvider::class, 'upsert')] public function testUpsert(array $firstData, array $secondData): void { $db = $this->getConnection(true); @@ -2026,11 +1802,6 @@ protected function internalExecute(): void $command->prepare(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws Throwable - */ protected function performAndCompareUpsertResult(PdoConnectionInterface $db, array $data): void { $params = []; diff --git a/tests/Common/CommonSchemaTest.php b/tests/Common/CommonSchemaTest.php index 523c008ef..c84f4a4e6 100644 --- a/tests/Common/CommonSchemaTest.php +++ b/tests/Common/CommonSchemaTest.php @@ -24,13 +24,7 @@ use Yiisoft\Db\Tests\Support\Assert; use function count; -use function gettype; -use function is_array; -use function json_encode; -use function ksort; use function mb_chr; -use function str_replace; -use function strtolower; abstract class CommonSchemaTest extends AbstractSchemaTest { @@ -71,18 +65,13 @@ public function testCompositeFk(): void $this->assertNotNull($table); - $fk = $table->getForeignKeys(); + $foreignKeys = $table->getForeignKeys(); + $foreignKey = $foreignKeys['FK_composite_fk_order_item']; - $expectedKey = match ($db->getDriverName()) { - 'mysql', 'sqlsrv' => $fk['FK_composite_fk_order_item'], - default => $fk['fk_composite_fk_order_item'], - }; - - $this->assertCount(1, $fk); - $this->assertTrue(isset($expectedKey)); - $this->assertSame('order_item', $expectedKey[0]); - $this->assertSame('order_id', $expectedKey['order_id']); - $this->assertSame('item_id', $expectedKey['item_id']); + $this->assertCount(1, $foreignKeys); + $this->assertSame('FK_composite_fk_order_item', $foreignKey->name); + $this->assertSame('order_item', $foreignKey->foreignTableName); + $this->assertSame(['order_id', 'item_id'], $foreignKey->foreignColumnNames); $db->close(); } @@ -157,7 +146,7 @@ public function testFindUniquesIndexes(): void $uniqueIndexes = $schema->findUniqueIndexes($tableSchema); - $this->assertSame(['someCol2Unique' => ['someCol2'], 'somecolUnique' => ['somecol']], $uniqueIndexes); + $this->assertEquals(['somecolUnique' => ['somecol'], 'someCol2Unique' => ['someCol2']], $uniqueIndexes); /** @link https://github.com/yiisoft/yii2/issues/13814 */ $command->createIndex( @@ -172,8 +161,8 @@ public function testFindUniquesIndexes(): void $uniqueIndexes = $schema->findUniqueIndexes($tableSchema); - $this->assertSame( - ['another unique index' => ['someCol3'], 'someCol2Unique' => ['someCol2'], 'somecolUnique' => ['somecol']], + $this->assertEquals( + ['somecolUnique' => ['somecol'], 'someCol2Unique' => ['someCol2'], 'another unique index' => ['someCol3']], $uniqueIndexes, ); @@ -199,7 +188,7 @@ public function testGetDefaultSchema(): void $schema = $db->getSchema(); - $this->assertNull($schema->getDefaultSchema()); + $this->assertSame('', $schema->getDefaultSchema()); $db->close(); } @@ -358,7 +347,6 @@ public function testGetTableNames(array $pdoAttributes): void public function testHasTable(): void { $db = $this->getConnection(true); - $schema = $db->getSchema(); $tempTableName = 'testTemporaryTable'; @@ -639,7 +627,7 @@ public function testTableSchemaConstraints(string $tableName, string $type, mixe $schema = $db->getSchema(); $constraints = $schema->{"getTable$type"}($tableName); - $this->assertMetadataEquals($expected, $constraints); + Assert::constraintsEquals($expected, $constraints); $db->close(); } @@ -657,7 +645,7 @@ public function testTableSchemaConstraintsWithPdoLowercase(string $tableName, st $db->getActivePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); $constraints = $schema->{"getTable$type"}($tableName, true); - $this->assertMetadataEquals($expected, $constraints); + Assert::constraintsEquals($expected, $constraints); $db->close(); } @@ -675,7 +663,7 @@ public function testTableSchemaConstraintsWithPdoUppercase(string $tableName, st $db->getActivePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); $constraints = $schema->{'getTable' . ucfirst($type)}($tableName, true); - $this->assertMetadataEquals($expected, $constraints); + Assert::constraintsEquals($expected, $constraints); $db->close(); } @@ -705,26 +693,6 @@ private function generateQuoterEscapingValues(): array return $result; } - protected function assertMetadataEquals( - array|Check|DefaultValue|ForeignKey|Index|null $expected, - array|Check|DefaultValue|ForeignKey|Index|null $actual, - ): void { - match (gettype($expected)) { - 'object' => $this->assertIsObject($actual), - 'array' => $this->assertIsArray($actual), - 'NULL' => $this->assertNull($actual), - }; - - if (is_array($expected)) { - $this->sortConstrainArray($expected); - $this->sortConstrainArray($actual); - } - - $this->normalizeConstraints($expected, $actual); - - $this->assertEquals($expected, $actual); - } - /** * @param ColumnInterface[] $columns */ @@ -753,47 +721,6 @@ protected function assertTableColumns(array $columns, string $tableName): void $db->close(); } - private function sortConstrainArray(array &$array): void - { - $newArray = []; - - foreach ($array as $value) { - $key = (array) $value; - unset($key['name']); - - $newArray[strtolower(json_encode($key, JSON_THROW_ON_ERROR))] = $value; - } - - ksort($newArray, SORT_STRING); - $array = array_values($newArray); - } - - private function normalizeConstraints( - array|Check|DefaultValue|ForeignKey|Index|null $expected, - array|Check|DefaultValue|ForeignKey|Index|null &$actual, - ): void { - if (is_array($expected)) { - foreach ($expected as $key => $value) { - $this->normalizeConstraintPair($value, $actual[$key]); - } - } elseif ($expected !== null && $actual !== null) { - $this->normalizeConstraintPair($expected, $actual); - } - } - - private function normalizeConstraintPair( - Check|DefaultValue|ForeignKey|Index $expectedConstraint, - Check|DefaultValue|ForeignKey|Index &$actualConstraint, - ): void { - if ($expectedConstraint::class !== $actualConstraint::class) { - return; - } - - if ($expectedConstraint->name === '') { - Assert::setPropertyValue($actualConstraint, 'name', ''); - } - } - public function testWorkWithUniqueConstraint(): void { $tableName = 'test_table_with'; @@ -806,7 +733,7 @@ public function testWorkWithUniqueConstraint(): void $db->createCommand()->addUnique($tableName, $indexName, $columnName)->execute(); $this->assertEquals( - [new Index($indexName, [$columnName], true)], + [$indexName => new Index($indexName, [$columnName], true)], $db->getSchema()->getTableUniques($tableName), ); @@ -841,10 +768,10 @@ public function testWorkWithCheckConstraint(): void $this->assertIsArray($constraints); $this->assertCount(1, $constraints); - $this->assertInstanceOf(Check::class, $constraints[0]); - $this->assertEquals($constraintName, $constraints[0]->name); - $this->assertEquals([$columnName], $constraints[0]->columnNames); - $this->assertStringContainsString($columnName, $constraints[0]->expression); + $this->assertInstanceOf(Check::class, $constraints[$constraintName]); + $this->assertEquals($constraintName, $constraints[$constraintName]->name); + $this->assertEquals([$columnName], $constraints[$constraintName]->columnNames); + $this->assertStringContainsString($columnName, $constraints[$constraintName]->expression); $db->createCommand()->dropCheck($tableName, $constraintName)->execute(); @@ -873,10 +800,10 @@ public function testWorkWithDefaultValueConstraint(): void $this->assertIsArray($constraints); $this->assertCount(1, $constraints); - $this->assertInstanceOf(DefaultValue::class, $constraints[0]); - $this->assertEquals($constraintName, $constraints[0]->name); - $this->assertEquals([$columnName], $constraints[0]->columnNames); - $this->assertStringContainsString('919', $constraints[0]->value); + $this->assertEquals( + new DefaultValue($constraintName, [$columnName], '((919))'), + $constraints[$constraintName] + ); $db->createCommand()->dropDefaultValue($tableName, $constraintName)->execute(); @@ -929,15 +856,14 @@ public function testWorkWithIndex( $columnName = 't_field'; $db = $this->getConnection(); - $qb = $db->getQueryBuilder(); + $command = $db->createCommand(); $this->createTableForIndexAndConstraintTests($db, $tableName, $columnName, $columnType); - $indexSql = $qb->createIndex($tableName, $indexName, $columnName, $indexType, $indexMethod); - $db->createCommand($indexSql)->execute(); + $command->createIndex($tableName, $indexName, $columnName, $indexType, $indexMethod)->execute(); $this->assertEquals( - [new Index($indexName, [$columnName], $isUnique, $isPrimary)], + [$indexName => new Index($indexName, [$columnName], $isUnique, $isPrimary)], $db->getSchema()->getTableIndexes($tableName), ); @@ -946,29 +872,6 @@ public function testWorkWithIndex( $db->close(); } - /** - * @link https://github.com/yiisoft/db/issues/718 - */ - public function testIssue718(): void - { - $db = $this->getConnection(); - - if ($db->getDriverName() === 'oci' || $db->getDriverName() === 'sqlite' || $db->getDriverName() === 'sqlsrv') { - $this->markTestSkipped('Test is not supported by sqlite, oci and sqlsrv drivers.'); - } - - if ($db->getTableSchema('{{%table}}', true) !== null) { - $db->createCommand()->dropTable('{{%table}}')->execute(); - } - - $db->createCommand()->createTable('{{%table}}', ['array' => 'json'])->execute(); - $db->createCommand()->insert('{{%table}}', ['array' => [1, 2]])->execute(); - - $result = str_replace(' ', '', $db->createCommand('SELECT * FROM {{%table}}')->queryScalar()); - - $this->assertSame('[1,2]', trim($result)); - } - #[DataProviderExternal(SchemaProvider::class, 'resultColumns')] public function testGetResultColumn(ColumnInterface|null $expected, array $metadata): void { diff --git a/tests/Db/Schema/SchemaTest.php b/tests/Db/Schema/SchemaTest.php index dea99767d..bb9cf85f1 100644 --- a/tests/Db/Schema/SchemaTest.php +++ b/tests/Db/Schema/SchemaTest.php @@ -10,12 +10,12 @@ use Yiisoft\Db\Constraint\Index; use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\Schema\Column\ColumnBuilder; +use Yiisoft\Db\Schema\TableSchema; use Yiisoft\Db\Schema\TableSchemaInterface; use Yiisoft\Db\Tests\AbstractSchemaTest; use Yiisoft\Db\Tests\Support\Assert; use Yiisoft\Db\Tests\Support\DbHelper; use Yiisoft\Db\Tests\Support\Stub\Schema; -use Yiisoft\Db\Tests\Support\Stub\TableSchema; use Yiisoft\Db\Tests\Support\TestTrait; /** @@ -160,11 +160,11 @@ public function testGetSchemaPrimaryKeys(): void $pksConstraint = new Index('PK__T_constr__A9FAE80AC2B18E65', ['"C_id'], true, true); $schemaMock = $this->getMockBuilder(Schema::class) - ->onlyMethods(['findTableNames', 'loadTablePrimaryKey']) + ->onlyMethods(['findTableNames', 'getTablePrimaryKey']) ->setConstructorArgs([$db, DbHelper::getSchemaCache()]) ->getMock(); $schemaMock->expects($this->once())->method('findTableNames')->willReturn(['T_constraints_1']); - $schemaMock->expects($this->once())->method('loadTablePrimaryKey')->willReturn($pksConstraint); + $schemaMock->expects($this->once())->method('getTablePrimaryKey')->willReturn($pksConstraint); $tablePks = $schemaMock->getSchemaPrimaryKeys(); $this->assertIsArray($tablePks); @@ -177,11 +177,11 @@ public function testGetSchemaUniques(): void $uniquesConstraint = [new Index('CN_unique', ['C_unique'], true)]; $schemaMock = $this->getMockBuilder(Schema::class) - ->onlyMethods(['findTableNames', 'loadTableUniques']) + ->onlyMethods(['findTableNames', 'getTableUniques']) ->setConstructorArgs([$db, DbHelper::getSchemaCache()]) ->getMock(); $schemaMock->expects($this->once())->method('findTableNames')->willReturn(['T_constraints_1']); - $schemaMock->expects($this->once())->method('loadTableUniques')->willReturn($uniquesConstraint); + $schemaMock->expects($this->once())->method('getTableUniques')->willReturn($uniquesConstraint); $tableUniques = $schemaMock->getSchemaUniques(); $this->assertIsArray($tableUniques); @@ -284,18 +284,6 @@ public function testRefreshTableSchemaWithSchemaCaseDisabled(): void $this->assertNotSame($noCacheTable, $refreshedTable); } - public function testResolveTableName(): void - { - $db = $this->getConnection(); - - $schema = $db->getSchema(); - - $this->expectException(NotSupportedException::class); - $this->expectExceptionMessage('Yiisoft\Db\Tests\Support\Stub\Schema does not support resolving table names.'); - - Assert::invokeMethod($schema, 'resolveTableName', ['customer']); - } - public function testSetTableMetadata(): void { $db = $this->getConnection(); @@ -324,17 +312,13 @@ public function testGetResultColumn(): void private function createTableSchemaStub(): TableSchemaInterface { // defined table T_constraints_1 - $tableSchema = new TableSchema(); - $tableSchema->column('C_id', ColumnBuilder::primaryKey()->dbType('int')); - $tableSchema->column('C_not_null', ColumnBuilder::integer()->dbType('int')); - $tableSchema->column('C_check', ColumnBuilder::string()->dbType('varchar(255)')); - $tableSchema->column('C_default', ColumnBuilder::integer()->dbType('int')); - $tableSchema->column('C_unique', ColumnBuilder::integer()->dbType('int')); - $tableSchema->fullName('T_constraints_1'); - $tableSchema->name('T_constraints_1'); - $tableSchema->primaryKey('C_id'); - $tableSchema->schemaName('dbo'); - - return $tableSchema; + return (new TableSchema('T_constraints_1', 'dbo')) + ->columns([ + 'C_id' => ColumnBuilder::primaryKey()->dbType('int'), + 'C_not_null' => ColumnBuilder::integer()->dbType('int'), + 'C_check' => ColumnBuilder::string()->dbType('varchar(255)'), + 'C_default' => ColumnBuilder::integer()->dbType('int'), + 'C_unique' => ColumnBuilder::integer()->dbType('int'), + ]); } } diff --git a/tests/Provider/SchemaProvider.php b/tests/Provider/SchemaProvider.php index e67efc8c7..9f96726d1 100644 --- a/tests/Provider/SchemaProvider.php +++ b/tests/Provider/SchemaProvider.php @@ -40,7 +40,10 @@ public static function constraints(): array '1: unique' => [ 'T_constraints_1', SchemaInterface::UNIQUES, - [new Index('CN_unique', ['C_unique'], true)], + [ + new Index('', ['C_id'], true, true), + new Index('CN_unique', ['C_unique'], true), + ], ], '1: index' => [ 'T_constraints_1', @@ -60,7 +63,10 @@ public static function constraints(): array '2: unique' => [ 'T_constraints_2', SchemaInterface::UNIQUES, - [new Index('CN_constraints_2_multi', ['C_index_2_1', 'C_index_2_2'], true)], + [ + new Index('CN_pk', ['C_id_1', 'C_id_2'], true, true), + new Index('CN_constraints_2_multi', ['C_index_2_1', 'C_index_2_2'], true), + ], ], '2: index' => [ 'T_constraints_2', @@ -107,7 +113,10 @@ public static function constraints(): array '4: unique' => [ 'T_constraints_4', SchemaInterface::UNIQUES, - [new Index('CN_constraints_4', ['C_col_1', 'C_col_2'], true)], + [ + new Index('', ['C_id'], true, true), + new Index('CN_constraints_4', ['C_col_1', 'C_col_2'], true), + ], ], '4: check' => ['T_constraints_4', SchemaInterface::CHECKS, []], '4: default' => ['T_constraints_4', SchemaInterface::DEFAULT_VALUES, false], diff --git a/tests/Support/Assert.php b/tests/Support/Assert.php index b12493339..e0237e642 100644 --- a/tests/Support/Assert.php +++ b/tests/Support/Assert.php @@ -9,11 +9,19 @@ use ReflectionException; use ReflectionObject; use ReflectionProperty; +use Yiisoft\Db\Constraint\Check; +use Yiisoft\Db\Constraint\DefaultValue; +use Yiisoft\Db\Constraint\ForeignKey; +use Yiisoft\Db\Constraint\Index; use function array_key_exists; +use function array_values; use function is_array; use function is_object; +use function json_encode; +use function ksort; use function ltrim; +use function strtolower; /** * @psalm-suppress PropertyNotSetInConstructor @@ -196,4 +204,55 @@ private static function getProperties(object $object): array return $properties; } + + public static function constraintsEquals( + array|Check|DefaultValue|ForeignKey|Index|null $expected, + array|Check|DefaultValue|ForeignKey|Index|null $actual, + ): void { + if ($expected === null) { + self::assertNull($actual); + return; + } + + if (is_array($expected)) { + self::assertIsArray($actual); + self::sortConstrains($expected); + self::sortConstrains($actual); + + foreach ($expected as $key => $constraint) { + self::normalizeConstraints($constraint, $actual[$key]); + } + } else { + self::assertIsObject($actual); + self::normalizeConstraints($expected, $actual); + } + + self::assertEquals($expected, $actual); + } + + private static function sortConstrains(array &$array): void + { + $sort = []; + + foreach ($array as $constraint) { + $key = (array) $constraint; + unset($key['name']); + + $sort[strtolower(json_encode($key))] = $constraint; + } + + ksort($sort, SORT_STRING); + $array = array_values($sort); + } + + private static function normalizeConstraints( + Check|DefaultValue|ForeignKey|Index $expected, + Check|DefaultValue|ForeignKey|Index &$actual, + ): void { + self::assertInstanceOf($expected::class, $actual); + + if ($expected->name === '') { + self::setPropertyValue($actual, 'name', ''); + } + } } diff --git a/tests/Support/Stub/TableSchema.php b/tests/Support/Stub/TableSchema.php deleted file mode 100644 index 7ff95c7ff..000000000 --- a/tests/Support/Stub/TableSchema.php +++ /dev/null @@ -1,11 +0,0 @@ -