From 7a9b1fe886f1ff9047bdc8440a0946bd469a72c9 Mon Sep 17 00:00:00 2001 From: Sergei Tigrov Date: Sun, 2 Jun 2024 09:34:39 +0700 Subject: [PATCH] Split magical and non-magical implementations (#339) --- src/ActiveRecord.php | 279 ++++- src/BaseActiveRecord.php | 263 ----- tests/ActiveQueryFindTest.php | 176 ++-- tests/ActiveQueryTest.php | 581 ++++++----- tests/ActiveRecordFactoryTest.php | 2 +- tests/ActiveRecordTest.php | 178 ++-- tests/BatchQueryResultTest.php | 36 +- tests/Driver/Mssql/MagicActiveRecordTest.php | 68 ++ tests/Driver/Mysql/ActiveRecordTest.php | 54 +- tests/Driver/Mysql/MagicActiveRecordTest.php | 74 ++ tests/Driver/Mysql/Stubs/Type.php | 13 + tests/Driver/Oracle/ActiveQueryFindTest.php | 64 +- tests/Driver/Oracle/ActiveQueryTest.php | 84 +- tests/Driver/Oracle/ActiveRecordTest.php | 14 +- tests/Driver/Oracle/BatchQueryResultTest.php | 18 + tests/Driver/Oracle/MagicActiveRecordTest.php | 169 ++++ tests/Driver/Oracle/Stubs/BitValues.php | 19 + tests/Driver/Oracle/Stubs/Customer.php | 8 +- tests/Driver/Oracle/Stubs/MagicCustomer.php | 24 + tests/Driver/Oracle/Stubs/MagicOrder.php | 23 + tests/Driver/Pgsql/ActiveRecordTest.php | 104 +- tests/Driver/Pgsql/MagicActiveRecordTest.php | 384 +++++++ tests/Driver/Pgsql/Stubs/Type.php | 17 + tests/Driver/Sqlite/ActiveRecordTest.php | 20 +- tests/Driver/Sqlite/MagicActiveRecordTest.php | 73 ++ tests/MagicActiveRecordTest.php | 951 ++++++++++++++++++ tests/Stubs/ActiveRecord.php | 39 + tests/Stubs/ActiveRecord/Alpha.php | 34 +- tests/Stubs/ActiveRecord/Animal.php | 8 +- .../Stubs/ActiveRecord/ArrayAndJsonTypes.php | 11 +- tests/Stubs/ActiveRecord/Beta.php | 35 +- tests/Stubs/ActiveRecord/BitValues.php | 7 +- tests/Stubs/ActiveRecord/BoolAR.php | 7 +- tests/Stubs/ActiveRecord/Category.php | 55 +- tests/Stubs/ActiveRecord/Customer.php | 167 ++- .../ActiveRecord/CustomerClosureField.php | 16 +- .../ActiveRecord/CustomerForArrayable.php | 16 +- .../Stubs/ActiveRecord/CustomerWithAlias.php | 18 +- .../ActiveRecord/CustomerWithConstructor.php | 31 +- tests/Stubs/ActiveRecord/DefaultPk.php | 5 +- tests/Stubs/ActiveRecord/Department.php | 24 +- tests/Stubs/ActiveRecord/Document.php | 15 +- tests/Stubs/ActiveRecord/Dossier.php | 67 +- tests/Stubs/ActiveRecord/Employee.php | 35 +- tests/Stubs/ActiveRecord/Item.php | 39 +- tests/Stubs/ActiveRecord/NoExist.php | 2 +- tests/Stubs/ActiveRecord/NullValues.php | 3 +- tests/Stubs/ActiveRecord/Order.php | 232 ++++- tests/Stubs/ActiveRecord/OrderItem.php | 91 +- .../ActiveRecord/OrderItemWithNullFK.php | 12 +- tests/Stubs/ActiveRecord/OrderWithNullFK.php | 32 +- tests/Stubs/ActiveRecord/Profile.php | 8 +- .../ActiveRecord/ProfileWithConstructor.php | 8 +- tests/Stubs/ActiveRecord/TestTrigger.php | 8 +- tests/Stubs/ActiveRecord/TestTriggerAlert.php | 8 +- tests/Stubs/ActiveRecord/Type.php | 37 +- tests/Stubs/ActiveRecord/UserAR.php | 14 +- tests/Stubs/MagicActiveRecord.php | 47 + tests/Stubs/MagicActiveRecord/Alpha.php | 27 + tests/Stubs/MagicActiveRecord/Animal.php | 49 + .../MagicActiveRecord/ArrayAndJsonTypes.php | 11 + tests/Stubs/MagicActiveRecord/Beta.php | 26 + tests/Stubs/MagicActiveRecord/BitValues.php | 17 + tests/Stubs/MagicActiveRecord/BoolAR.php | 15 + tests/Stubs/MagicActiveRecord/Cat.php | 36 + tests/Stubs/MagicActiveRecord/Category.php | 42 + tests/Stubs/MagicActiveRecord/Customer.php | 121 +++ .../CustomerClosureField.php | 33 + .../CustomerForArrayable.php | 57 ++ .../Stubs/MagicActiveRecord/CustomerQuery.php | 19 + .../MagicActiveRecord/CustomerWithAlias.php | 32 + .../CustomerWithConstructor.php | 38 + .../CustomerWithProperties.php | 6 +- tests/Stubs/MagicActiveRecord/DefaultPk.php | 15 + tests/Stubs/MagicActiveRecord/Department.php | 33 + tests/Stubs/MagicActiveRecord/Document.php | 22 + tests/Stubs/MagicActiveRecord/Dog.php | 18 + tests/Stubs/MagicActiveRecord/Dossier.php | 36 + tests/Stubs/MagicActiveRecord/Employee.php | 53 + tests/Stubs/MagicActiveRecord/Item.php | 28 + tests/Stubs/MagicActiveRecord/NoExist.php | 15 + tests/Stubs/MagicActiveRecord/NullValues.php | 24 + tests/Stubs/MagicActiveRecord/Order.php | 216 ++++ tests/Stubs/MagicActiveRecord/OrderItem.php | 63 ++ .../MagicActiveRecord/OrderItemWithNullFK.php | 23 + .../MagicActiveRecord/OrderWithNullFK.php | 23 + tests/Stubs/MagicActiveRecord/Profile.php | 23 + .../ProfileWithConstructor.php | 28 + tests/Stubs/MagicActiveRecord/TestTrigger.php | 21 + .../MagicActiveRecord/TestTriggerAlert.php | 21 + tests/Stubs/MagicActiveRecord/Type.php | 32 + .../UnqueryableQueryMock.php | 21 + tests/Stubs/MagicActiveRecord/UserAR.php | 19 + tests/data/mssql.sql | 7 +- tests/data/mysql.sql | 23 +- tests/data/pgsql.sql | 24 +- tests/data/sqlite.sql | 7 +- 97 files changed, 5067 insertions(+), 1084 deletions(-) delete mode 100644 src/BaseActiveRecord.php create mode 100644 tests/Driver/Mssql/MagicActiveRecordTest.php create mode 100644 tests/Driver/Mysql/MagicActiveRecordTest.php create mode 100644 tests/Driver/Mysql/Stubs/Type.php create mode 100644 tests/Driver/Oracle/MagicActiveRecordTest.php create mode 100644 tests/Driver/Oracle/Stubs/BitValues.php create mode 100644 tests/Driver/Oracle/Stubs/MagicCustomer.php create mode 100644 tests/Driver/Oracle/Stubs/MagicOrder.php create mode 100644 tests/Driver/Pgsql/MagicActiveRecordTest.php create mode 100644 tests/Driver/Pgsql/Stubs/Type.php create mode 100644 tests/Driver/Sqlite/MagicActiveRecordTest.php create mode 100644 tests/MagicActiveRecordTest.php create mode 100644 tests/Stubs/ActiveRecord.php create mode 100644 tests/Stubs/MagicActiveRecord.php create mode 100644 tests/Stubs/MagicActiveRecord/Alpha.php create mode 100644 tests/Stubs/MagicActiveRecord/Animal.php create mode 100644 tests/Stubs/MagicActiveRecord/ArrayAndJsonTypes.php create mode 100644 tests/Stubs/MagicActiveRecord/Beta.php create mode 100644 tests/Stubs/MagicActiveRecord/BitValues.php create mode 100644 tests/Stubs/MagicActiveRecord/BoolAR.php create mode 100644 tests/Stubs/MagicActiveRecord/Cat.php create mode 100644 tests/Stubs/MagicActiveRecord/Category.php create mode 100644 tests/Stubs/MagicActiveRecord/Customer.php create mode 100644 tests/Stubs/MagicActiveRecord/CustomerClosureField.php create mode 100644 tests/Stubs/MagicActiveRecord/CustomerForArrayable.php create mode 100644 tests/Stubs/MagicActiveRecord/CustomerQuery.php create mode 100644 tests/Stubs/MagicActiveRecord/CustomerWithAlias.php create mode 100644 tests/Stubs/MagicActiveRecord/CustomerWithConstructor.php rename tests/Stubs/{ActiveRecord => MagicActiveRecord}/CustomerWithProperties.php (89%) create mode 100644 tests/Stubs/MagicActiveRecord/DefaultPk.php create mode 100644 tests/Stubs/MagicActiveRecord/Department.php create mode 100644 tests/Stubs/MagicActiveRecord/Document.php create mode 100644 tests/Stubs/MagicActiveRecord/Dog.php create mode 100644 tests/Stubs/MagicActiveRecord/Dossier.php create mode 100644 tests/Stubs/MagicActiveRecord/Employee.php create mode 100644 tests/Stubs/MagicActiveRecord/Item.php create mode 100644 tests/Stubs/MagicActiveRecord/NoExist.php create mode 100644 tests/Stubs/MagicActiveRecord/NullValues.php create mode 100644 tests/Stubs/MagicActiveRecord/Order.php create mode 100644 tests/Stubs/MagicActiveRecord/OrderItem.php create mode 100644 tests/Stubs/MagicActiveRecord/OrderItemWithNullFK.php create mode 100644 tests/Stubs/MagicActiveRecord/OrderWithNullFK.php create mode 100644 tests/Stubs/MagicActiveRecord/Profile.php create mode 100644 tests/Stubs/MagicActiveRecord/ProfileWithConstructor.php create mode 100644 tests/Stubs/MagicActiveRecord/TestTrigger.php create mode 100644 tests/Stubs/MagicActiveRecord/TestTriggerAlert.php create mode 100644 tests/Stubs/MagicActiveRecord/Type.php create mode 100644 tests/Stubs/MagicActiveRecord/UnqueryableQueryMock.php create mode 100644 tests/Stubs/MagicActiveRecord/UserAR.php diff --git a/src/ActiveRecord.php b/src/ActiveRecord.php index f2a7f830e..8d3aea4f8 100644 --- a/src/ActiveRecord.php +++ b/src/ActiveRecord.php @@ -4,41 +4,260 @@ namespace Yiisoft\ActiveRecord; -use ArrayAccess; -use IteratorAggregate; -use Yiisoft\ActiveRecord\Trait\ArrayableTrait; -use Yiisoft\ActiveRecord\Trait\ArrayAccessTrait; -use Yiisoft\ActiveRecord\Trait\ArrayIteratorTrait; -use Yiisoft\ActiveRecord\Trait\MagicPropertiesTrait; -use Yiisoft\ActiveRecord\Trait\MagicRelationsTrait; -use Yiisoft\ActiveRecord\Trait\TransactionalTrait; -use Yiisoft\Arrays\ArrayableInterface; +use Throwable; +use Yiisoft\Db\Exception\Exception; +use Yiisoft\Db\Exception\InvalidArgumentException; +use Yiisoft\Db\Exception\InvalidConfigException; +use Yiisoft\Db\Schema\TableSchemaInterface; + +use function array_diff; +use function array_keys; +use function array_map; +use function array_values; +use function get_object_vars; +use function in_array; +use function is_array; +use function is_string; +use function key; +use function preg_replace; /** - * Active Record class which implements {@see ActiveRecordInterface} and provides additional features like: + * Active Record class which implements {@see ActiveRecordInterface} interface with the minimum set of methods. + * + * Active Record implements the [Active Record design pattern](https://en.wikipedia.org/wiki/Active_record). + * + * The premise behind Active Record is that an individual {@see ActiveRecord} object is associated with a specific row + * in a database table. The object's attributes are mapped to the columns of the corresponding table. + * + * Referencing an Active Record attribute is equivalent to accessing the corresponding table column for that record. + * + * As an example, say that the `Customer` ActiveRecord class is associated with the `customer` table. + * + * This would mean that the class's `name` attribute is automatically mapped to the `name` column in `customer` table. + * Thanks to Active Record, assuming the variable `$customer` is an object of type `Customer`, to get the value of the + * `name` column for the table row, you can use the expression `$customer->name`. + * + * In this example, Active Record is providing an object-oriented interface for accessing data stored in the database. + * But Active Record provides much more functionality than this. + * + * To declare an ActiveRecord class you need to extend {@see ActiveRecord} and implement the `getTableName` method: + * + * ```php + * class Customer extends ActiveRecord + * { + * public static function getTableName(): string + * { + * return 'customer'; + * } + * } + * ``` + * + * The `getTableName` method only has to return the name of the database table associated with the class. + * + * Class instances are obtained in one of two ways: + * + * Using the `new` operator to create a new, empty object. + * Using a method to fetch an existing record (or records) from the database. * - * - {@see ArrayableInterface}: to convert the object into an array; - * - {@see ArrayAccess}: to access attributes as array elements; - * - {@see IteratorAggregate}: to iterate over attributes; - * - {@see TransactionalInterface}: to handle transactions; - * - {@see MagicPropertiesTrait}: to access attributes as properties; - * - {@see MagicRelationsTrait}: to access relation queries. + * Below is an example showing some typical usage of ActiveRecord: * - * @see BaseActiveRecord for more information. + * ```php + * $user = new User($db); + * $user->name = 'Qiang'; + * $user->save(); // a new row is inserted into user table * - * @template-implements ArrayAccess - * @template-implements IteratorAggregate + * // the following will retrieve the user 'CeBe' from the database + * $userQuery = new ActiveQuery(User::class, $db); + * $user = $userQuery->where(['name' => 'CeBe'])->one(); + * + * // this will get related records from orders table when relation is defined + * $orders = $user->orders; + * ``` + * + * For more details and usage information on ActiveRecord, + * {@see the [guide article on ActiveRecord](guide:db-active-record)} */ -class ActiveRecord extends BaseActiveRecord implements - ArrayableInterface, - ArrayAccess, - IteratorAggregate, - TransactionalInterface +class ActiveRecord extends AbstractActiveRecord { - use ArrayableTrait; - use ArrayAccessTrait; - use ArrayIteratorTrait; - use MagicPropertiesTrait; - use MagicRelationsTrait; - use TransactionalTrait; + public function attributes(): array + { + return $this->getTableSchema()->getColumnNames(); + } + + public function filterCondition(array $condition, array $aliases = []): array + { + $result = []; + + $columnNames = $this->filterValidColumnNames($aliases); + + foreach ($condition as $key => $value) { + if (is_string($key) && !in_array($this->db()->getQuoter()->quoteSql($key), $columnNames, true)) { + throw new InvalidArgumentException( + 'Key "' . $key . '" is not a column name and can not be used as a filter' + ); + } + $result[$key] = is_array($value) ? array_values($value) : $value; + } + + return $result; + } + + public function filterValidAliases(ActiveQuery $query): array + { + $tables = $query->getTablesUsedInFrom(); + + $aliases = array_diff(array_keys($tables), $tables); + + return array_map(static fn ($alias) => preg_replace('/{{([\w]+)}}/', '$1', $alias), array_values($aliases)); + } + + /** + * Returns the schema information of the DB table associated with this AR class. + * + * @throws Exception + * @throws InvalidConfigException If the table for the AR class does not exist. + * + * @return TableSchemaInterface The schema information of the DB table associated with this AR class. + */ + public function getTableSchema(): TableSchemaInterface + { + $tableSchema = $this->db()->getSchema()->getTableSchema($this->getTableName()); + + if ($tableSchema === null) { + throw new InvalidConfigException('The table does not exist: ' . $this->getTableName()); + } + + return $tableSchema; + } + + /** + * Loads default values from database table schema. + * + * You may call this method to load default values after creating a new instance: + * + * ```php + * // class Customer extends ActiveRecord + * $customer = new Customer($db); + * $customer->loadDefaultValues(); + * ``` + * + * @param bool $skipIfSet Whether existing value should be preserved. This will only set defaults for attributes + * that are `null`. + * + * @throws Exception + * @throws InvalidConfigException + * + * @return self The active record instance itself. + */ + public function loadDefaultValues(bool $skipIfSet = true): self + { + foreach ($this->getTableSchema()->getColumns() as $name => $column) { + if ($column->getDefaultValue() !== null && (!$skipIfSet || $this->getAttribute($name) === null)) { + $this->setAttribute($name, $column->getDefaultValue()); + } + } + + return $this; + } + + public function populateRecord(array|object $row): void + { + $row = ArArrayHelper::toArray($row); + $columns = $this->getTableSchema()->getColumns(); + $rowColumns = array_intersect_key($row, $columns); + + foreach ($rowColumns as $name => &$value) { + $value = $columns[$name]->phpTypecast($value); + } + + parent::populateRecord($rowColumns + $row); + } + + public function primaryKey(): array + { + return $this->getTableSchema()->getPrimaryKey(); + } + + /** + * @throws Exception + * @throws InvalidArgumentException + * @throws InvalidConfigException + * @throws Throwable + */ + public function refresh(): bool + { + $query = $this->instantiateQuery(static::class); + + $tableName = key($query->getTablesUsedInFrom()); + $pk = []; + + /** disambiguate column names in case ActiveQuery adds a JOIN */ + foreach ($this->getPrimaryKey(true) as $key => $value) { + $pk[$tableName . '.' . $key] = $value; + } + + $query->where($pk); + + return $this->refreshInternal($query->onePopulate()); + } + + /** + * Valid column names are table column names or column names prefixed with table name or table alias. + * + * @throws Exception + * @throws InvalidConfigException + */ + protected function filterValidColumnNames(array $aliases): array + { + $columnNames = []; + $tableName = $this->getTableName(); + $quotedTableName = $this->db()->getQuoter()->quoteTableName($tableName); + + foreach ($this->getTableSchema()->getColumnNames() as $columnName) { + $columnNames[] = $columnName; + $columnNames[] = $this->db()->getQuoter()->quoteColumnName($columnName); + $columnNames[] = "$tableName.$columnName"; + $columnNames[] = $this->db()->getQuoter()->quoteSql("$quotedTableName.[[$columnName]]"); + + foreach ($aliases as $tableAlias) { + $columnNames[] = "$tableAlias.$columnName"; + $quotedTableAlias = $this->db()->getQuoter()->quoteTableName($tableAlias); + $columnNames[] = $this->db()->getQuoter()->quoteSql("$quotedTableAlias.[[$columnName]]"); + } + } + + return $columnNames; + } + + protected function getAttributesInternal(): array + { + return get_object_vars($this); + } + + protected function insertInternal(array $attributes = null): bool + { + $values = $this->getDirtyAttributes($attributes); + $primaryKeys = $this->db()->createCommand()->insertWithReturningPks($this->getTableName(), $values); + + if ($primaryKeys === false) { + return false; + } + + $columns = $this->getTableSchema()->getColumns(); + + foreach ($primaryKeys as $name => $value) { + $id = $columns[$name]->phpTypecast($value); + $this->setAttribute($name, $id); + $values[$name] = $id; + } + + $this->setOldAttributes($values); + + return true; + } + + protected function populateAttribute(string $name, mixed $value): void + { + $this->$name = $value; + } } diff --git a/src/BaseActiveRecord.php b/src/BaseActiveRecord.php deleted file mode 100644 index df4f6305e..000000000 --- a/src/BaseActiveRecord.php +++ /dev/null @@ -1,263 +0,0 @@ -name`. - * - * In this example, Active Record is providing an object-oriented interface for accessing data stored in the database. - * But Active Record provides much more functionality than this. - * - * To declare an ActiveRecord class you need to extend {@see ActiveRecord} and implement the `getTableName` method: - * - * ```php - * class Customer extends ActiveRecord - * { - * public static function getTableName(): string - * { - * return 'customer'; - * } - * } - * ``` - * - * The `getTableName` method only has to return the name of the database table associated with the class. - * - * Class instances are obtained in one of two ways: - * - * Using the `new` operator to create a new, empty object. - * Using a method to fetch an existing record (or records) from the database. - * - * Below is an example showing some typical usage of ActiveRecord: - * - * ```php - * $user = new User($db); - * $user->name = 'Qiang'; - * $user->save(); // a new row is inserted into user table - * - * // the following will retrieve the user 'CeBe' from the database - * $userQuery = new ActiveQuery(User::class, $db); - * $user = $userQuery->where(['name' => 'CeBe'])->one(); - * - * // this will get related records from orders table when relation is defined - * $orders = $user->orders; - * ``` - * - * For more details and usage information on ActiveRecord, - * {@see the [guide article on ActiveRecord](guide:db-active-record)} - */ -class BaseActiveRecord extends AbstractActiveRecord -{ - public function attributes(): array - { - return $this->getTableSchema()->getColumnNames(); - } - - public function filterCondition(array $condition, array $aliases = []): array - { - $result = []; - - $columnNames = $this->filterValidColumnNames($aliases); - - foreach ($condition as $key => $value) { - if (is_string($key) && !in_array($this->db()->getQuoter()->quoteSql($key), $columnNames, true)) { - throw new InvalidArgumentException( - 'Key "' . $key . '" is not a column name and can not be used as a filter' - ); - } - $result[$key] = is_array($value) ? array_values($value) : $value; - } - - return $result; - } - - public function filterValidAliases(ActiveQuery $query): array - { - $tables = $query->getTablesUsedInFrom(); - - $aliases = array_diff(array_keys($tables), $tables); - - return array_map(static fn ($alias) => preg_replace('/{{([\w]+)}}/', '$1', $alias), array_values($aliases)); - } - - /** - * Returns the schema information of the DB table associated with this AR class. - * - * @throws Exception - * @throws InvalidConfigException If the table for the AR class does not exist. - * - * @return TableSchemaInterface The schema information of the DB table associated with this AR class. - */ - public function getTableSchema(): TableSchemaInterface - { - $tableSchema = $this->db()->getSchema()->getTableSchema($this->getTableName()); - - if ($tableSchema === null) { - throw new InvalidConfigException('The table does not exist: ' . $this->getTableName()); - } - - return $tableSchema; - } - - /** - * Loads default values from database table schema. - * - * You may call this method to load default values after creating a new instance: - * - * ```php - * // class Customer extends ActiveRecord - * $customer = new Customer($db); - * $customer->loadDefaultValues(); - * ``` - * - * @param bool $skipIfSet Whether existing value should be preserved. This will only set defaults for attributes - * that are `null`. - * - * @throws Exception - * @throws InvalidConfigException - * - * @return self The active record instance itself. - */ - public function loadDefaultValues(bool $skipIfSet = true): self - { - foreach ($this->getTableSchema()->getColumns() as $name => $column) { - if ($column->getDefaultValue() !== null && (!$skipIfSet || $this->getAttribute($name) === null)) { - $this->setAttribute($name, $column->getDefaultValue()); - } - } - - return $this; - } - - public function populateRecord(array|object $row): void - { - $row = ArArrayHelper::toArray($row); - $columns = $this->getTableSchema()->getColumns(); - $rowColumns = array_intersect_key($row, $columns); - - foreach ($rowColumns as $name => &$value) { - $value = $columns[$name]->phpTypecast($value); - } - - parent::populateRecord($rowColumns + $row); - } - - public function primaryKey(): array - { - return $this->getTableSchema()->getPrimaryKey(); - } - - /** - * @throws Exception - * @throws InvalidArgumentException - * @throws InvalidConfigException - * @throws Throwable - */ - public function refresh(): bool - { - $query = $this->instantiateQuery(static::class); - - $tableName = key($query->getTablesUsedInFrom()); - $pk = []; - - /** disambiguate column names in case ActiveQuery adds a JOIN */ - foreach ($this->getPrimaryKey(true) as $key => $value) { - $pk[$tableName . '.' . $key] = $value; - } - - $query->where($pk); - - return $this->refreshInternal($query->onePopulate()); - } - - /** - * Valid column names are table column names or column names prefixed with table name or table alias. - * - * @throws Exception - * @throws InvalidConfigException - */ - protected function filterValidColumnNames(array $aliases): array - { - $columnNames = []; - $tableName = $this->getTableName(); - $quotedTableName = $this->db()->getQuoter()->quoteTableName($tableName); - - foreach ($this->getTableSchema()->getColumnNames() as $columnName) { - $columnNames[] = $columnName; - $columnNames[] = $this->db()->getQuoter()->quoteColumnName($columnName); - $columnNames[] = "$tableName.$columnName"; - $columnNames[] = $this->db()->getQuoter()->quoteSql("$quotedTableName.[[$columnName]]"); - - foreach ($aliases as $tableAlias) { - $columnNames[] = "$tableAlias.$columnName"; - $quotedTableAlias = $this->db()->getQuoter()->quoteTableName($tableAlias); - $columnNames[] = $this->db()->getQuoter()->quoteSql("$quotedTableAlias.[[$columnName]]"); - } - } - - return $columnNames; - } - - protected function getAttributesInternal(): array - { - return get_object_vars($this); - } - - protected function insertInternal(array $attributes = null): bool - { - $values = $this->getDirtyAttributes($attributes); - $primaryKeys = $this->db()->createCommand()->insertWithReturningPks($this->getTableName(), $values); - - if ($primaryKeys === false) { - return false; - } - - $columns = $this->getTableSchema()->getColumns(); - - foreach ($primaryKeys as $name => $value) { - $id = $columns[$name]->phpTypecast($value); - $this->setAttribute($name, $id); - $values[$name] = $id; - } - - $this->setOldAttributes($values); - - return true; - } - - protected function populateAttribute(string $name, mixed $value): void - { - $this->$name = $value; - } -} diff --git a/tests/ActiveQueryFindTest.php b/tests/ActiveQueryFindTest.php index 179817c66..519cb4993 100644 --- a/tests/ActiveQueryFindTest.php +++ b/tests/ActiveQueryFindTest.php @@ -101,7 +101,7 @@ public function testFindLazyViaTable(): void $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->findOne(2); - $this->assertCount(0, $orders->books); + $this->assertCount(0, $orders->getBooks()); $this->assertEquals(2, $orders->getAttribute('id')); $orders = $orderQuery->where(['id' => 1])->asArray()->one(); @@ -117,19 +117,19 @@ public function testFindEagerViaTable(): void $this->assertCount(3, $orders); $order = $orders[0]; - $this->assertCount(2, $order->books); + $this->assertCount(2, $order->getBooks()); $this->assertEquals(1, $order->getAttribute('id')); - $this->assertEquals(1, $order->books[0]->getAttribute('id')); - $this->assertEquals(2, $order->books[1]->getAttribute('id')); + $this->assertEquals(1, $order->getBooks()[0]->getAttribute('id')); + $this->assertEquals(2, $order->getBooks()[1]->getAttribute('id')); $order = $orders[1]; - $this->assertCount(0, $order->books); + $this->assertCount(0, $order->getBooks()); $this->assertEquals(2, $order->getAttribute('id')); $order = $orders[2]; - $this->assertCount(1, $order->books); + $this->assertCount(1, $order->getBooks()); $this->assertEquals(3, $order->getAttribute('id')); - $this->assertEquals(2, $order->books[0]->getAttribute('id')); + $this->assertEquals(2, $order->getBooks()[0]->getAttribute('id')); /** https://github.com/yiisoft/yii2/issues/1402 */ $orderQuery = new ActiveQuery(Order::class, $this->db); @@ -158,10 +158,10 @@ public function testFindCompositeRelationWithJoin(): void /** @var $orderItems OrderItem */ $orderItems = $orderItemQuery->findOne([1, 1]); - $orderItemNoJoin = $orderItems->orderItemCompositeNoJoin; + $orderItemNoJoin = $orderItems->getOrderItemCompositeNoJoin(); $this->assertInstanceOf(OrderItem::class, $orderItemNoJoin); - $orderItemWithJoin = $orderItems->orderItemCompositeWithJoin; + $orderItemWithJoin = $orderItems->getOrderItemCompositeWithJoin(); $this->assertInstanceOf(OrderItem::class, $orderItemWithJoin); } @@ -172,13 +172,13 @@ public function testFindSimpleRelationWithJoin(): void $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->findOne(1); - $customerNoJoin = $orders->customer; + $customerNoJoin = $orders->getCustomer(); $this->assertInstanceOf(Customer::class, $customerNoJoin); - $customerWithJoin = $orders->customerJoinedWithProfile; + $customerWithJoin = $orders->getCustomerJoinedWithProfile(); $this->assertInstanceOf(Customer::class, $customerWithJoin); - $customerWithJoinIndexOrdered = $orders->customerJoinedWithProfileIndexOrdered; + $customerWithJoinIndexOrdered = $orders->getCustomerJoinedWithProfileIndexOrdered(); $this->assertArrayHasKey('user1', $customerWithJoinIndexOrdered); $this->assertInstanceOf(Customer::class, $customerWithJoinIndexOrdered['user1']); $this->assertIsArray($customerWithJoinIndexOrdered); @@ -192,12 +192,12 @@ public function testFindOneByColumnName(): void $customerQuery = new CustomerQuery(Customer::class, $this->db); $arClass = $customer->findOne(['id' => 1]); - $this->assertEquals(1, $arClass->id); + $this->assertEquals(1, $arClass->getId()); $customerQuery->joinWithProfile = true; $arClass = $customer->findOne(['customer.id' => 1]); - $this->assertEquals(1, $arClass->id); + $this->assertEquals(1, $arClass->getId()); $customerQuery->joinWithProfile = false; } @@ -225,7 +225,7 @@ public function testFind(): void $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(2); $this->assertInstanceOf(Customer::class, $customer); - $this->assertEquals('user2', $customer->name); + $this->assertEquals('user2', $customer->getName()); $customer = $customerQuery->findOne(5); $this->assertNull($customer); @@ -241,7 +241,7 @@ public function testFind(): void $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(['id' => 2, 'name' => 'user2']); $this->assertInstanceOf(Customer::class, $customer); - $this->assertEquals('user2', $customer->name); + $this->assertEquals('user2', $customer->getName()); $customer = $customerQuery->findOne(['id' => 2, 'name' => 'user1']); $this->assertNull($customer); @@ -256,7 +256,7 @@ public function testFind(): void $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->where(['name' => 'user2'])->onePopulate(); $this->assertInstanceOf(Customer::class, $customer); - $this->assertEquals(2, $customer->id); + $this->assertEquals(2, $customer->getId()); /** scope */ $customerQuery = new CustomerQuery(Customer::class, $this->db); @@ -277,6 +277,7 @@ public function testFindAsArray(): void 'name' => 'user2', 'address' => 'address2', 'status' => 1, + 'bool_status' => true, 'profile_id' => null, ], $customer); @@ -317,7 +318,10 @@ public function testFindIndexBy(): void /** indexBy callable */ $customer = new ActiveQuery(Customer::class, $this->db); - $customers = $customer->indexBy(fn (Customer $customer) => $customer->id . '-' . $customer->name)->orderBy('id')->all(); + $customers = $customer + ->indexBy(fn (Customer $customer) => $customer->getId() . '-' . $customer->getName()) + ->orderBy('id') + ->all(); $this->assertCount(3, $customers); $this->assertInstanceOf(Customer::class, $customers['1-user1']); @@ -394,7 +398,7 @@ public function testFindLimit(): void /** one */ $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->orderBy('id')->onePopulate(); - $this->assertEquals('user1', $customer->name); + $this->assertEquals('user1', $customer->getName()); /** all */ $customerQuery = new ActiveQuery(Customer::class, $this->db); @@ -405,20 +409,20 @@ public function testFindLimit(): void $customerQuery = new ActiveQuery(Customer::class, $this->db); $customers = $customerQuery->orderBy('id')->limit(1)->allPopulate(); $this->assertCount(1, $customers); - $this->assertEquals('user1', $customers[0]->name); + $this->assertEquals('user1', $customers[0]->getName()); $customers = $customerQuery->orderBy('id')->limit(1)->offset(1)->allPopulate(); $this->assertCount(1, $customers); - $this->assertEquals('user2', $customers[0]->name); + $this->assertEquals('user2', $customers[0]->getName()); $customers = $customerQuery->orderBy('id')->limit(1)->offset(2)->allPopulate(); $this->assertCount(1, $customers); - $this->assertEquals('user3', $customers[0]->name); + $this->assertEquals('user3', $customers[0]->getName()); $customers = $customerQuery->orderBy('id')->limit(2)->offset(1)->allPopulate(); $this->assertCount(2, $customers); - $this->assertEquals('user2', $customers[0]->name); - $this->assertEquals('user3', $customers[1]->name); + $this->assertEquals('user2', $customers[0]->getName()); + $this->assertEquals('user3', $customers[1]->getName()); $customers = $customerQuery->limit(2)->offset(3)->allPopulate(); $this->assertCount(0, $customers); @@ -426,13 +430,13 @@ public function testFindLimit(): void /** offset */ $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->orderBy('id')->offset(0)->onePopulate(); - $this->assertEquals('user1', $customer->name); + $this->assertEquals('user1', $customer->getName()); $customer = $customerQuery->orderBy('id')->offset(1)->onePopulate(); - $this->assertEquals('user2', $customer->name); + $this->assertEquals('user2', $customer->getName()); $customer = $customerQuery->orderBy('id')->offset(2)->onePopulate(); - $this->assertEquals('user3', $customer->name); + $this->assertEquals('user3', $customer->getName()); $customer = $customerQuery->offset(3)->onePopulate(); $this->assertNull($customer); @@ -482,12 +486,12 @@ public function testFindNullValues(): void $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(2); - $customer->name = null; + $customer->setName(null); $customer->save(); $result = $customerQuery->where(['name' => null])->all(); $this->assertCount(1, $result); - $this->assertEquals(2, reset($result)->primaryKey); + $this->assertEquals(2, reset($result)->getPrimaryKey()); } public function testFindEager(): void @@ -502,17 +506,17 @@ public function testFindEager(): void $this->assertTrue($customers[1]->isRelationPopulated('orders')); $this->assertTrue($customers[2]->isRelationPopulated('orders')); $this->assertTrue($customers[3]->isRelationPopulated('orders')); - $this->assertCount(1, $customers[1]->orders); - $this->assertCount(2, $customers[2]->orders); - $this->assertCount(0, $customers[3]->orders); + $this->assertCount(1, $customers[1]->getOrders()); + $this->assertCount(2, $customers[2]->getOrders()); + $this->assertCount(0, $customers[3]->getOrders()); - unset($customers[1]->orders); + $customers[1]->resetRelation('orders'); $this->assertFalse($customers[1]->isRelationPopulated('orders')); $customer = $customerQuery->where(['id' => 1])->with('orders')->onePopulate(); $this->assertTrue($customer->isRelationPopulated('orders')); - $this->assertCount(1, $customer->orders); - $this->assertCount(1, $customer->relatedRecords); + $this->assertCount(1, $customer->getOrders()); + $this->assertCount(1, $customer->getRelatedRecords()); /** multiple with() calls */ $orderQuery = new ActiveQuery(Order::class, $this->db); @@ -536,11 +540,11 @@ public function testFindEagerViaRelation(): void $this->assertCount(3, $orders); $order = $orders[0]; - $this->assertEquals(1, $order->id); + $this->assertEquals(1, $order->getId()); $this->assertTrue($order->isRelationPopulated('items')); - $this->assertCount(2, $order->items); - $this->assertEquals(1, $order->items[0]->id); - $this->assertEquals(2, $order->items[1]->id); + $this->assertCount(2, $order->getItems()); + $this->assertEquals(1, $order->getItems()[0]->getId()); + $this->assertEquals(2, $order->getItems()[1]->getId()); } public function testFindNestedRelation(): void @@ -555,23 +559,23 @@ public function testFindNestedRelation(): void $this->assertTrue($customers[1]->isRelationPopulated('orders')); $this->assertTrue($customers[2]->isRelationPopulated('orders')); $this->assertTrue($customers[3]->isRelationPopulated('orders')); - $this->assertCount(1, $customers[1]->orders); - $this->assertCount(2, $customers[2]->orders); - $this->assertCount(0, $customers[3]->orders); - $this->assertTrue($customers[1]->orders[0]->isRelationPopulated('items')); - $this->assertTrue($customers[2]->orders[0]->isRelationPopulated('items')); - $this->assertTrue($customers[2]->orders[1]->isRelationPopulated('items')); - $this->assertCount(2, $customers[1]->orders[0]->items); - $this->assertCount(3, $customers[2]->orders[0]->items); - $this->assertCount(1, $customers[2]->orders[1]->items); + $this->assertCount(1, $customers[1]->getOrders()); + $this->assertCount(2, $customers[2]->getOrders()); + $this->assertCount(0, $customers[3]->getOrders()); + $this->assertTrue($customers[1]->getOrders()[0]->isRelationPopulated('items')); + $this->assertTrue($customers[2]->getOrders()[0]->isRelationPopulated('items')); + $this->assertTrue($customers[2]->getOrders()[1]->isRelationPopulated('items')); + $this->assertCount(2, $customers[1]->getOrders()[0]->getItems()); + $this->assertCount(3, $customers[2]->getOrders()[0]->getItems()); + $this->assertCount(1, $customers[2]->getOrders()[1]->getItems()); $customers = $customerQuery->where(['id' => 1])->with('ordersWithItems')->onePopulate(); $this->assertTrue($customers->isRelationPopulated('ordersWithItems')); - $this->assertCount(1, $customers->ordersWithItems); + $this->assertCount(1, $customers->getOrdersWithItems()); - $order = $customers->ordersWithItems[0]; + $order = $customers->getOrdersWithItems()[0]; $this->assertTrue($order->isRelationPopulated('orderItems')); - $this->assertCount(2, $order->orderItems); + $this->assertCount(2, $order->getOrderItems()); } /** @@ -588,25 +592,25 @@ public function testFindEagerViaRelationPreserveOrder(): void $this->assertCount(3, $orders); $order = $orders[0]; - $this->assertEquals(1, $order->id); + $this->assertEquals(1, $order->getId()); $this->assertTrue($order->isRelationPopulated('itemsInOrder1')); - $this->assertCount(2, $order->itemsInOrder1); - $this->assertEquals(1, $order->itemsInOrder1[0]->id); - $this->assertEquals(2, $order->itemsInOrder1[1]->id); + $this->assertCount(2, $order->getItemsInOrder1()); + $this->assertEquals(1, $order->getItemsInOrder1()[0]->getId()); + $this->assertEquals(2, $order->getItemsInOrder1()[1]->getId()); $order = $orders[1]; - $this->assertEquals(2, $order->id); + $this->assertEquals(2, $order->getId()); $this->assertTrue($order->isRelationPopulated('itemsInOrder1')); - $this->assertCount(3, $order->itemsInOrder1); - $this->assertEquals(5, $order->itemsInOrder1[0]->id); - $this->assertEquals(3, $order->itemsInOrder1[1]->id); - $this->assertEquals(4, $order->itemsInOrder1[2]->id); + $this->assertCount(3, $order->getItemsInOrder1()); + $this->assertEquals(5, $order->getItemsInOrder1()[0]->getId()); + $this->assertEquals(3, $order->getItemsInOrder1()[1]->getId()); + $this->assertEquals(4, $order->getItemsInOrder1()[2]->getId()); $order = $orders[2]; - $this->assertEquals(3, $order->id); + $this->assertEquals(3, $order->getId()); $this->assertTrue($order->isRelationPopulated('itemsInOrder1')); - $this->assertCount(1, $order->itemsInOrder1); - $this->assertEquals(2, $order->itemsInOrder1[0]->id); + $this->assertCount(1, $order->getItemsInOrder1()); + $this->assertEquals(2, $order->getItemsInOrder1()[0]->getId()); } public function testFindEagerViaRelationPreserveOrderB(): void @@ -619,25 +623,25 @@ public function testFindEagerViaRelationPreserveOrderB(): void $this->assertCount(3, $orders); $order = $orders[0]; - $this->assertEquals(1, $order->id); + $this->assertEquals(1, $order->getId()); $this->assertTrue($order->isRelationPopulated('itemsInOrder2')); - $this->assertCount(2, $order->itemsInOrder2); - $this->assertEquals(1, $order->itemsInOrder2[0]->id); - $this->assertEquals(2, $order->itemsInOrder2[1]->id); + $this->assertCount(2, $order->getItemsInOrder2()); + $this->assertEquals(1, $order->getItemsInOrder2()[0]->getId()); + $this->assertEquals(2, $order->getItemsInOrder2()[1]->getId()); $order = $orders[1]; - $this->assertEquals(2, $order->id); + $this->assertEquals(2, $order->getId()); $this->assertTrue($order->isRelationPopulated('itemsInOrder2')); - $this->assertCount(3, $order->itemsInOrder2); - $this->assertEquals(5, $order->itemsInOrder2[0]->id); - $this->assertEquals(3, $order->itemsInOrder2[1]->id); - $this->assertEquals(4, $order->itemsInOrder2[2]->id); + $this->assertCount(3, $order->getItemsInOrder2()); + $this->assertEquals(5, $order->getItemsInOrder2()[0]->getId()); + $this->assertEquals(3, $order->getItemsInOrder2()[1]->getId()); + $this->assertEquals(4, $order->getItemsInOrder2()[2]->getId()); $order = $orders[2]; - $this->assertEquals(3, $order->id); + $this->assertEquals(3, $order->getId()); $this->assertTrue($order->isRelationPopulated('itemsInOrder2')); - $this->assertCount(1, $order->itemsInOrder2); - $this->assertEquals(2, $order->itemsInOrder2[0]->id); + $this->assertCount(1, $order->getItemsInOrder2()); + $this->assertEquals(2, $order->getItemsInOrder2()[0]->getId()); } public function testFindEmptyInCondition(): void @@ -666,7 +670,7 @@ public function testFindEagerIndexBy(): void $order = $orderQuery->with('itemsIndexed')->where(['id' => 1])->onePopulate(); $this->assertTrue($order->isRelationPopulated('itemsIndexed')); - $items = $order->itemsIndexed; + $items = $order->getItemsIndexed(); $this->assertCount(2, $items); $this->assertTrue(isset($items[1])); $this->assertTrue(isset($items[2])); @@ -674,7 +678,7 @@ public function testFindEagerIndexBy(): void $order = $orderQuery->with('itemsIndexed')->where(['id' => 2])->onePopulate(); $this->assertTrue($order->isRelationPopulated('itemsIndexed')); - $items = $order->itemsIndexed; + $items = $order->getItemsIndexed(); $this->assertCount(3, $items); $this->assertTrue(isset($items[3])); $this->assertTrue(isset($items[4])); @@ -689,12 +693,12 @@ public function testFindLazy(): void $customer = $customerQuery->findOne(2); $this->assertFalse($customer->isRelationPopulated('orders')); - $orders = $customer->orders; + $orders = $customer->getOrders(); $this->assertTrue($customer->isRelationPopulated('orders')); $this->assertCount(2, $orders); - $this->assertCount(1, $customer->relatedRecords); + $this->assertCount(1, $customer->getRelatedRecords()); - unset($customer['orders']); + $customer->resetRelation('orders'); $this->assertFalse($customer->isRelationPopulated('orders')); $customer = $customerQuery->findOne(2); @@ -702,9 +706,9 @@ public function testFindLazy(): void $orders = $customer->getOrdersQuery()->where(['id' => 3])->all(); $this->assertFalse($customer->isRelationPopulated('orders')); - $this->assertCount(0, $customer->relatedRecords); + $this->assertCount(0, $customer->getRelatedRecords()); $this->assertCount(1, $orders); - $this->assertEquals(3, $orders[0]->id); + $this->assertEquals(3, $orders[0]->getId()); } public function testFindLazyVia(): void @@ -714,9 +718,9 @@ public function testFindLazyVia(): void $orderQuery = new ActiveQuery(Order::class, $this->db); $order = $orderQuery->findOne(1); - $this->assertEquals(1, $order->id); - $this->assertCount(2, $order->items); - $this->assertEquals(1, $order->items[0]->id); - $this->assertEquals(2, $order->items[1]->id); + $this->assertEquals(1, $order->getId()); + $this->assertCount(2, $order->getItems()); + $this->assertEquals(1, $order->getItems()[0]->getId()); + $this->assertEquals(2, $order->getItems()[1]->getId()); } } diff --git a/tests/ActiveQueryTest.php b/tests/ActiveQueryTest.php index 73469fd46..6d604d5af 100644 --- a/tests/ActiveQueryTest.php +++ b/tests/ActiveQueryTest.php @@ -326,22 +326,22 @@ public function testDeeplyNestedTableRelationWith(): void $category = $categories[1]; $this->assertNotNull($category); - $orders = $category->orders; + $orders = $category->getOrders(); $this->assertCount(2, $orders); $this->assertInstanceOf(Order::class, $orders[0]); $this->assertInstanceOf(Order::class, $orders[1]); - $ids = [$orders[0]->id, $orders[1]->id]; + $ids = [$orders[0]->getId(), $orders[1]->getId()]; sort($ids); $this->assertEquals([1, 3], $ids); $category = $categories[2]; $this->assertNotNull($category); - $orders = $category->orders; + $orders = $category->getOrders(); $this->assertCount(1, $orders); $this->assertInstanceOf(Order::class, $orders[0]); - $this->assertEquals(2, $orders[0]->id); + $this->assertEquals(2, $orders[0]->getId()); } public function testGetSql(): void @@ -417,7 +417,7 @@ public function testDeeplyNestedTableRelation(): void $customers = $customerQuery->findOne(1); $this->assertNotNull($customerQuery); - $items = $customers->orderItems; + $items = $customers->getOrderItems(); $this->assertCount(2, $items); $this->assertEquals(1, $items[0]->getAttribute('id')); @@ -441,19 +441,19 @@ public function testDeeplyNestedTableRelation2(): void $categories = $categoryQuery->where(['id' => 1])->onePopulate(); $this->assertNotNull($categories); - $orders = $categories->orders; + $orders = $categories->getOrders(); $this->assertCount(2, $orders); $this->assertInstanceOf(Order::class, $orders[0]); $this->assertInstanceOf(Order::class, $orders[1]); - $ids = [$orders[0]->id, $orders[1]->getAttribute('id')]; + $ids = [$orders[0]->getId(), $orders[1]->getAttribute('id')]; sort($ids); $this->assertEquals([1, 3], $ids); $categories = $categoryQuery->where(['id' => 2])->onePopulate(); $this->assertNotNull($categories); - $orders = $categories->orders; + $orders = $categories->getOrders(); $this->assertCount(1, $orders); $this->assertEquals(2, $orders[0]->getAttribute('id')); $this->assertInstanceOf(Order::class, $orders[0]); @@ -467,9 +467,9 @@ public function testJoinWith(): void $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->joinWith('customer')->orderBy('customer.id DESC, order.id')->all(); $this->assertCount(3, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); - $this->assertEquals(1, $orders[2]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); + $this->assertEquals(1, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); $this->assertTrue($orders[1]->isRelationPopulated('customer')); $this->assertTrue($orders[2]->isRelationPopulated('customer')); @@ -484,8 +484,8 @@ public function testJoinWith(): void ] )->orderBy('order.id')->all(); $this->assertCount(2, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); $this->assertTrue($orders[1]->isRelationPopulated('customer')); @@ -499,7 +499,7 @@ public function testJoinWith(): void ] )->where(['order.id' => [1, 2]])->orderBy('order.id')->all(); $this->assertCount(1, $orders); - $this->assertEquals(2, $orders[0]->id); + $this->assertEquals(2, $orders[0]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); /** inner join filtering without eager loading */ @@ -513,8 +513,8 @@ public function testJoinWith(): void false )->orderBy('order.id')->all(); $this->assertCount(2, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); $this->assertFalse($orders[0]->isRelationPopulated('customer')); $this->assertFalse($orders[1]->isRelationPopulated('customer')); @@ -529,17 +529,17 @@ public function testJoinWith(): void false )->where(['order.id' => [1, 2]])->orderBy('order.id')->all(); $this->assertCount(1, $orders); - $this->assertEquals(2, $orders[0]->id); + $this->assertEquals(2, $orders[0]->getId()); $this->assertFalse($orders[0]->isRelationPopulated('customer')); /** join with via-relation */ $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->innerJoinWith('books')->orderBy('order.id')->all(); $this->assertCount(2, $orders); - $this->assertCount(2, $orders[0]->books); - $this->assertCount(1, $orders[1]->books); - $this->assertEquals(1, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); + $this->assertCount(2, $orders[0]->getBooks()); + $this->assertCount(1, $orders[1]->getBooks()); + $this->assertEquals(1, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('books')); $this->assertTrue($orders[1]->isRelationPopulated('books')); @@ -556,11 +556,11 @@ public function testJoinWith(): void ] )->orderBy('order.id')->all(); $this->assertCount(1, $orders); - $this->assertCount(3, $orders[0]->items); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(2, $orders[0]->items[0]->category->id); + $this->assertCount(3, $orders[0]->getItems()); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId()); $this->assertTrue($orders[0]->isRelationPopulated('items')); - $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category')); + $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category')); /** join with table alias */ $orderQuery = new ActiveQuery(Order::class, $this->db); @@ -572,9 +572,9 @@ public function testJoinWith(): void ] )->orderBy('c.id DESC, order.id')->all(); $this->assertCount(3, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); - $this->assertEquals(1, $orders[2]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); + $this->assertEquals(1, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); $this->assertTrue($orders[1]->isRelationPopulated('customer')); $this->assertTrue($orders[2]->isRelationPopulated('customer')); @@ -583,9 +583,9 @@ public function testJoinWith(): void $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->joinWith('customer as c')->orderBy('c.id DESC, order.id')->all(); $this->assertCount(3, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); - $this->assertEquals(1, $orders[2]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); + $this->assertEquals(1, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); $this->assertTrue($orders[1]->isRelationPopulated('customer')); $this->assertTrue($orders[2]->isRelationPopulated('customer')); @@ -603,22 +603,22 @@ public function testJoinWith(): void ] )->orderBy('order.id')->all(); $this->assertCount(1, $orders); - $this->assertCount(3, $orders[0]->items); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(2, $orders[0]->items[0]->category->id); + $this->assertCount(3, $orders[0]->getItems()); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId()); $this->assertTrue($orders[0]->isRelationPopulated('items')); - $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category')); + $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category')); /** join with ON condition */ $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->joinWith('books2')->orderBy('order.id')->all(); $this->assertCount(3, $orders); - $this->assertCount(2, $orders[0]->books2); - $this->assertCount(0, $orders[1]->books2); - $this->assertCount(1, $orders[2]->books2); - $this->assertEquals(1, $orders[0]->id); - $this->assertEquals(2, $orders[1]->id); - $this->assertEquals(3, $orders[2]->id); + $this->assertCount(2, $orders[0]->getBooks2()); + $this->assertCount(0, $orders[1]->getBooks2()); + $this->assertCount(1, $orders[2]->getBooks2()); + $this->assertEquals(1, $orders[0]->getId()); + $this->assertEquals(2, $orders[1]->getId()); + $this->assertEquals(3, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('books2')); $this->assertTrue($orders[1]->isRelationPopulated('books2')); $this->assertTrue($orders[2]->isRelationPopulated('books2')); @@ -626,26 +626,26 @@ public function testJoinWith(): void /** lazy loading with ON condition */ $orderQuery = new ActiveQuery(Order::class, $this->db); $order = $orderQuery->findOne(1); - $this->assertCount(2, $order->books2); + $this->assertCount(2, $order->getBooks2()); $orderQuery = new ActiveQuery(Order::class, $this->db); $order = $orderQuery->findOne(2); - $this->assertCount(0, $order->books2); + $this->assertCount(0, $order->getBooks2()); $order = new ActiveQuery(Order::class, $this->db); $order = $order->findOne(3); - $this->assertCount(1, $order->books2); + $this->assertCount(1, $order->getBooks2()); /** eager loading with ON condition */ $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->with('books2')->all(); $this->assertCount(3, $orders); - $this->assertCount(2, $orders[0]->books2); - $this->assertCount(0, $orders[1]->books2); - $this->assertCount(1, $orders[2]->books2); - $this->assertEquals(1, $orders[0]->id); - $this->assertEquals(2, $orders[1]->id); - $this->assertEquals(3, $orders[2]->id); + $this->assertCount(2, $orders[0]->getBooks2()); + $this->assertCount(0, $orders[1]->getBooks2()); + $this->assertCount(1, $orders[2]->getBooks2()); + $this->assertEquals(1, $orders[0]->getId()); + $this->assertEquals(2, $orders[1]->getId()); + $this->assertEquals(3, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('books2')); $this->assertTrue($orders[1]->isRelationPopulated('books2')); $this->assertTrue($orders[2]->isRelationPopulated('books2')); @@ -669,7 +669,7 @@ public function testJoinWith(): void }, ] )->onePopulate(); - $this->assertEquals(1, $customer->id); + $this->assertEquals(1, $customer->getId()); $orderQuery = new ActiveQuery(Order::class, $this->db); $order = $orderQuery->joinWith( @@ -695,11 +695,11 @@ public function testJoinWith(): void ] )->orderBy('order.id')->all(); $this->assertCount(1, $orders); - $this->assertCount(3, $orders[0]->items); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(2, $orders[0]->items[0]->category->id); + $this->assertCount(3, $orders[0]->getItems()); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId()); $this->assertTrue($orders[0]->isRelationPopulated('items')); - $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category')); + $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category')); } /** @@ -713,17 +713,17 @@ public function testJoinWithAndScope(): void $customer = new CustomerQuery(Customer::class, $this->db); $customers = $customer->active()->innerJoinWith('profile')->orderBy('customer.id')->all(); $this->assertCount(1, $customers); - $this->assertEquals(1, $customers[0]->id); + $this->assertEquals(1, $customers[0]->getId()); $this->assertTrue($customers[0]->isRelationPopulated('profile')); /** hasOne outer join */ $customer = new CustomerQuery(Customer::class, $this->db); $customers = $customer->active()->joinWith('profile')->orderBy('customer.id')->all(); $this->assertCount(2, $customers); - $this->assertEquals(1, $customers[0]->id); - $this->assertEquals(2, $customers[1]->id); - $this->assertInstanceOf(Profile::class, $customers[0]->profile); - $this->assertNull($customers[1]->profile); + $this->assertEquals(1, $customers[0]->getId()); + $this->assertEquals(2, $customers[1]->getId()); + $this->assertInstanceOf(Profile::class, $customers[0]->getProfile()); + $this->assertNull($customers[1]->getProfile()); $this->assertTrue($customers[0]->isRelationPopulated('profile')); $this->assertTrue($customers[1]->isRelationPopulated('profile')); @@ -737,8 +737,8 @@ public function testJoinWithAndScope(): void ] )->orderBy('customer.id DESC, order.id')->all(); $this->assertCount(2, $customers); - $this->assertEquals(2, $customers[0]->id); - $this->assertEquals(1, $customers[1]->id); + $this->assertEquals(2, $customers[0]->getId()); + $this->assertEquals(1, $customers[1]->getId()); $this->assertTrue($customers[0]->isRelationPopulated('orders')); $this->assertTrue($customers[1]->isRelationPopulated('orders')); } @@ -806,9 +806,9 @@ public function testJoinWithAlias(string $aliasMethod): void } $this->assertCount(3, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); - $this->assertEquals(1, $orders[2]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); + $this->assertEquals(1, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); $this->assertTrue($orders[1]->isRelationPopulated('customer')); $this->assertTrue($orders[2]->isRelationPopulated('customer')); @@ -828,8 +828,8 @@ public function testJoinWithAlias(string $aliasMethod): void } $this->assertCount(2, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); $this->assertTrue($orders[1]->isRelationPopulated('customer')); @@ -848,8 +848,8 @@ public function testJoinWithAlias(string $aliasMethod): void } $this->assertCount(2, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); $this->assertFalse($orders[0]->isRelationPopulated('customer')); $this->assertFalse($orders[1]->isRelationPopulated('customer')); @@ -872,10 +872,10 @@ public function testJoinWithAlias(string $aliasMethod): void } $this->assertCount(2, $orders); - $this->assertCount(2, $orders[0]->books); - $this->assertCount(1, $orders[1]->books); - $this->assertEquals(1, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); + $this->assertCount(2, $orders[0]->getBooks()); + $this->assertCount(1, $orders[1]->getBooks()); + $this->assertEquals(1, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('books')); $this->assertTrue($orders[1]->isRelationPopulated('books')); @@ -915,11 +915,11 @@ public function testJoinWithAlias(string $aliasMethod): void } $this->assertCount(1, $orders); - $this->assertCount(3, $orders[0]->items); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(2, $orders[0]->items[0]->category->id); + $this->assertCount(3, $orders[0]->getItems()); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId()); $this->assertTrue($orders[0]->isRelationPopulated('items')); - $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category')); + $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category')); /** join with ON condition */ if ($aliasMethod === 'explicit' || $aliasMethod === 'querysyntax') { @@ -929,12 +929,12 @@ public function testJoinWithAlias(string $aliasMethod): void $orders = $orderQuery->joinWith(["$relationName b"])->orderBy('order.id')->all(); $this->assertCount(3, $orders); - $this->assertCount(2, $orders[0]->$relationName); - $this->assertCount(0, $orders[1]->$relationName); - $this->assertCount(1, $orders[2]->$relationName); - $this->assertEquals(1, $orders[0]->id); - $this->assertEquals(2, $orders[1]->id); - $this->assertEquals(3, $orders[2]->id); + $this->assertCount(2, $orders[0]->relation($relationName)); + $this->assertCount(0, $orders[1]->relation($relationName)); + $this->assertCount(1, $orders[2]->relation($relationName)); + $this->assertEquals(1, $orders[0]->getId()); + $this->assertEquals(2, $orders[1]->getId()); + $this->assertEquals(3, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated($relationName)); $this->assertTrue($orders[1]->isRelationPopulated($relationName)); $this->assertTrue($orders[2]->isRelationPopulated($relationName)); @@ -948,12 +948,12 @@ public function testJoinWithAlias(string $aliasMethod): void $orders = $orderQuery->joinWith([(string)$relationName])->orderBy('order.id')->all(); $this->assertCount(3, $orders); - $this->assertCount(2, $orders[0]->$relationName); - $this->assertCount(0, $orders[1]->$relationName); - $this->assertCount(1, $orders[2]->$relationName); - $this->assertEquals(1, $orders[0]->id); - $this->assertEquals(2, $orders[1]->id); - $this->assertEquals(3, $orders[2]->id); + $this->assertCount(2, $orders[0]->relation($relationName)); + $this->assertCount(0, $orders[1]->relation($relationName)); + $this->assertCount(1, $orders[2]->relation($relationName)); + $this->assertEquals(1, $orders[0]->getId()); + $this->assertEquals(2, $orders[1]->getId()); + $this->assertEquals(3, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated($relationName)); $this->assertTrue($orders[1]->isRelationPopulated($relationName)); $this->assertTrue($orders[2]->isRelationPopulated($relationName)); @@ -990,7 +990,7 @@ public function testJoinWithAlias(string $aliasMethod): void $customer = $customerQuery->where([$query->applyAlias('order', 'id') => 1])->onePopulate(); } - $this->assertEquals(1, $customer->id); + $this->assertEquals(1, $customer->getId()); $this->assertNotNull($customer); /** join with sub-relation called inside Closure */ @@ -1014,11 +1014,11 @@ public function testJoinWithAlias(string $aliasMethod): void )->orderBy('order.id')->all(); $this->assertCount(1, $orders); - $this->assertCount(3, $orders[0]->items); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(2, $orders[0]->items[0]->category->id); + $this->assertCount(3, $orders[0]->getItems()); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId()); $this->assertTrue($orders[0]->isRelationPopulated('items')); - $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category')); + $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category')); } /** @@ -1040,7 +1040,7 @@ public function testJoinWithSameTable(): void $orders, $query->createCommand()->getRawSql() . print_r($orders, true) ); - $this->assertEquals(2, $orders[0]->id); + $this->assertEquals(2, $orders[0]->getId()); $this->assertFalse($orders[0]->isRelationPopulated('bookItems')); $this->assertFalse($orders[0]->isRelationPopulated('movieItems')); @@ -1053,9 +1053,9 @@ public function testJoinWithSameTable(): void $orders, $query->createCommand()->getRawSql() . print_r($orders, true) ); - $this->assertCount(0, $orders[0]->bookItems); - $this->assertCount(3, $orders[0]->movieItems); - $this->assertEquals(2, $orders[0]->id); + $this->assertCount(0, $orders[0]->getBookItems()); + $this->assertCount(3, $orders[0]->getMovieItems()); + $this->assertEquals(2, $orders[0]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('bookItems')); $this->assertTrue($orders[0]->isRelationPopulated('movieItems')); @@ -1086,7 +1086,7 @@ public function testJoinWithSameTable(): void $orders, $query->createCommand()->getRawSql() . print_r($orders, true) ); - $this->assertEquals(2, $orders[0]->id); + $this->assertEquals(2, $orders[0]->getId()); $this->assertFalse($orders[0]->isRelationPopulated('itemsIndexed')); /** with eager loading, only for one relation as it would be overwritten otherwise. */ @@ -1110,8 +1110,8 @@ public function testJoinWithSameTable(): void )->where(['movies.name' => 'Toy Story']); $orders = $query->all(); $this->assertCount(1, $orders, $query->createCommand()->getRawSql() . print_r($orders, true)); - $this->assertCount(3, $orders[0]->itemsIndexed); - $this->assertEquals(2, $orders[0]->id); + $this->assertCount(3, $orders[0]->getItemsIndexed()); + $this->assertEquals(2, $orders[0]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('itemsIndexed')); /** with eager loading, and the other relation */ @@ -1136,8 +1136,8 @@ public function testJoinWithSameTable(): void ->where(['movies.name' => 'Toy Story']); $orders = $query->all(); $this->assertCount(1, $orders, $query->createCommand()->getRawSql() . print_r($orders, true)); - $this->assertCount(0, $orders[0]->itemsIndexed); - $this->assertEquals(2, $orders[0]->id); + $this->assertCount(0, $orders[0]->getItemsIndexed()); + $this->assertEquals(2, $orders[0]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('itemsIndexed')); } @@ -1158,9 +1158,9 @@ public function testJoinWithDuplicateSimple(): void ->all(); $this->assertCount(3, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); - $this->assertEquals(1, $orders[2]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); + $this->assertEquals(1, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); $this->assertTrue($orders[1]->isRelationPopulated('customer')); $this->assertTrue($orders[2]->isRelationPopulated('customer')); @@ -1185,8 +1185,8 @@ public function testJoinWithDuplicateCallbackFiltering(): void ])->orderBy('order.id')->all(); $this->assertCount(2, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); $this->assertTrue($orders[1]->isRelationPopulated('customer')); } @@ -1210,7 +1210,7 @@ public function testJoinWithDuplicateCallbackFilteringConditionsOnPrimary(): voi ])->where(['order.id' => [1, 2]])->orderBy('order.id')->all(); $this->assertCount(1, $orders); - $this->assertEquals(2, $orders[0]->id); + $this->assertEquals(2, $orders[0]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); } @@ -1234,10 +1234,10 @@ public function testJoinWithDuplicateWithSubRelation(): void $this->assertCount(1, $orders); $this->assertTrue($orders[0]->isRelationPopulated('items')); - $this->assertEquals(2, $orders[0]->id); - $this->assertCount(3, $orders[0]->items); - $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category')); - $this->assertEquals(2, $orders[0]->items[0]->category->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertCount(3, $orders[0]->getItems()); + $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category')); + $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId()); } /** @@ -1259,9 +1259,9 @@ public function testJoinWithDuplicateTableAlias1(): void ])->orderBy('c.id DESC, order.id')->all(); $this->assertCount(3, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); - $this->assertEquals(1, $orders[2]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); + $this->assertEquals(1, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); $this->assertTrue($orders[1]->isRelationPopulated('customer')); $this->assertTrue($orders[2]->isRelationPopulated('customer')); @@ -1284,9 +1284,9 @@ public function testJoinWithDuplicateTableAlias2(): void ->all(); $this->assertCount(3, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); - $this->assertEquals(1, $orders[2]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); + $this->assertEquals(1, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); $this->assertTrue($orders[1]->isRelationPopulated('customer')); $this->assertTrue($orders[2]->isRelationPopulated('customer')); @@ -1316,10 +1316,10 @@ public function testJoinWithDuplicateTableAliasSubRelation(): void $this->assertCount(1, $orders); $this->assertTrue($orders[0]->isRelationPopulated('items')); - $this->assertEquals(2, $orders[0]->id); - $this->assertCount(3, $orders[0]->items); - $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category')); - $this->assertEquals(2, $orders[0]->items[0]->category->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertCount(3, $orders[0]->getItems()); + $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category')); + $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId()); } /** @@ -1349,10 +1349,10 @@ public function testJoinWithDuplicateSubRelationCalledInsideClosure(): void $this->assertCount(1, $orders); $this->assertTrue($orders[0]->isRelationPopulated('items')); - $this->assertEquals(2, $orders[0]->id); - $this->assertCount(3, $orders[0]->items); - $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category')); - $this->assertEquals(2, $orders[0]->items[0]->category->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertCount(3, $orders[0]->getItems()); + $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category')); + $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId()); } public function testAlias(): void @@ -1381,28 +1381,28 @@ public function testInverseOf(): void /** eager loading: find one and all */ $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->with('orders2')->where(['id' => 1])->onePopulate(); - $this->assertSame($customer->orders2[0]->customer2, $customer); + $this->assertSame($customer->getOrders2()[0]->getCustomer2(), $customer); //$customerQuery = new ActiveQuery(Customer::class, $this->db); $customers = $customerQuery->with('orders2')->where(['id' => [1, 3]])->all(); - $this->assertEmpty($customers[1]->orders2); - $this->assertSame($customers[0]->orders2[0]->customer2, $customers[0]); + $this->assertEmpty($customers[1]->getOrders2()); + $this->assertSame($customers[0]->getOrders2()[0]->getCustomer2(), $customers[0]); /** lazy loading */ $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(2); - $orders = $customer->orders2; + $orders = $customer->getOrders2(); $this->assertCount(2, $orders); - $this->assertSame($customer->orders2[0]->customer2, $customer); - $this->assertSame($customer->orders2[1]->customer2, $customer); + $this->assertSame($customer->getOrders2()[0]->getCustomer2(), $customer); + $this->assertSame($customer->getOrders2()[1]->getCustomer2(), $customer); /** ad-hoc lazy loading */ $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(2); $orders = $customer->getOrders2Query()->all(); $this->assertCount(2, $orders); - $this->assertSame($orders[0]->customer2, $customer); - $this->assertSame($orders[1]->customer2, $customer); + $this->assertSame($orders[0]->getCustomer2(), $customer); + $this->assertSame($orders[1]->getCustomer2(), $customer); $this->assertTrue( $orders[0]->isRelationPopulated('customer2'), 'inverse relation did not populate the relation' @@ -1424,11 +1424,11 @@ public function testInverseOf(): void $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->with('customer2')->where(['id' => 1])->all(); - $this->assertSame($orders[0]->customer2->orders2, [$orders[0]]); + $this->assertSame($orders[0]->getCustomer2()->getOrders2(), [$orders[0]]); $orderQuery = new ActiveQuery(Order::class, $this->db); $order = $orderQuery->with('customer2')->where(['id' => 1])->onePopulate(); - $this->assertSame($order->customer2->orders2, [$order]); + $this->assertSame($order->getCustomer2()->getOrders2(), [$order]); $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->with('customer2')->where(['id' => 1])->asArray()->all(); @@ -1440,13 +1440,13 @@ public function testInverseOf(): void $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->with('customer2')->where(['id' => [1, 3]])->all(); - $this->assertSame($orders[0]->customer2->orders2, [$orders[0]]); - $this->assertSame($orders[1]->customer2->orders2, [$orders[1]]); + $this->assertSame($orders[0]->getCustomer2()->getOrders2(), [$orders[0]]); + $this->assertSame($orders[1]->getCustomer2()->getOrders2(), [$orders[1]]); $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->with('customer2')->where(['id' => [2, 3]])->orderBy('id')->all(); - $this->assertSame($orders[0]->customer2->orders2, $orders); - $this->assertSame($orders[1]->customer2->orders2, $orders); + $this->assertSame($orders[0]->getCustomer2()->getOrders2(), $orders); + $this->assertSame($orders[1]->getCustomer2()->getOrders2(), $orders); $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->with('customer2')->where(['id' => [2, 3]])->orderBy('id')->asArray()->all(); @@ -1463,7 +1463,7 @@ public function testUnlinkAllViaTable(): void /** via table with delete. */ $orderQuery = new ActiveQuery(Order::class, $this->db); $order = $orderQuery->findOne(1); - $this->assertCount(2, $order->booksViaTable); + $this->assertCount(2, $order->getBooksViaTable()); $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db); $orderItemCount = $orderItemQuery->count(); @@ -1474,17 +1474,17 @@ public function testUnlinkAllViaTable(): void $order->unlinkAll('booksViaTable', true); $this->assertEquals(5, $itemQuery->count()); $this->assertEquals($orderItemCount - 2, $orderItemQuery->count()); - $this->assertCount(0, $order->booksViaTable); + $this->assertCount(0, $order->getBooksViaTable()); /** via table without delete */ - $this->assertCount(2, $order->booksWithNullFKViaTable); + $this->assertCount(2, $order->getBooksWithNullFKViaTable()); $orderItemsWithNullFKQuery = new ActiveQuery(OrderItemWithNullFK::class, $this->db); $orderItemCount = $orderItemsWithNullFKQuery->count(); $this->assertEquals(5, $itemQuery->count()); $order->unlinkAll('booksWithNullFKViaTable', false); - $this->assertCount(0, $order->booksWithNullFKViaTable); + $this->assertCount(0, $order->getBooksWithNullFKViaTable()); $this->assertEquals(2, $orderItemsWithNullFKQuery->where( ['AND', ['item_id' => [1, 2]], ['order_id' => null]] )->count()); @@ -1510,9 +1510,9 @@ public function testIssues(): void $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->with('orderItems')->orderBy('id')->all(); $this->assertCount(3, $orders); - $this->assertCount(2, $orders[0]->orderItems); - $this->assertCount(3, $orders[1]->orderItems); - $this->assertCount(1, $orders[2]->orderItems); + $this->assertCount(2, $orders[0]->getOrderItems()); + $this->assertCount(3, $orders[1]->getOrderItems()); + $this->assertCount(1, $orders[2]->getOrderItems()); $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->with( @@ -1523,19 +1523,19 @@ public function testIssues(): void ] )->orderBy('id')->all(); $this->assertCount(3, $orders); - $this->assertCount(2, $orders[0]->orderItems); - $this->assertCount(3, $orders[1]->orderItems); - $this->assertCount(1, $orders[2]->orderItems); + $this->assertCount(2, $orders[0]->getOrderItems()); + $this->assertCount(3, $orders[1]->getOrderItems()); + $this->assertCount(1, $orders[2]->getOrderItems()); /** {@see https://github.com/yiisoft/yii2/issues/8149} */ $arClass = new Customer($this->db); - $arClass->name = 'test'; - $arClass->email = 'test'; + $arClass->setName('test'); + $arClass->setEmail('test'); $arClass->save(); $arClass->updateCounters(['status' => 1]); - $this->assertEquals(1, $arClass->status); + $this->assertEquals(1, $arClass->getStatus()); } public function testPopulateWithoutPk(): void @@ -1602,9 +1602,9 @@ public function testPopulateWithoutPk(): void $this->assertContainsOnlyInstancesOf(Customer::class, $aggregation); foreach ($aggregation as $item) { - if ($item->status === 1) { + if ($item->getStatus() === 1) { $this->assertEquals(183, $item->sumTotal); - } elseif ($item->status === 2) { + } elseif ($item->getStatus() === 2) { $this->assertEquals(0, $item->sumTotal); } } @@ -1648,12 +1648,12 @@ public function testPopulateWithoutPk(): void $this->assertContainsOnlyInstancesOf(OrderItem::class, $aggregation); foreach ($aggregation as $item) { - if ($item->order_id === 1) { - $this->assertEquals(70, $item->subtotal); - } elseif ($item->order_id === 2) { - $this->assertEquals(33, $item->subtotal); - } elseif ($item->order_id === 3) { - $this->assertEquals(40, $item->subtotal); + if ($item->getOrderId() === 1) { + $this->assertEquals(70, $item->getSubtotal()); + } elseif ($item->getOrderId() === 2) { + $this->assertEquals(33, $item->getSubtotal()); + } elseif ($item->getOrderId() === 3) { + $this->assertEquals(40, $item->getSubtotal()); } } } @@ -1667,13 +1667,13 @@ public function testLinkWhenRelationIsIndexed2(): void $orderItem = new OrderItem($this->db); - $orderItem->order_id = $order->id; - $orderItem->item_id = 3; - $orderItem->quantity = 1; - $orderItem->subtotal = 10.0; + $orderItem->setOrderId($order->getId()); + $orderItem->setItemId(3); + $orderItem->setQuantity(1); + $orderItem->setSubtotal(10.0); $order->link('orderItems2', $orderItem); - $this->assertTrue(isset($order->orderItems2['3'])); + $this->assertTrue(isset($order->getOrderItems2()['3'])); } public function testEmulateExecution(): void @@ -1714,7 +1714,7 @@ public function testUnlinkAllOnCondition(): void * Ensure that limitedItems relation returns only one item (category_id = 2 and id in (1,2,3)) */ $category = $categoryQuery->onePopulate(); - $this->assertCount(1, $category->limitedItems); + $this->assertCount(1, $category->getLimitedItems()); /** Unlink all items in the limitedItems relation */ $category->unlinkAll('limitedItems', true); @@ -1724,7 +1724,7 @@ public function testUnlinkAllOnCondition(): void $this->assertEquals(2, $itemsCount); /** Call $categoryQuery again to ensure no items were found */ - $this->assertCount(0, $categoryQuery->onePopulate()->limitedItems); + $this->assertCount(0, $categoryQuery->onePopulate()->getLimitedItems()); } /** @@ -1746,13 +1746,13 @@ public function testUnlinkAllOnConditionViaTable(): void * Ensure that limitedItems relation returns only one item (category_id = 2 and id in (4, 5)). */ $category = $orderQuery->onePopulate(); - $this->assertCount(2, $category->limitedItems); + $this->assertCount(2, $category->getLimitedItems()); /** Unlink all items in the limitedItems relation */ $category->unlinkAll('limitedItems', true); /** Call $orderQuery again to ensure that links are removed */ - $this->assertCount(0, $orderQuery->onePopulate()->limitedItems); + $this->assertCount(0, $orderQuery->onePopulate()->getLimitedItems()); /** Make sure that only links were removed, the items were not removed */ $this->assertEquals(3, $itemQuery->where(['category_id' => 2])->count()); @@ -1768,15 +1768,15 @@ public function testIndexByAfterLoadingRelations(): void $orderQuery = new ActiveQuery(Order::class, $this->db); $orderQuery->with('customer')->indexBy(function (Order $order) { $this->assertTrue($order->isRelationPopulated('customer')); - $this->assertNotEmpty($order->customer->id); + $this->assertNotEmpty($order->getCustomer()?->getId()); - return $order->customer->id; + return $order->getCustomer()?->getId(); })->all(); $orders = $orderQuery->with('customer')->indexBy('customer.id')->all(); foreach ($orders as $customer_id => $order) { - $this->assertEquals($customer_id, $order->customer_id); + $this->assertEquals($customer_id, $order->getCustomerId()); } } @@ -1881,20 +1881,20 @@ public function testOutdatedRelationsAreResetForExistingRecords(): void $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db); $orderItems = $orderItemQuery->findOne(1); - $this->assertEquals(1, $orderItems->order->id); - $this->assertEquals(1, $orderItems->item->id); + $this->assertEquals(1, $orderItems->getOrder()->getId()); + $this->assertEquals(1, $orderItems->getItem()->getId()); /** test `__set()`. */ - $orderItems->order_id = 2; - $orderItems->item_id = 1; - $this->assertEquals(2, $orderItems->order->id); - $this->assertEquals(1, $orderItems->item->id); + $orderItems->setOrderId(2); + $orderItems->setItemId(1); + $this->assertEquals(2, $orderItems->getOrder()->getId()); + $this->assertEquals(1, $orderItems->getItem()->getId()); /** Test `setAttribute()`. */ $orderItems->setAttribute('order_id', 3); $orderItems->setAttribute('item_id', 1); - $this->assertEquals(3, $orderItems->order->id); - $this->assertEquals(1, $orderItems->item->id); + $this->assertEquals(3, $orderItems->getOrder()->getId()); + $this->assertEquals(1, $orderItems->getItem()->getId()); } public function testOutdatedCompositeKeyRelationsAreReset(): void @@ -1904,26 +1904,27 @@ public function testOutdatedCompositeKeyRelationsAreReset(): void $dossierQuery = new ActiveQuery(Dossier::class, $this->db); $dossiers = $dossierQuery->findOne(['department_id' => 1, 'employee_id' => 1]); - $this->assertEquals('John Doe', $dossiers->employee->fullName); + $this->assertEquals('John Doe', $dossiers->getEmployee()->getFullName()); - $dossiers->department_id = 2; - $this->assertEquals('Ann Smith', $dossiers->employee->fullName); + $dossiers->setDepartmentId(2); + $this->assertEquals('Ann Smith', $dossiers->getEmployee()->getFullName()); - $dossiers->employee_id = 2; - $this->assertEquals('Will Smith', $dossiers->employee->fullName); + $dossiers->setEmployeeId(2); + $this->assertEquals('Will Smith', $dossiers->getEmployee()->getFullName()); - unset($dossiers->employee_id); - $this->assertNull($dossiers->employee); + // Dossier::$employee_id property cannot be null + // unset($dossiers->employee_id); + // $this->assertNull($dossiers->getEmployee()); $dossier = new Dossier($this->db); - $this->assertNull($dossier->employee); + $this->assertNull($dossier->getEmployee()); - $dossier->employee_id = 1; - $dossier->department_id = 2; - $this->assertEquals('Ann Smith', $dossier->employee->fullName); + $dossier->setEmployeeId(1); + $dossier->setDepartmentId(2); + $this->assertEquals('Ann Smith', $dossier->getEmployee()->getFullName()); - $dossier->employee_id = 2; - $this->assertEquals('Will Smith', $dossier->employee->fullName); + $dossier->setEmployeeId(2); + $this->assertEquals('Will Smith', $dossier->getEmployee()->getFullName()); } public function testOutdatedViaTableRelationsAreReset(): void @@ -1933,23 +1934,23 @@ public function testOutdatedViaTableRelationsAreReset(): void $orderQuery = new ActiveQuery(Order::class, $this->db); $orders = $orderQuery->findOne(1); - $orderItemIds = ArArrayHelper::getColumn($orders->items, 'id'); + $orderItemIds = ArArrayHelper::getColumn($orders->getItems(), 'id'); sort($orderItemIds); $this->assertSame([1, 2], $orderItemIds); - $orders->id = 2; + $orders->setId(2); sort($orderItemIds); - $orderItemIds = ArArrayHelper::getColumn($orders->items, 'id'); + $orderItemIds = ArArrayHelper::getColumn($orders->getItems(), 'id'); $this->assertSame([3, 4, 5], $orderItemIds); - unset($orders->id); - $this->assertSame([], $orders->items); + $orders->setId(null); + $this->assertSame([], $orders->getItems()); $order = new Order($this->db); - $this->assertSame([], $order->items); + $this->assertSame([], $order->getItems()); - $order->id = 3; - $orderItemIds = ArArrayHelper::getColumn($order->items, 'id'); + $order->setId(3); + $orderItemIds = ArArrayHelper::getColumn($order->getItems(), 'id'); $this->assertSame([2], $orderItemIds); } @@ -1963,27 +1964,27 @@ public function testInverseOfDynamic(): void /** request the inverseOf relation without explicitly (eagerly) loading it */ $orders2 = $customer->getOrders2Query()->all(); - $this->assertSame($customer, $orders2[0]->customer2); + $this->assertSame($customer, $orders2[0]->getCustomer2()); $orders2 = $customer->getOrders2Query()->onePopulate(); - $this->assertSame($customer, $orders2->customer2); + $this->assertSame($customer, $orders2->getCustomer2()); /** * request the inverseOf relation while also explicitly eager loading it (while possible, this is of course * redundant) */ $orders2 = $customer->getOrders2Query()->with('customer2')->all(); - $this->assertSame($customer, $orders2[0]->customer2); + $this->assertSame($customer, $orders2[0]->getCustomer2()); $orders2 = $customer->getOrders2Query()->with('customer2')->onePopulate(); - $this->assertSame($customer, $orders2->customer2); + $this->assertSame($customer, $orders2->getCustomer2()); /** request the inverseOf relation as array */ $orders2 = $customer->getOrders2Query()->asArray()->all(); - $this->assertEquals($customer['id'], $orders2[0]['customer2']['id']); + $this->assertEquals($customer->getId(), $orders2[0]['customer2']->getId()); $orders2 = $customer->getOrders2Query()->asArray()->onePopulate(); - $this->assertEquals($customer['id'], $orders2['customer2']['id']); + $this->assertEquals($customer->getId(), $orders2['customer2']->getId()); } public function testOptimisticLock(): void @@ -2059,10 +2060,10 @@ public function testUpdateAttributes(): void $order = $orderQuery->findOne(1); $newTotal = 978; $this->assertSame(1, $order->updateAttributes(['total' => $newTotal])); - $this->assertEquals($newTotal, $order->total); + $this->assertEquals($newTotal, $order->getTotal()); $order = $orderQuery->findOne(1); - $this->assertEquals($newTotal, $order->total); + $this->assertEquals($newTotal, $order->getTotal()); /** @see https://github.com/yiisoft/yii2/issues/12143 */ $newOrder = new Order($this->db); @@ -2071,7 +2072,7 @@ public function testUpdateAttributes(): void $newTotal = 200; $this->assertSame(0, $newOrder->updateAttributes(['total' => $newTotal])); $this->assertTrue($newOrder->getIsNewRecord()); - $this->assertEquals($newTotal, $newOrder->total); + $this->assertEquals($newTotal, $newOrder->getTotal()); } /** @@ -2102,7 +2103,7 @@ public function testCustomARRelation(): void $orderItem = $orderItem->findOne(1); - $this->assertInstanceOf(Order::class, $orderItem->custom); + $this->assertInstanceOf(Order::class, $orderItem->getCustom()); } public function testGetAttributes(): void @@ -2115,15 +2116,7 @@ public function testGetAttributes(): void $attributesExpected['name'] = 'user1'; $attributesExpected['address'] = 'address1'; $attributesExpected['status'] = 1; - - if ($this->db->getDriverName() === 'pgsql') { - $attributesExpected['bool_status'] = true; - } - - if ($this->db->getDriverName() === 'oci') { - $attributesExpected['bool_status'] = '1'; - } - + $attributesExpected['bool_status'] = true; $attributesExpected['profile_id'] = 1; $customer = new ActiveQuery(Customer::class, $this->db); @@ -2199,15 +2192,7 @@ public function testGetOldAttributes(): void $attributes['name'] = 'user1'; $attributes['address'] = 'address1'; $attributes['status'] = 1; - - if ($this->db->getDriverName() === 'pgsql') { - $attributes['bool_status'] = true; - } - - if ($this->db->getDriverName() === 'oci') { - $attributes['bool_status'] = '1'; - } - + $attributes['bool_status'] = true; $attributes['profile_id'] = 1; $customer = new ActiveQuery(Customer::class, $this->db); @@ -2222,15 +2207,7 @@ public function testGetOldAttributes(): void $attributesNew['name'] = 'samdark'; $attributesNew['address'] = 'address1'; $attributesNew['status'] = 1; - - if ($this->db->getDriverName() === 'pgsql') { - $attributesNew['bool_status'] = true; - } - - if ($this->db->getDriverName() === 'oci') { - $attributesNew['bool_status'] = '1'; - } - + $attributesNew['bool_status'] = true; $attributesNew['profile_id'] = 1; $this->assertEquals($attributesNew, $query->getAttributes()); @@ -2297,6 +2274,8 @@ public function testOldAttributeAfterInsertAndUpdate(): void public function testCheckRelationUnknownPropertyException(): void { + self::markTestSkipped('There is no check for access to an unknown property.'); + $this->checkFixture($this->db, 'customer'); $customer = new ActiveQuery(Customer::class, $this->db); @@ -2310,6 +2289,8 @@ public function testCheckRelationUnknownPropertyException(): void public function testCheckRelationInvalidCallException(): void { + self::markTestSkipped('There is no check for access to an unknown property.'); + $this->checkFixture($this->db, 'customer'); $customer = new ActiveQuery(Customer::class, $this->db); @@ -2341,6 +2322,8 @@ public function testGetRelationInvalidArgumentException(): void public function testGetRelationInvalidArgumentExceptionHasNoRelationNamed(): void { + self::markTestSkipped('The same as test testGetRelationInvalidArgumentException()'); + $this->checkFixture($this->db, 'customer'); $customer = new ActiveQuery(Customer::class, $this->db); @@ -2357,6 +2340,8 @@ public function testGetRelationInvalidArgumentExceptionHasNoRelationNamed(): voi public function testGetRelationInvalidArgumentExceptionCaseSensitive(): void { + self::markTestSkipped('The same as test testGetRelationInvalidArgumentException()'); + $this->checkFixture($this->db, 'customer'); $customer = new ActiveQuery(Customer::class, $this->db); @@ -2393,22 +2378,22 @@ public function testUnlink(): void /** has many without delete */ $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(2); - $this->assertCount(2, $customer->ordersWithNullFK); - $customer->unlink('ordersWithNullFK', $customer->ordersWithNullFK[1], false); - $this->assertCount(1, $customer->ordersWithNullFK); + $this->assertCount(2, $customer->getOrdersWithNullFK()); + $customer->unlink('ordersWithNullFK', $customer->getOrdersWithNullFK()[1], false); + $this->assertCount(1, $customer->getOrdersWithNullFK()); $orderWithNullFKQuery = new ActiveQuery(OrderWithNullFK::class, $this->db); $orderWithNullFK = $orderWithNullFKQuery->findOne(3); - $this->assertEquals(3, $orderWithNullFK->id); - $this->assertNull($orderWithNullFK->customer_id); + $this->assertEquals(3, $orderWithNullFK->getId()); + $this->assertNull($orderWithNullFK->getCustomerId()); /** has many with delete */ $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(2); - $this->assertCount(2, $customer->orders); + $this->assertCount(2, $customer->getOrders()); - $customer->unlink('orders', $customer->orders[1], true); - $this->assertCount(1, $customer->orders); + $customer->unlink('orders', $customer->getOrders()[1], true); + $this->assertCount(1, $customer->getOrders()); $orderQuery = new ActiveQuery(Order::class, $this->db); $this->assertNull($orderQuery->findOne(3)); @@ -2416,18 +2401,18 @@ public function testUnlink(): void /** via model with delete */ $orderQuery = new ActiveQuery(Order::class, $this->db); $order = $orderQuery->findOne(2); - $this->assertCount(3, $order->items); - $this->assertCount(3, $order->orderItems); - $order->unlink('items', $order->items[2], true); - $this->assertCount(2, $order->items); - $this->assertCount(2, $order->orderItems); + $this->assertCount(3, $order->getItems()); + $this->assertCount(3, $order->getOrderItems()); + $order->unlink('items', $order->getItems()[2], true); + $this->assertCount(2, $order->getItems()); + $this->assertCount(2, $order->getOrderItems()); /** via model without delete */ - $this->assertCount(2, $order->itemsWithNullFK); - $order->unlink('itemsWithNullFK', $order->itemsWithNullFK[1], false); + $this->assertCount(2, $order->getItemsWithNullFK()); + $order->unlink('itemsWithNullFK', $order->getItemsWithNullFK()[1], false); - $this->assertCount(1, $order->itemsWithNullFK); - $this->assertCount(2, $order->orderItems); + $this->assertCount(1, $order->getItemsWithNullFK()); + $this->assertCount(2, $order->getOrderItems()); } public function testUnlinkAllAndConditionSetNull(): void @@ -2440,20 +2425,20 @@ public function testUnlinkAllAndConditionSetNull(): void $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(1); - $this->assertCount(3, $customer->ordersWithNullFK); - $this->assertCount(1, $customer->expensiveOrdersWithNullFK); + $this->assertCount(3, $customer->getOrdersWithNullFK()); + $this->assertCount(1, $customer->getExpensiveOrdersWithNullFK()); $orderWithNullFKQuery = new ActiveQuery(OrderWithNullFK::class, $this->db); $this->assertEquals(3, $orderWithNullFKQuery->count()); $customer->unlinkAll('expensiveOrdersWithNullFK'); - $this->assertCount(3, $customer->ordersWithNullFK); - $this->assertCount(0, $customer->expensiveOrdersWithNullFK); + $this->assertCount(3, $customer->getOrdersWithNullFK()); + $this->assertCount(0, $customer->getExpensiveOrdersWithNullFK()); $this->assertEquals(3, $orderWithNullFKQuery->count()); $customer = $customerQuery->findOne(1); - $this->assertCount(2, $customer->ordersWithNullFK); - $this->assertCount(0, $customer->expensiveOrdersWithNullFK); + $this->assertCount(2, $customer->getOrdersWithNullFK()); + $this->assertCount(0, $customer->getExpensiveOrdersWithNullFK()); } public function testUnlinkAllAndConditionDelete(): void @@ -2466,20 +2451,20 @@ public function testUnlinkAllAndConditionDelete(): void $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(1); - $this->assertCount(3, $customer->orders); - $this->assertCount(1, $customer->expensiveOrders); + $this->assertCount(3, $customer->getOrders()); + $this->assertCount(1, $customer->getExpensiveOrders()); $orderQuery = new ActiveQuery(Order::class, $this->db); $this->assertEquals(3, $orderQuery->count()); $customer->unlinkAll('expensiveOrders', true); - $this->assertCount(3, $customer->orders); - $this->assertCount(0, $customer->expensiveOrders); + $this->assertCount(3, $customer->getOrders()); + $this->assertCount(0, $customer->getExpensiveOrders()); $this->assertEquals(2, $orderQuery->count()); $customer = $customerQuery->findOne(1); - $this->assertCount(2, $customer->orders); - $this->assertCount(0, $customer->expensiveOrders); + $this->assertCount(2, $customer->getOrders()); + $this->assertCount(0, $customer->getExpensiveOrders()); } public function testUpdate(): void @@ -2534,28 +2519,28 @@ public function testUpdateCounters(): void $pk = ['order_id' => 2, 'item_id' => 4]; $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db); $orderItem = $orderItemQuery->findOne($pk); - $this->assertEquals(1, $orderItem->quantity); + $this->assertEquals(1, $orderItem->getQuantity()); $ret = $orderItem->updateCounters(['quantity' => -1]); $this->assertTrue($ret); - $this->assertEquals(0, $orderItem->quantity); + $this->assertEquals(0, $orderItem->getQuantity()); $orderItem = $orderItemQuery->findOne($pk); - $this->assertEquals(0, $orderItem->quantity); + $this->assertEquals(0, $orderItem->getQuantity()); /** updateAllCounters */ $pk = ['order_id' => 1, 'item_id' => 2]; $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db); $orderItem = $orderItemQuery->findOne($pk); - $this->assertEquals(2, $orderItem->quantity); + $this->assertEquals(2, $orderItem->getQuantity()); $orderItem = new OrderItem($this->db); $ret = $orderItem->updateAllCounters(['quantity' => 3, 'subtotal' => -10], $pk); $this->assertEquals(1, $ret); $orderItem = $orderItemQuery->findOne($pk); - $this->assertEquals(5, $orderItem->quantity); - $this->assertEquals(30, $orderItem->subtotal); + $this->assertEquals(5, $orderItem->getQuantity()); + $this->assertEquals(30, $orderItem->getSubtotal()); } public function testDelete(): void @@ -2566,7 +2551,7 @@ public function testDelete(): void $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(2); $this->assertInstanceOf(Customer::class, $customer); - $this->assertEquals('user2', $customer->name); + $this->assertEquals('user2', $customer->getName()); $customer->delete(); @@ -2600,14 +2585,14 @@ public function testViaWithCallable(): void $order = $orderQuery->findOne(2); - $expensiveItems = $order->expensiveItemsUsingViaWithCallable; - $cheapItems = $order->cheapItemsUsingViaWithCallable; + $expensiveItems = $order->getExpensiveItemsUsingViaWithCallable(); + $cheapItems = $order->getCheapItemsUsingViaWithCallable(); $this->assertCount(2, $expensiveItems); - $this->assertEquals(4, $expensiveItems[0]->id); - $this->assertEquals(5, $expensiveItems[1]->id); + $this->assertEquals(4, $expensiveItems[0]->getId()); + $this->assertEquals(5, $expensiveItems[1]->getId()); $this->assertCount(1, $cheapItems); - $this->assertEquals(3, $cheapItems[0]->id); + $this->assertEquals(3, $cheapItems[0]->getId()); } public function testLink(): void @@ -2616,36 +2601,36 @@ public function testLink(): void $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(2); - $this->assertCount(2, $customer->orders); + $this->assertCount(2, $customer->getOrders()); /** has many */ $order = new Order($this->db); - $order->total = 100; - $order->created_at = time(); - $this->assertTrue($order->isNewRecord); + $order->setTotal(100); + $order->setCreatedAt(time()); + $this->assertTrue($order->getIsNewRecord()); /** belongs to */ $order = new Order($this->db); - $order->total = 100; - $order->created_at = time(); - $this->assertTrue($order->isNewRecord); + $order->setTotal(100); + $order->setCreatedAt(time()); + $this->assertTrue($order->getIsNewRecord()); $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(1); - $this->assertNull($order->customer); + $this->assertNull($order->getCustomer()); $order->link('customer', $customer); - $this->assertFalse($order->isNewRecord); - $this->assertEquals(1, $order->customer_id); - $this->assertEquals(1, $order->customer->primaryKey); + $this->assertFalse($order->getIsNewRecord()); + $this->assertEquals(1, $order->getCustomerId()); + $this->assertEquals(1, $order->getCustomer()->getPrimaryKey()); /** via model */ $orderQuery = new ActiveQuery(Order::class, $this->db); $order = $orderQuery->findOne(1); - $this->assertCount(2, $order->items); - $this->assertCount(2, $order->orderItems); + $this->assertCount(2, $order->getItems()); + $this->assertCount(2, $order->getOrderItems()); $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db); $orderItem = $orderItemQuery->findOne(['order_id' => 1, 'item_id' => 3]); @@ -2654,14 +2639,14 @@ public function testLink(): void $itemQuery = new ActiveQuery(Item::class, $this->db); $item = $itemQuery->findOne(3); $order->link('items', $item, ['quantity' => 10, 'subtotal' => 100]); - $this->assertCount(3, $order->items); - $this->assertCount(3, $order->orderItems); + $this->assertCount(3, $order->getItems()); + $this->assertCount(3, $order->getOrderItems()); $orderItemQuery = new ActiveQuery(OrderItem::class, $this->db); $orderItem = $orderItemQuery->findOne(['order_id' => 1, 'item_id' => 3]); $this->assertInstanceOf(OrderItem::class, $orderItem); - $this->assertEquals(10, $orderItem->quantity); - $this->assertEquals(100, $orderItem->subtotal); + $this->assertEquals(10, $orderItem->getQuantity()); + $this->assertEquals(100, $orderItem->getSubtotal()); } public function testEqual(): void diff --git a/tests/ActiveRecordFactoryTest.php b/tests/ActiveRecordFactoryTest.php index ac4dd0abc..59ed3f938 100644 --- a/tests/ActiveRecordFactoryTest.php +++ b/tests/ActiveRecordFactoryTest.php @@ -87,6 +87,6 @@ public function testGetArInstanceWithConstructor(): void $query = $this->arFactory->createQueryTo(CustomerWithConstructor::class); $customer = $query->onePopulate(); - $this->assertNotNull($customer->profile); + $this->assertNotNull($customer->getProfile()); } } diff --git a/tests/ActiveRecordTest.php b/tests/ActiveRecordTest.php index 02d6d8ef8..55f797d5b 100644 --- a/tests/ActiveRecordTest.php +++ b/tests/ActiveRecordTest.php @@ -13,7 +13,6 @@ use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\CustomerClosureField; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\CustomerForArrayable; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\CustomerWithAlias; -use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\CustomerWithProperties; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Dog; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Item; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\NoExist; @@ -128,22 +127,21 @@ public function testOutdatedRelationsAreResetForNewRecords(): void $orderItem = new OrderItem($this->db); - $orderItem->order_id = 1; - $orderItem->item_id = 3; - $this->assertEquals(1, $orderItem->order->id); - $this->assertEquals(3, $orderItem->item->id); + $orderItem->setOrderId(1); + $orderItem->setItemId(3); + $this->assertEquals(1, $orderItem->getOrder()->getId()); + $this->assertEquals(3, $orderItem->getItem()->getId()); - /** test `__set()`. */ - $orderItem->order_id = 2; - $orderItem->item_id = 1; - $this->assertEquals(2, $orderItem->order->id); - $this->assertEquals(1, $orderItem->item->id); + $orderItem->setOrderId(2); + $orderItem->setItemId(1); + $this->assertEquals(2, $orderItem->getOrder()->getId()); + $this->assertEquals(1, $orderItem->getItem()->getId()); /** test `setAttribute()`. */ $orderItem->setAttribute('order_id', 2); $orderItem->setAttribute('item_id', 2); - $this->assertEquals(2, $orderItem->order->id); - $this->assertEquals(2, $orderItem->item->id); + $this->assertEquals(2, $orderItem->getOrder()->getId()); + $this->assertEquals(2, $orderItem->getItem()->getId()); } public function testDefaultValues(): void @@ -243,21 +241,21 @@ public function testNoTablenameReplacement(): void $customer = new Customer($this->db); - $customer->name = 'Some {{weird}} name'; - $customer->email = 'test@example.com'; - $customer->address = 'Some {{%weird}} address'; + $customer->setName('Some {{weird}} name'); + $customer->setEmail('test@example.com'); + $customer->setAddress('Some {{%weird}} address'); $customer->insert(); $customer->refresh(); - $this->assertEquals('Some {{weird}} name', $customer->name); - $this->assertEquals('Some {{%weird}} address', $customer->address); + $this->assertEquals('Some {{weird}} name', $customer->getName()); + $this->assertEquals('Some {{%weird}} address', $customer->getAddress()); - $customer->name = 'Some {{updated}} name'; - $customer->address = 'Some {{%updated}} address'; + $customer->setName('Some {{updated}} name'); + $customer->setAddress('Some {{%updated}} address'); $customer->update(); - $this->assertEquals('Some {{updated}} name', $customer->name); - $this->assertEquals('Some {{%updated}} address', $customer->address); + $this->assertEquals('Some {{updated}} name', $customer->getName()); + $this->assertEquals('Some {{%updated}} address', $customer->getAddress()); } public static function legalValuesForFindByCondition(): array @@ -373,23 +371,25 @@ public function testResetNotSavedRelation(): void $order = new Order($this->db); - $order->customer_id = 1; - $order->created_at = 1_325_502_201; - $order->total = 0; + $order->setCustomerId(1); + $order->setCreatedAt(1_325_502_201); + $order->setTotal(0); $orderItem = new OrderItem($this->db); - $order->orderItems; + $order->getOrderItems(); $order->populateRelation('orderItems', [$orderItem]); $order->save(); - $this->assertCount(1, $order->orderItems); + $this->assertCount(1, $order->getOrderItems()); } public function testIssetException(): void { + self::markTestSkipped('There are no magic properties in the Cat class'); + $this->checkFixture($this->db, 'cat'); $cat = new Cat($this->db); @@ -400,6 +400,8 @@ public function testIssetException(): void public function testIssetThrowable(): void { + self::markTestSkipped('There are no magic properties in the Cat class'); + $this->checkFixture($this->db, 'cat'); $cat = new Cat($this->db); @@ -410,6 +412,8 @@ public function testIssetThrowable(): void public function testIssetNonExisting(): void { + self::markTestSkipped('There are no magic properties in the Cat class'); + $this->checkFixture($this->db, 'cat'); $cat = new Cat($this->db); @@ -443,13 +447,15 @@ public function testSetAttributes(): void public function testSetAttributeNoExist(): void { + self::markTestSkipped('There are no magic properties in the Cat class'); + $this->checkFixture($this->db, 'cat'); $cat = new Cat($this->db); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage( - 'Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Cat has no attribute named "noExist"' + 'Yiisoft\ActiveRecord\Tests\Stubs\MagicActiveRecord\Cat has no attribute named "noExist"' ); $cat->setAttribute('noExist', 1); @@ -489,9 +495,9 @@ public function testIsAttributeChangedNotChanged(): void $customer = new Customer($this->db); - $this->assertEmpty($customer->getAttribute('name')); - $this->assertEmpty($customer->getOldAttribute('name')); - $this->assertFalse($customer->isAttributeChanged('name', false)); + $this->assertEmpty($customer->getAttribute('email')); + $this->assertEmpty($customer->getOldAttribute('email')); + $this->assertFalse($customer->isAttributeChanged('email', false)); } public function testTableSchemaException(): void @@ -509,17 +515,17 @@ public function testInsert(): void $customer = new Customer($this->db); - $customer->email = 'user4@example.com'; - $customer->name = 'user4'; - $customer->address = 'address4'; + $customer->setEmail('user4@example.com'); + $customer->setName('user4'); + $customer->setAddress('address4'); - $this->assertNull($customer->id); - $this->assertTrue($customer->isNewRecord); + $this->assertNull($customer->getAttribute('id')); + $this->assertTrue($customer->getIsNewRecord()); $customer->save(); - $this->assertNotNull($customer->id); - $this->assertFalse($customer->isNewRecord); + $this->assertNotNull($customer->getId()); + $this->assertFalse($customer->getIsNewRecord()); } /** @@ -533,31 +539,33 @@ public function testBooleanAttribute(): void $customer = new Customer($this->db); - $customer->name = 'boolean customer'; - $customer->email = 'mail@example.com'; - $customer->status = true; + $customer->setName('boolean customer'); + $customer->setEmail('mail@example.com'); + $customer->setStatus(1); $customer->save(); $customer->refresh(); - $this->assertEquals(1, $customer->status); + $this->assertEquals(1, $customer->getStatus()); - $customer->status = false; + $customer->setStatus(0); $customer->save(); $customer->refresh(); - $this->assertEquals(0, $customer->status); + $this->assertEquals(0, $customer->getStatus()); $customerQuery = new ActiveQuery(Customer::class, $this->db); - $customers = $customerQuery->where(['status' => true])->all(); + $customers = $customerQuery->where(['status' => 1])->all(); $this->assertCount(2, $customers); $customerQuery = new ActiveQuery(Customer::class, $this->db); - $customers = $customerQuery->where(['status' => false])->all(); + $customers = $customerQuery->where(['status' => 0])->all(); $this->assertCount(1, $customers); } public function testAttributeAccess(): void { + self::markTestSkipped('There are no magic properties in the Cat class'); + $this->checkFixture($this->db, 'customer'); $arClass = new Customer($this->db); @@ -639,10 +647,10 @@ public function testRefresh(): void $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(1); - $customer->name = 'to be refreshed'; + $customer->setName('to be refreshed'); $this->assertTrue($customer->refresh()); - $this->assertEquals('user1', $customer->name); + $this->assertEquals('user1', $customer->getName()); } public function testEquals(): void @@ -679,12 +687,12 @@ public function testUnlinkWithViaOnCondition($delete, $count) $orderQuery = new ActiveQuery(Order::class, $this->db); $order = $orderQuery->findOne(2); - $this->assertCount(1, $order->itemsFor8); - $order->unlink('itemsFor8', $order->itemsFor8[0], $delete); + $this->assertCount(1, $order->getItemsFor8()); + $order->unlink('itemsFor8', $order->getItemsFor8()[0], $delete); $order = $orderQuery->findOne(2); - $this->assertCount(0, $order->itemsFor8); - $this->assertCount(2, $order->orderItemsWithNullFK); + $this->assertCount(0, $order->getItemsFor8()); + $this->assertCount(2, $order->getOrderItemsWithNullFK()); $orderItemQuery = new ActiveQuery(OrderItemWithNullFK::class, $this->db); $this->assertCount(1, $orderItemQuery->findAll([ @@ -705,7 +713,7 @@ public function testVirtualRelation() /** @var Order $order */ $order = $orderQuery->findOne(2); - $order->setVirtualCustomerId($order->customer_id); + $order->setVirtualCustomerId($order->getCustomerId()); $this->assertNotNull($order->getVirtualCustomerQuery()); } @@ -722,14 +730,14 @@ public function testJoinWithEager() $eagerCustomers = $customerQuery->joinWith(['items2'])->all(); $eagerItemsCount = 0; foreach ($eagerCustomers as $customer) { - $eagerItemsCount += is_countable($customer->items2) ? count($customer->items2) : 0; + $eagerItemsCount += is_countable($customer->getItems2()) ? count($customer->getItems2()) : 0; } $customerQuery = new ActiveQuery(Customer::class, $this->db); $lazyCustomers = $customerQuery->all(); $lazyItemsCount = 0; foreach ($lazyCustomers as $customer) { - $lazyItemsCount += is_countable($customer->items2) ? count($customer->items2) : 0; + $lazyItemsCount += is_countable($customer->getItems2()) ? count($customer->getItems2()) : 0; } $this->assertEquals($eagerItemsCount, $lazyItemsCount); @@ -749,6 +757,7 @@ public function testToArray(): void 'name' => 'user1', 'address' => 'address1', 'status' => 1, + 'bool_status' => true, 'profile_id' => 1, ], $customer->toArray(), @@ -769,6 +778,7 @@ public function testToArrayWithClosure(): void 'name' => 'user1', 'address' => 'address1', 'status' => 'active', + 'bool_status' => true, 'profile_id' => 1, ], $customer->toArray(), @@ -859,7 +869,7 @@ public function testGetOldPrimaryKey(): void $customerQuery = new ActiveQuery(Customer::class, $this->db); $customer = $customerQuery->findOne(1); - $customer->id = 2; + $customer->setId(2); $this->assertSame(1, $customer->getOldPrimaryKey()); $this->assertSame(['id' => 1], $customer->getOldPrimaryKey(true)); @@ -871,18 +881,40 @@ public function testGetDirtyAttributesOnNewRecord(): void $customer = new Customer($this->db); - $this->assertSame([], $customer->getDirtyAttributes()); + $this->assertSame( + [ + 'name' => null, + 'address' => null, + 'status' => 0, + 'bool_status' => false, + 'profile_id' => null, + ], + $customer->getDirtyAttributes() + ); $customer->setAttribute('name', 'Adam'); $customer->setAttribute('email', 'adam@example.com'); $customer->setAttribute('address', null); + $this->assertEquals([], $customer->getDirtyAttributes([])); + $this->assertEquals( - ['name' => 'Adam', 'email' => 'adam@example.com', 'address' => null], + [ + 'name' => 'Adam', + 'email' => 'adam@example.com', + 'address' => null, + 'status' => 0, + 'bool_status' => false, + 'profile_id' => null, + ], $customer->getDirtyAttributes() ); $this->assertEquals( - ['email' => 'adam@example.com', 'address' => null], + [ + 'email' => 'adam@example.com', + 'address' => null, + 'status' => 0, + ], $customer->getDirtyAttributes(['id', 'email', 'address', 'status', 'unknown']), ); @@ -916,34 +948,4 @@ public function testGetDirtyAttributesAfterFind(): void $customer->getDirtyAttributes(['id', 'email', 'address', 'status', 'unknown']), ); } - - public function testGetDirtyAttributesWithProperties(): void - { - $this->checkFixture($this->db, 'customer'); - - $customer = new CustomerWithProperties($this->db); - $this->assertSame([ - 'name' => null, - 'address' => null, - ], $customer->getDirtyAttributes()); - - $customerQuery = new ActiveQuery(CustomerWithProperties::class, $this->db); - $customer = $customerQuery->findOne(1); - - $this->assertSame([], $customer->getDirtyAttributes()); - - $customer->setEmail('adam@example.com'); - $customer->setName('Adam'); - $customer->setAddress(null); - $customer->setStatus(null); - - $this->assertEquals( - ['email' => 'adam@example.com', 'name' => 'Adam', 'address' => null, 'status' => null], - $customer->getDirtyAttributes(), - ); - $this->assertEquals( - ['email' => 'adam@example.com', 'address' => null], - $customer->getDirtyAttributes(['id', 'email', 'address', 'unknown']), - ); - } } diff --git a/tests/BatchQueryResultTest.php b/tests/BatchQueryResultTest.php index c7de6fd97..2398272e9 100644 --- a/tests/BatchQueryResultTest.php +++ b/tests/BatchQueryResultTest.php @@ -38,9 +38,9 @@ public function testQuery(): void } $this->assertCount(3, $allRows); - $this->assertEquals('user1', $allRows[0]['name']); - $this->assertEquals('user2', $allRows[1]['name']); - $this->assertEquals('user3', $allRows[2]['name']); + $this->assertEquals('user1', $allRows[0]->getName()); + $this->assertEquals('user2', $allRows[1]->getName()); + $this->assertEquals('user3', $allRows[2]->getName()); /** rewind */ $allRows = []; @@ -79,9 +79,9 @@ public function testQuery(): void } $this->assertCount(3, $allRows); - $this->assertEquals('address1', $allRows['user1']['address']); - $this->assertEquals('address2', $allRows['user2']['address']); - $this->assertEquals('address3', $allRows['user3']['address']); + $this->assertEquals('address1', $allRows['user1']->getAddress()); + $this->assertEquals('address2', $allRows['user2']->getAddress()); + $this->assertEquals('address3', $allRows['user3']->getAddress()); /** each */ $customerQuery = new ActiveQuery(Customer::class, $this->db); @@ -94,9 +94,9 @@ public function testQuery(): void $allRows[$index] = $row; } $this->assertCount(3, $allRows); - $this->assertEquals('user1', $allRows[0]['name']); - $this->assertEquals('user2', $allRows[1]['name']); - $this->assertEquals('user3', $allRows[2]['name']); + $this->assertEquals('user1', $allRows[0]->getName()); + $this->assertEquals('user2', $allRows[1]->getName()); + $this->assertEquals('user3', $allRows[2]->getName()); /** each with key */ $customerQuery = new ActiveQuery(Customer::class, $this->db); @@ -110,9 +110,9 @@ public function testQuery(): void } $this->assertCount(3, $allRows); - $this->assertEquals('address1', $allRows['user1']['address']); - $this->assertEquals('address2', $allRows['user2']['address']); - $this->assertEquals('address3', $allRows['user3']['address']); + $this->assertEquals('address1', $allRows['user1']->getAddress()); + $this->assertEquals('address2', $allRows['user2']->getAddress()); + $this->assertEquals('address3', $allRows['user3']->getAddress()); } public function testActiveQuery(): void @@ -131,9 +131,9 @@ public function testActiveQuery(): void } $this->assertCount(3, $customers); - $this->assertCount(1, $customers[0]->orders); - $this->assertCount(2, $customers[1]->orders); - $this->assertCount(0, $customers[2]->orders); + $this->assertCount(1, $customers[0]->getOrders()); + $this->assertCount(2, $customers[1]->getOrders()); + $this->assertCount(0, $customers[2]->getOrders()); } public function testBatchWithIndexBy(): void @@ -147,9 +147,9 @@ public function testBatchWithIndexBy(): void $customers = $this->getAllRowsFromBatch($query->batch(2)); $this->assertCount(3, $customers); - $this->assertEquals('user1', $customers[0]->name); - $this->assertEquals('user2', $customers[1]->name); - $this->assertEquals('user3', $customers[2]->name); + $this->assertEquals('user1', $customers[0]->getName()); + $this->assertEquals('user2', $customers[1]->getName()); + $this->assertEquals('user3', $customers[2]->getName()); } protected function getAllRowsFromBatch(BatchQueryResultInterface $batch): array diff --git a/tests/Driver/Mssql/MagicActiveRecordTest.php b/tests/Driver/Mssql/MagicActiveRecordTest.php new file mode 100644 index 000000000..219c6da92 --- /dev/null +++ b/tests/Driver/Mssql/MagicActiveRecordTest.php @@ -0,0 +1,68 @@ +db = $mssqlHelper->createConnection(); + } + + protected function tearDown(): void + { + parent::tearDown(); + + $this->db->close(); + + unset($this->db); + } + + public function testSaveWithTrigger(): void + { + $this->checkFixture($this->db, 'test_trigger'); + + // drop trigger if exist + $sql = <<db->createCommand($sql)->execute(); + + // create trigger + $sql = <<db->createCommand($sql)->execute(); + + $record = new TestTrigger($this->db); + + $record->stringcol = 'test'; + + $this->assertTrue($record->save()); + $this->assertEquals(1, $record->id); + + $testRecordQuery = new ActiveQuery(TestTriggerAlert::class, $this->db); + + $this->assertEquals('test', $testRecordQuery->findOne(1)->stringcol); + } +} diff --git a/tests/Driver/Mysql/ActiveRecordTest.php b/tests/Driver/Mysql/ActiveRecordTest.php index 9f72399a7..92972d7aa 100644 --- a/tests/Driver/Mysql/ActiveRecordTest.php +++ b/tests/Driver/Mysql/ActiveRecordTest.php @@ -5,6 +5,7 @@ namespace Yiisoft\ActiveRecord\Tests\Driver\Mysql; use Yiisoft\ActiveRecord\ActiveQuery; +use Yiisoft\ActiveRecord\Tests\Driver\Mysql\Stubs\Type; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Beta; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer; use Yiisoft\ActiveRecord\Tests\Support\MysqlHelper; @@ -28,23 +29,56 @@ protected function tearDown(): void unset($this->db); } + public function testCastValues(): void + { + $this->checkFixture($this->db, 'type'); + + $arClass = new Type($this->db); + + $arClass->int_col = 123; + $arClass->int_col2 = 456; + $arClass->smallint_col = 42; + $arClass->char_col = '1337'; + $arClass->char_col2 = 'test'; + $arClass->char_col3 = 'test123'; + $arClass->enum_col = 'B'; + $arClass->float_col = 3.742; + $arClass->float_col2 = 42.1337; + $arClass->bool_col = true; + $arClass->bool_col2 = false; + + $arClass->save(); + + /** @var $model Type */ + $aqClass = new ActiveQuery(Type::class, $this->db); + $query = $aqClass->onePopulate(); + + $this->assertSame(123, $query->int_col); + $this->assertSame(456, $query->int_col2); + $this->assertSame(42, $query->smallint_col); + $this->assertSame('1337', trim($query->char_col)); + $this->assertSame('test', $query->char_col2); + $this->assertSame('test123', $query->char_col3); + $this->assertSame('B', $query->enum_col); + } + public function testExplicitPkOnAutoIncrement(): void { $this->checkFixture($this->db, 'customer'); $customer = new Customer($this->db); - $customer->id = 1337; - $customer->email = 'user1337@example.com'; - $customer->name = 'user1337'; - $customer->address = 'address1337'; + $customer->setId(1337); + $customer->setEmail('user1337@example.com'); + $customer->setName('user1337'); + $customer->setAddress('address1337'); - $this->assertTrue($customer->isNewRecord); + $this->assertTrue($customer->getIsNewRecord()); $customer->save(); - $this->assertEquals(1337, $customer->id); - $this->assertFalse($customer->isNewRecord); + $this->assertEquals(1337, $customer->getId()); + $this->assertFalse($customer->getIsNewRecord()); } /** @@ -64,9 +98,9 @@ public function testEagerLoadingUsingStringIdentifiers(): void /** @var Beta[] $betas */ foreach ($betas as $beta) { - $this->assertNotNull($beta->alpha); - $this->assertEquals($beta->alpha_string_identifier, $beta->alpha->string_identifier); - $alphaIdentifiers[] = $beta->alpha->string_identifier; + $this->assertNotNull($beta->getAlpha()); + $this->assertEquals($beta->getAlphaStringIdentifier(), $beta->getAlpha()->getStringIdentifier()); + $alphaIdentifiers[] = $beta->getAlpha()->getStringIdentifier(); } $this->assertEquals(['1', '01', '001', '001', '2', '2b', '2b', '02'], $alphaIdentifiers); diff --git a/tests/Driver/Mysql/MagicActiveRecordTest.php b/tests/Driver/Mysql/MagicActiveRecordTest.php new file mode 100644 index 000000000..411dbe03e --- /dev/null +++ b/tests/Driver/Mysql/MagicActiveRecordTest.php @@ -0,0 +1,74 @@ +db = $mysqlHelper->createConnection(); + } + + protected function tearDown(): void + { + parent::tearDown(); + + $this->db->close(); + + unset($this->db); + } + + public function testExplicitPkOnAutoIncrement(): void + { + $this->checkFixture($this->db, 'customer'); + + $customer = new Customer($this->db); + + $customer->id = 1337; + $customer->email = 'user1337@example.com'; + $customer->name = 'user1337'; + $customer->address = 'address1337'; + + $this->assertTrue($customer->isNewRecord); + + $customer->save(); + + $this->assertEquals(1337, $customer->id); + $this->assertFalse($customer->isNewRecord); + } + + /** + * {@see https://github.com/yiisoft/yii2/issues/15482} + */ + public function testEagerLoadingUsingStringIdentifiers(): void + { + $this->checkFixture($this->db, 'beta'); + + $betaQuery = new ActiveQuery(Beta::class, $this->db); + + $betas = $betaQuery->with('alpha')->all(); + + $this->assertNotEmpty($betas); + + $alphaIdentifiers = []; + + /** @var Beta[] $betas */ + foreach ($betas as $beta) { + $this->assertNotNull($beta->alpha); + $this->assertEquals($beta->alpha_string_identifier, $beta->alpha->string_identifier); + $alphaIdentifiers[] = $beta->alpha->string_identifier; + } + + $this->assertEquals(['1', '01', '001', '001', '2', '2b', '2b', '02'], $alphaIdentifiers); + } +} diff --git a/tests/Driver/Mysql/Stubs/Type.php b/tests/Driver/Mysql/Stubs/Type.php new file mode 100644 index 000000000..860da85e3 --- /dev/null +++ b/tests/Driver/Mysql/Stubs/Type.php @@ -0,0 +1,13 @@ +db); } + public function testFindLimit(): void + { + $this->checkFixture($this->db, 'customer', true); + + /** one */ + $customerQuery = new ActiveQuery(CustomerWithRownumid::class, $this->db); + $customer = $customerQuery->orderBy('id')->onePopulate(); + $this->assertEquals('user1', $customer->getName()); + + /** all */ + $customerQuery = new ActiveQuery(CustomerWithRownumid::class, $this->db); + $customers = $customerQuery->allPopulate(); + $this->assertCount(3, $customers); + + /** limit */ + $customerQuery = new ActiveQuery(CustomerWithRownumid::class, $this->db); + $customers = $customerQuery->orderBy('id')->limit(1)->allPopulate(); + $this->assertCount(1, $customers); + $this->assertEquals('user1', $customers[0]->getName()); + + $customers = $customerQuery->orderBy('id')->limit(1)->offset(1)->allPopulate(); + $this->assertCount(1, $customers); + $this->assertEquals('user2', $customers[0]->getName()); + + $customers = $customerQuery->orderBy('id')->limit(1)->offset(2)->allPopulate(); + $this->assertCount(1, $customers); + $this->assertEquals('user3', $customers[0]->getName()); + + $customers = $customerQuery->orderBy('id')->limit(2)->offset(1)->allPopulate(); + $this->assertCount(2, $customers); + $this->assertEquals('user2', $customers[0]->getName()); + $this->assertEquals('user3', $customers[1]->getName()); + + $customers = $customerQuery->limit(2)->offset(3)->allPopulate(); + $this->assertCount(0, $customers); + + /** offset */ + $customerQuery = new ActiveQuery(CustomerWithRownumid::class, $this->db); + $customer = $customerQuery->orderBy('id')->offset(0)->onePopulate(); + $this->assertEquals('user1', $customer->getName()); + + $customer = $customerQuery->orderBy('id')->offset(1)->onePopulate(); + $this->assertEquals('user2', $customer->getName()); + + $customer = $customerQuery->orderBy('id')->offset(2)->onePopulate(); + $this->assertEquals('user3', $customer->getName()); + + $customer = $customerQuery->offset(3)->onePopulate(); + $this->assertNull($customer); + } + public function testFindEager(): void { $this->checkFixture($this->db, 'customer', true); @@ -40,17 +92,17 @@ public function testFindEager(): void $this->assertTrue($customers[1]->isRelationPopulated('orders')); $this->assertTrue($customers[2]->isRelationPopulated('orders')); $this->assertTrue($customers[3]->isRelationPopulated('orders')); - $this->assertCount(1, $customers[1]->orders); - $this->assertCount(2, $customers[2]->orders); - $this->assertCount(0, $customers[3]->orders); + $this->assertCount(1, $customers[1]->getOrders()); + $this->assertCount(2, $customers[2]->getOrders()); + $this->assertCount(0, $customers[3]->getOrders()); - unset($customers[1]->orders); + $customers[1]->resetRelation('orders'); $this->assertFalse($customers[1]->isRelationPopulated('orders')); $customer = $customerQuery->where(['id' => 1])->with('orders')->onePopulate(); $this->assertTrue($customer->isRelationPopulated('orders')); - $this->assertCount(1, $customer->orders); - $this->assertCount(1, $customer->relatedRecords); + $this->assertCount(1, $customer->getOrders()); + $this->assertCount(1, $customer->getRelatedRecords()); /** multiple with() calls */ $orderQuery = new ActiveQuery(Order::class, $this->db); diff --git a/tests/Driver/Oracle/ActiveQueryTest.php b/tests/Driver/Oracle/ActiveQueryTest.php index 13d9a3695..ccce1fa4e 100644 --- a/tests/Driver/Oracle/ActiveQueryTest.php +++ b/tests/Driver/Oracle/ActiveQueryTest.php @@ -7,7 +7,7 @@ use Throwable; use Yiisoft\ActiveRecord\ActiveQuery; use Yiisoft\ActiveRecord\Tests\Driver\Oracle\Stubs\Order; -use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\BitValues; +use Yiisoft\ActiveRecord\Tests\Driver\Oracle\Stubs\BitValues; use Yiisoft\ActiveRecord\Tests\Support\OracleHelper; use Yiisoft\Db\Exception\Exception; use Yiisoft\Db\Exception\InvalidConfigException; @@ -62,9 +62,9 @@ public function testJoinWithAlias(string $aliasMethod): void } $this->assertCount(3, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); - $this->assertEquals(1, $orders[2]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); + $this->assertEquals(1, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); $this->assertTrue($orders[1]->isRelationPopulated('customer')); $this->assertTrue($orders[2]->isRelationPopulated('customer')); @@ -84,8 +84,8 @@ public function testJoinWithAlias(string $aliasMethod): void } $this->assertCount(2, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('customer')); $this->assertTrue($orders[1]->isRelationPopulated('customer')); @@ -104,8 +104,8 @@ public function testJoinWithAlias(string $aliasMethod): void } $this->assertCount(2, $orders); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); $this->assertFalse($orders[0]->isRelationPopulated('customer')); $this->assertFalse($orders[1]->isRelationPopulated('customer')); @@ -128,10 +128,10 @@ public function testJoinWithAlias(string $aliasMethod): void } $this->assertCount(2, $orders); - $this->assertCount(2, $orders[0]->books); - $this->assertCount(1, $orders[1]->books); - $this->assertEquals(1, $orders[0]->id); - $this->assertEquals(3, $orders[1]->id); + $this->assertCount(2, $orders[0]->getBooks()); + $this->assertCount(1, $orders[1]->getBooks()); + $this->assertEquals(1, $orders[0]->getId()); + $this->assertEquals(3, $orders[1]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('books')); $this->assertTrue($orders[1]->isRelationPopulated('books')); @@ -171,11 +171,11 @@ public function testJoinWithAlias(string $aliasMethod): void } $this->assertCount(1, $orders); - $this->assertCount(3, $orders[0]->items); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(2, $orders[0]->items[0]->category->id); + $this->assertCount(3, $orders[0]->getItems()); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId()); $this->assertTrue($orders[0]->isRelationPopulated('items')); - $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category')); + $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category')); /** join with ON condition */ if ($aliasMethod === 'explicit' || $aliasMethod === 'querysyntax') { @@ -185,12 +185,12 @@ public function testJoinWithAlias(string $aliasMethod): void $orders = $orderQuery->joinWith(["$relationName b"])->orderBy('order.id')->all(); $this->assertCount(3, $orders); - $this->assertCount(2, $orders[0]->$relationName); - $this->assertCount(0, $orders[1]->$relationName); - $this->assertCount(1, $orders[2]->$relationName); - $this->assertEquals(1, $orders[0]->id); - $this->assertEquals(2, $orders[1]->id); - $this->assertEquals(3, $orders[2]->id); + $this->assertCount(2, $orders[0]->relation($relationName)); + $this->assertCount(0, $orders[1]->relation($relationName)); + $this->assertCount(1, $orders[2]->relation($relationName)); + $this->assertEquals(1, $orders[0]->getId()); + $this->assertEquals(2, $orders[1]->getId()); + $this->assertEquals(3, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated($relationName)); $this->assertTrue($orders[1]->isRelationPopulated($relationName)); $this->assertTrue($orders[2]->isRelationPopulated($relationName)); @@ -204,12 +204,12 @@ public function testJoinWithAlias(string $aliasMethod): void $orders = $orderQuery->joinWith([(string)$relationName])->orderBy('order.id')->all(); $this->assertCount(3, $orders); - $this->assertCount(2, $orders[0]->$relationName); - $this->assertCount(0, $orders[1]->$relationName); - $this->assertCount(1, $orders[2]->$relationName); - $this->assertEquals(1, $orders[0]->id); - $this->assertEquals(2, $orders[1]->id); - $this->assertEquals(3, $orders[2]->id); + $this->assertCount(2, $orders[0]->relation($relationName)); + $this->assertCount(0, $orders[1]->relation($relationName)); + $this->assertCount(1, $orders[2]->relation($relationName)); + $this->assertEquals(1, $orders[0]->getId()); + $this->assertEquals(2, $orders[1]->getId()); + $this->assertEquals(3, $orders[2]->getId()); $this->assertTrue($orders[0]->isRelationPopulated($relationName)); $this->assertTrue($orders[1]->isRelationPopulated($relationName)); $this->assertTrue($orders[2]->isRelationPopulated($relationName)); @@ -246,7 +246,7 @@ public function testJoinWithAlias(string $aliasMethod): void $customer = $customerQuery->where([$query->applyAlias('order', 'id') => 1])->onePopulate(); } - $this->assertEquals(1, $customer->id); + $this->assertEquals(1, $customer->getId()); $this->assertNotNull($customer); /** join with sub-relation called inside Closure */ @@ -270,11 +270,11 @@ public function testJoinWithAlias(string $aliasMethod): void )->orderBy('order.id')->all(); $this->assertCount(1, $orders); - $this->assertCount(3, $orders[0]->items); - $this->assertEquals(2, $orders[0]->id); - $this->assertEquals(2, $orders[0]->items[0]->category->id); + $this->assertCount(3, $orders[0]->getItems()); + $this->assertEquals(2, $orders[0]->getId()); + $this->assertEquals(2, $orders[0]->getItems()[0]->getCategory()->getId()); $this->assertTrue($orders[0]->isRelationPopulated('items')); - $this->assertTrue($orders[0]->items[0]->isRelationPopulated('category')); + $this->assertTrue($orders[0]->getItems()[0]->isRelationPopulated('category')); } /** @@ -299,7 +299,7 @@ public function testJoinWithSameTable(): void $orders, $query->createCommand()->getRawSql() . print_r($orders, true) ); - $this->assertEquals(2, $orders[0]->id); + $this->assertEquals(2, $orders[0]->getId()); $this->assertFalse($orders[0]->isRelationPopulated('bookItems')); $this->assertFalse($orders[0]->isRelationPopulated('movieItems')); @@ -312,9 +312,9 @@ public function testJoinWithSameTable(): void $orders, $query->createCommand()->getRawSql() . print_r($orders, true) ); - $this->assertCount(0, $orders[0]->bookItems); - $this->assertCount(3, $orders[0]->movieItems); - $this->assertEquals(2, $orders[0]->id); + $this->assertCount(0, $orders[0]->getBookItems()); + $this->assertCount(3, $orders[0]->getMovieItems()); + $this->assertEquals(2, $orders[0]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('bookItems')); $this->assertTrue($orders[0]->isRelationPopulated('movieItems')); @@ -345,7 +345,7 @@ public function testJoinWithSameTable(): void $orders, $query->createCommand()->getRawSql() . print_r($orders, true) ); - $this->assertEquals(2, $orders[0]->id); + $this->assertEquals(2, $orders[0]->getId()); $this->assertFalse($orders[0]->isRelationPopulated('itemsIndexed')); /** with eager loading, only for one relation as it would be overwritten otherwise. */ @@ -369,8 +369,8 @@ public function testJoinWithSameTable(): void )->where(['{{movies}}.[[name]]' => 'Toy Story']); $orders = $query->all(); $this->assertCount(1, $orders, $query->createCommand()->getRawSql() . print_r($orders, true)); - $this->assertCount(3, $orders[0]->itemsIndexed); - $this->assertEquals(2, $orders[0]->id); + $this->assertCount(3, $orders[0]->getItemsIndexed()); + $this->assertEquals(2, $orders[0]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('itemsIndexed')); /** with eager loading, and the other relation */ @@ -395,8 +395,8 @@ public function testJoinWithSameTable(): void ->where(['{{movies}}.[[name]]' => 'Toy Story']); $orders = $query->all(); $this->assertCount(1, $orders, $query->createCommand()->getRawSql() . print_r($orders, true)); - $this->assertCount(0, $orders[0]->itemsIndexed); - $this->assertEquals(2, $orders[0]->id); + $this->assertCount(0, $orders[0]->getItemsIndexed()); + $this->assertEquals(2, $orders[0]->getId()); $this->assertTrue($orders[0]->isRelationPopulated('itemsIndexed')); } diff --git a/tests/Driver/Oracle/ActiveRecordTest.php b/tests/Driver/Oracle/ActiveRecordTest.php index dbad51704..5b41b4541 100644 --- a/tests/Driver/Oracle/ActiveRecordTest.php +++ b/tests/Driver/Oracle/ActiveRecordTest.php @@ -102,22 +102,22 @@ public function testBooleanAttribute(): void $customer = new Customer($this->db); - $customer->name = 'boolean customer'; - $customer->email = 'mail@example.com'; - $customer->status = '1'; + $customer->setName('boolean customer'); + $customer->setEmail('mail@example.com'); + $customer->setStatus(1); $customer->save(); $customer->refresh(); - $this->assertEquals('1', $customer->status); + $this->assertEquals(1, $customer->getStatus()); - $customer->status = '0'; + $customer->setStatus(0); $customer->save(); $customer->refresh(); - $this->assertEquals('0', $customer->status); + $this->assertEquals(0, $customer->getStatus()); $customerQuery = new ActiveQuery(Customer::class, $this->db); - $customers = $customerQuery->where(['status' => '1'])->all(); + $customers = $customerQuery->where(['status' => 1])->all(); $this->assertCount(2, $customers); $customerQuery = new ActiveQuery(Customer::class, $this->db); diff --git a/tests/Driver/Oracle/BatchQueryResultTest.php b/tests/Driver/Oracle/BatchQueryResultTest.php index 228e03698..6324c7dd6 100644 --- a/tests/Driver/Oracle/BatchQueryResultTest.php +++ b/tests/Driver/Oracle/BatchQueryResultTest.php @@ -4,6 +4,8 @@ namespace Yiisoft\ActiveRecord\Tests\Driver\Oracle; +use Yiisoft\ActiveRecord\ActiveQuery; +use Yiisoft\ActiveRecord\Tests\Driver\Oracle\Stubs\Customer; use Yiisoft\ActiveRecord\Tests\Support\OracleHelper; final class BatchQueryResultTest extends \Yiisoft\ActiveRecord\Tests\BatchQueryResultTest @@ -24,4 +26,20 @@ protected function tearDown(): void unset($this->db); } + + public function testBatchWithIndexBy(): void + { + $this->checkFixture($this->db, 'customer'); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + + $query = $customerQuery->orderBy('id')->limit(3)->indexBy('id'); + + $customers = $this->getAllRowsFromBatch($query->batch(2)); + + $this->assertCount(3, $customers); + $this->assertEquals('user1', $customers[0]->getName()); + $this->assertEquals('user2', $customers[1]->getName()); + $this->assertEquals('user3', $customers[2]->getName()); + } } diff --git a/tests/Driver/Oracle/MagicActiveRecordTest.php b/tests/Driver/Oracle/MagicActiveRecordTest.php new file mode 100644 index 000000000..c2c95fd59 --- /dev/null +++ b/tests/Driver/Oracle/MagicActiveRecordTest.php @@ -0,0 +1,169 @@ +db = $oracleHelper->createConnection(); + } + + protected function tearDown(): void + { + parent::tearDown(); + + $this->db->close(); + + unset($this->db); + } + + public function testCastValues(): void + { + $this->markTestSkipped('Cant bind floats without support from a custom PDO driver.'); + + $this->checkFixture($this->db, 'customer'); + + $arClass = new Type($this->db); + $arClass->int_col = 123; + $arClass->int_col2 = 456; + $arClass->smallint_col = 42; + $arClass->char_col = '1337'; + $arClass->char_col2 = 'test'; + $arClass->char_col3 = 'test123'; + /** can't bind floats without support from a custom PDO driver */ + $arClass->float_col = 2; + $arClass->float_col2 = 1; + $arClass->bool_col = 1; + $arClass->bool_col2 = 0; + $arClass->save(); + + $aqClass = new ActiveQuery(Type::class, $this->db); + $query = $aqClass->onePopulate(); + + $this->assertSame(123, $query->int_col); + $this->assertSame(456, $query->int_col2); + $this->assertSame(42, $query->smallint_col); + $this->assertSame('1337', trim($query->char_col)); + $this->assertSame('test', $query->char_col2); + $this->assertSame('test123', $query->char_col3); + $this->assertSame(2.0, $query->float_col); + $this->assertSame(1.0, $query->float_col2); + $this->assertEquals('1', $query->bool_col); + $this->assertEquals('0', $query->bool_col2); + } + + public function testDefaultValues(): void + { + $this->checkFixture($this->db, 'customer'); + + $arClass = new Type($this->db); + $arClass->loadDefaultValues(); + $this->assertEquals(1, $arClass->int_col2); + $this->assertEquals('something', $arClass->char_col2); + $this->assertEquals(1.23, $arClass->float_col2); + $this->assertEquals(33.22, $arClass->numeric_col); + $this->assertEquals('1', $arClass->bool_col2); + + // not testing $arClass->time, because oci\Schema can't read default value + + $arClass = new Type($this->db); + $arClass->char_col2 = 'not something'; + + $arClass->loadDefaultValues(); + $this->assertEquals('not something', $arClass->char_col2); + + $arClass = new Type($this->db); + $arClass->char_col2 = 'not something'; + + $arClass->loadDefaultValues(false); + $this->assertEquals('something', $arClass->char_col2); + } + + /** + * Some PDO implementations (e.g. cubrid) do not support boolean values. + * + * Make sure this does not affect AR layer. + */ + public function testBooleanAttribute(): void + { + $this->checkFixture($this->db, 'customer', true); + + $customer = new Customer($this->db); + + $customer->name = 'boolean customer'; + $customer->email = 'mail@example.com'; + $customer->status = '1'; + + $customer->save(); + $customer->refresh(); + $this->assertEquals('1', $customer->status); + + $customer->status = '0'; + $customer->save(); + + $customer->refresh(); + $this->assertEquals('0', $customer->status); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + $customers = $customerQuery->where(['status' => '1'])->all(); + $this->assertCount(2, $customers); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + $customers = $customerQuery->where(['status' => '0'])->all(); + $this->assertCount(1, $customers); + } + + public function testToArray(): void + { + $this->checkFixture($this->db, 'customer', true); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + $customer = $customerQuery->findOne(1); + + $this->assertSame( + [ + 'id' => 1, + 'email' => 'user1@example.com', + 'name' => 'user1', + 'address' => 'address1', + 'status' => 1, + 'bool_status' => '1', + 'profile_id' => 1, + ], + $customer->toArray(), + ); + } + + public function testToArrayWithClosure(): void + { + $this->checkFixture($this->db, 'customer', true); + + $customerQuery = new ActiveQuery(CustomerClosureField::class, $this->db); + $customer = $customerQuery->findOne(1); + + $this->assertSame( + [ + 'id' => 1, + 'email' => 'user1@example.com', + 'name' => 'user1', + 'address' => 'address1', + 'status' => 'active', + 'bool_status' => '1', + 'profile_id' => 1, + ], + $customer->toArray(), + ); + } +} diff --git a/tests/Driver/Oracle/Stubs/BitValues.php b/tests/Driver/Oracle/Stubs/BitValues.php new file mode 100644 index 000000000..a077b6ec7 --- /dev/null +++ b/tests/Driver/Oracle/Stubs/BitValues.php @@ -0,0 +1,19 @@ +hasMany(Order::class, ['customer_id' => 'id'])->orderBy('{{customer}}.[[id]]'); diff --git a/tests/Driver/Oracle/Stubs/MagicCustomer.php b/tests/Driver/Oracle/Stubs/MagicCustomer.php new file mode 100644 index 000000000..5787ffad8 --- /dev/null +++ b/tests/Driver/Oracle/Stubs/MagicCustomer.php @@ -0,0 +1,24 @@ +hasMany(Order::class, ['customer_id' => 'id'])->orderBy('{{customer}}.[[id]]'); + } +} diff --git a/tests/Driver/Oracle/Stubs/MagicOrder.php b/tests/Driver/Oracle/Stubs/MagicOrder.php new file mode 100644 index 000000000..89d526c15 --- /dev/null +++ b/tests/Driver/Oracle/Stubs/MagicOrder.php @@ -0,0 +1,23 @@ +hasOne(MagicCustomer::class, ['id' => 'customer_id']); + } +} diff --git a/tests/Driver/Pgsql/ActiveRecordTest.php b/tests/Driver/Pgsql/ActiveRecordTest.php index 116d76532..74707f18a 100644 --- a/tests/Driver/Pgsql/ActiveRecordTest.php +++ b/tests/Driver/Pgsql/ActiveRecordTest.php @@ -7,6 +7,7 @@ use ArrayAccess; use Traversable; use Yiisoft\ActiveRecord\ActiveQuery; +use Yiisoft\ActiveRecord\Tests\Driver\Pgsql\Stubs\Type; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\ArrayAndJsonTypes; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Beta; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\BoolAR; @@ -39,20 +40,79 @@ protected function tearDown(): void unset($this->db); } + public function testDefaultValues(): void + { + $this->checkFixture($this->db, 'type'); + + $arClass = new Type($this->db); + + $arClass->loadDefaultValues(); + + $this->assertEquals(1, $arClass->int_col2); + $this->assertEquals('something', $arClass->char_col2); + $this->assertEquals(1.23, $arClass->float_col2); + $this->assertEquals(33.22, $arClass->numeric_col); + $this->assertEquals(true, $arClass->bool_col2); + $this->assertEquals('2002-01-01 00:00:00', $arClass->time); + + $arClass = new Type($this->db); + $arClass->char_col2 = 'not something'; + + $arClass->loadDefaultValues(); + $this->assertEquals('not something', $arClass->char_col2); + + $arClass = new Type($this->db); + $arClass->char_col2 = 'not something'; + + $arClass->loadDefaultValues(false); + $this->assertEquals('something', $arClass->char_col2); + } + + public function testCastValues(): void + { + $this->checkFixture($this->db, 'type'); + + $arClass = new Type($this->db); + + $arClass->int_col = 123; + $arClass->int_col2 = 456; + $arClass->smallint_col = 42; + $arClass->char_col = '1337'; + $arClass->char_col2 = 'test'; + $arClass->char_col3 = 'test123'; + $arClass->float_col = 3.742; + $arClass->float_col2 = 42.1337; + $arClass->bool_col = true; + $arClass->bool_col2 = false; + + $arClass->save(); + + /** @var $model Type */ + $aqClass = new ActiveQuery(Type::class, $this->db); + $query = $aqClass->onePopulate(); + + $this->assertSame(123, $query->int_col); + $this->assertSame(456, $query->int_col2); + $this->assertSame(42, $query->smallint_col); + $this->assertSame('1337', trim($query->char_col)); + $this->assertSame('test', $query->char_col2); + $this->assertSame('test123', $query->char_col3); + } + public function testExplicitPkOnAutoIncrement(): void { $this->checkFixture($this->db, 'customer'); $customer = new Customer($this->db); - $customer->id = 1337; - $customer->email = 'user1337@example.com'; - $customer->name = 'user1337'; - $customer->address = 'address1337'; - $this->assertTrue($customer->isNewRecord); + $customer->setId(1337); + $customer->setEmail('user1337@example.com'); + $customer->setName('user1337'); + $customer->setAddress('address1337'); + $this->assertTrue($customer->getIsNewRecord()); $customer->save(); - $this->assertEquals(1337, $customer->id); - $this->assertFalse($customer->isNewRecord); + $this->assertEquals(1337, $customer->getId()); + $this->assertFalse($customer->getIsNewRecord()); } /** @@ -70,9 +130,9 @@ public function testEagerLoadingUsingStringIdentifiers(): void /** @var Beta[] $betas */ foreach ($betas as $beta) { - $this->assertNotNull($beta->alpha); - $this->assertEquals($beta->alpha_string_identifier, $beta->alpha->string_identifier); - $alphaIdentifiers[] = $beta->alpha->string_identifier; + $this->assertNotNull($beta->getAlpha()); + $this->assertEquals($beta->getAlphaStringIdentifier(), $beta->getAlpha()->getStringIdentifier()); + $alphaIdentifiers[] = $beta->getAlpha()->getStringIdentifier(); } $this->assertEquals(['1', '01', '001', '001', '2', '2b', '2b', '02'], $alphaIdentifiers); @@ -83,19 +143,19 @@ public function testBooleanAttribute(): void $this->checkFixture($this->db, 'customer', true); $customer = new Customer($this->db); - $customer->name = 'boolean customer'; - $customer->email = 'mail@example.com'; - $customer->bool_status = false; + $customer->setName('boolean customer'); + $customer->setEmail('mail@example.com'); + $customer->setBoolStatus(false); $customer->save(); $customer->refresh(); - $this->assertFalse($customer->bool_status); + $this->assertFalse($customer->getBoolStatus()); - $customer->bool_status = true; + $customer->setBoolStatus(true); $customer->save(); $customer->refresh(); - $this->assertTrue($customer->bool_status); + $this->assertTrue($customer->getBoolStatus()); $customerQuery = new ActiveQuery(Customer::class, $this->db); $customers = $customerQuery->where(['bool_status' => true])->all(); @@ -177,8 +237,8 @@ public function testBooleanDefaultValues(): void $arClass = new BoolAR($this->db); $this->assertNull($arClass->bool_col); - $this->assertNull($arClass->default_true); - $this->assertNull($arClass->default_false); + $this->assertTrue($arClass->default_true); + $this->assertFalse($arClass->default_false); $arClass->loadDefaultValues(); @@ -198,7 +258,7 @@ public function testPrimaryKeyAfterSave(): void $record->save(); - $this->assertEquals(5, $record->primaryKey); + $this->assertEquals(5, $record->getPrimaryKey()); } public static function arrayValuesProvider(): array @@ -303,7 +363,7 @@ public function testArrayValues($attributes): void $type = new ArrayAndJsonTypes($this->db); foreach ($attributes as $attribute => $expected) { - $type->$attribute = $expected[0]; + $type->setAttribute($attribute, $expected[0]); } $type->save(); @@ -314,7 +374,7 @@ public function testArrayValues($attributes): void foreach ($attributes as $attribute => $expected) { $expected = $expected[1] ?? $expected[0]; - $value = $type->$attribute; + $value = $type->getAttribute($attribute); if ($expected instanceof ArrayExpression) { $expected = $expected->getValue(); @@ -326,7 +386,7 @@ public function testArrayValues($attributes): void $this->assertInstanceOf(ArrayAccess::class, $value); $this->assertInstanceOf(Traversable::class, $value); /** testing arrayaccess */ - foreach ($type->$attribute as $key => $v) { + foreach ($type->getAttribute($attribute) as $key => $v) { $this->assertSame($expected[$key], $value[$key]); } } diff --git a/tests/Driver/Pgsql/MagicActiveRecordTest.php b/tests/Driver/Pgsql/MagicActiveRecordTest.php new file mode 100644 index 000000000..20821bcb9 --- /dev/null +++ b/tests/Driver/Pgsql/MagicActiveRecordTest.php @@ -0,0 +1,384 @@ +db = $pgsqlHelper->createConnection(); + } + + protected function tearDown(): void + { + parent::tearDown(); + + $this->db->close(); + + unset($this->db); + } + + public function testExplicitPkOnAutoIncrement(): void + { + $this->checkFixture($this->db, 'customer'); + + $customer = new Customer($this->db); + $customer->id = 1337; + $customer->email = 'user1337@example.com'; + $customer->name = 'user1337'; + $customer->address = 'address1337'; + $this->assertTrue($customer->isNewRecord); + + $customer->save(); + $this->assertEquals(1337, $customer->id); + $this->assertFalse($customer->isNewRecord); + } + + /** + * {@see https://github.com/yiisoft/yii2/issues/15482} + */ + public function testEagerLoadingUsingStringIdentifiers(): void + { + $this->checkFixture($this->db, 'beta'); + + $betaQuery = new ActiveQuery(Beta::class, $this->db); + $betas = $betaQuery->with('alpha')->all(); + $this->assertNotEmpty($betas); + + $alphaIdentifiers = []; + + /** @var Beta[] $betas */ + foreach ($betas as $beta) { + $this->assertNotNull($beta->alpha); + $this->assertEquals($beta->alpha_string_identifier, $beta->alpha->string_identifier); + $alphaIdentifiers[] = $beta->alpha->string_identifier; + } + + $this->assertEquals(['1', '01', '001', '001', '2', '2b', '2b', '02'], $alphaIdentifiers); + } + + public function testBooleanAttribute(): void + { + $this->checkFixture($this->db, 'customer', true); + + $customer = new Customer($this->db); + $customer->name = 'boolean customer'; + $customer->email = 'mail@example.com'; + $customer->bool_status = false; + $customer->save(); + + $customer->refresh(); + $this->assertFalse($customer->bool_status); + + $customer->bool_status = true; + + $customer->save(); + $customer->refresh(); + $this->assertTrue($customer->bool_status); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + $customers = $customerQuery->where(['bool_status' => true])->all(); + $this->assertCount(3, $customers); + + $customers = $customerQuery->where(['bool_status' => false])->all(); + $this->assertCount(1, $customers); + } + + public function testBooleanValues(): void + { + $this->checkFixture($this->db, 'bool_values'); + + $command = $this->db->createCommand(); + $command->batchInsert('bool_values', ['bool_col'], [[true], [false]])->execute(); + $boolARQuery = new ActiveQuery(BoolAR::class, $this->db); + + $this->assertTrue($boolARQuery->where(['bool_col' => true])->onePopulate()->bool_col); + $this->assertFalse($boolARQuery->where(['bool_col' => false])->onePopulate()->bool_col); + + $this->assertEquals(1, $boolARQuery->where('bool_col = TRUE')->count('*')); + $this->assertEquals(1, $boolARQuery->where('bool_col = FALSE')->count('*')); + $this->assertEquals(2, $boolARQuery->where('bool_col IN (TRUE, FALSE)')->count('*')); + + $this->assertEquals(1, $boolARQuery->where(['bool_col' => true])->count('*')); + $this->assertEquals(1, $boolARQuery->where(['bool_col' => false])->count('*')); + $this->assertEquals(2, $boolARQuery->where(['bool_col' => [true, false]])->count('*')); + + $this->assertEquals(1, $boolARQuery->where('bool_col = :bool_col', ['bool_col' => true])->count('*')); + $this->assertEquals(1, $boolARQuery->where('bool_col = :bool_col', ['bool_col' => false])->count('*')); + } + + /** + * {@see https://github.com/yiisoft/yii2/issues/4672} + */ + public function testBooleanValues2(): void + { + $this->checkFixture($this->db, 'bool_user'); + + //$this->db->setCharset('utf8'); + $this->db->createCommand('DROP TABLE IF EXISTS bool_user;')->execute(); + $this->db->createCommand()->createTable('bool_user', [ + 'id' => SchemaPgsql::TYPE_PK, + 'username' => SchemaPgsql::TYPE_STRING . ' NOT NULL', + 'auth_key' => SchemaPgsql::TYPE_STRING . '(32) NOT NULL', + 'password_hash' => SchemaPgsql::TYPE_STRING . ' NOT NULL', + 'password_reset_token' => SchemaPgsql::TYPE_STRING, + 'email' => SchemaPgsql::TYPE_STRING . ' NOT NULL', + 'role' => SchemaPgsql::TYPE_SMALLINT . ' NOT NULL DEFAULT 10', + 'status' => SchemaPgsql::TYPE_SMALLINT . ' NOT NULL DEFAULT 10', + 'created_at' => SchemaPgsql::TYPE_INTEGER . ' NOT NULL', + 'updated_at' => SchemaPgsql::TYPE_INTEGER . ' NOT NULL', + ])->execute(); + $this->db->createCommand()->addColumn( + 'bool_user', + 'is_deleted', + SchemaPgsql::TYPE_BOOLEAN . ' NOT NULL DEFAULT FALSE' + )->execute(); + + $user = new UserAR($this->db); + $user->username = 'test'; + $user->auth_key = 'test'; + $user->password_hash = 'test'; + $user->email = 'test@example.com'; + $user->created_at = time(); + $user->updated_at = time(); + $user->save(); + + $userQuery = new ActiveQuery(UserAR::class, $this->db); + $this->assertCount(1, $userQuery->where(['is_deleted' => false])->all()); + $this->assertCount(0, $userQuery->where(['is_deleted' => true])->all()); + $this->assertCount(1, $userQuery->where(['is_deleted' => [true, false]])->all()); + } + + public function testBooleanDefaultValues(): void + { + $this->checkFixture($this->db, 'bool_values'); + + $arClass = new BoolAR($this->db); + + $this->assertNull($arClass->bool_col); + $this->assertNull($arClass->default_true); + $this->assertNull($arClass->default_false); + + $arClass->loadDefaultValues(); + + $this->assertNull($arClass->bool_col); + $this->assertTrue($arClass->default_true); + $this->assertFalse($arClass->default_false); + $this->assertTrue($arClass->save()); + } + + public function testPrimaryKeyAfterSave(): void + { + $this->checkFixture($this->db, 'default_pk'); + + $record = new DefaultPk($this->db); + + $record->type = 'type'; + + $record->save(); + + $this->assertEquals(5, $record->primaryKey); + } + + public static function arrayValuesProvider(): array + { + return [ + 'simple arrays values' => [[ + 'intarray_col' => [ + new ArrayExpression([1,-2,null,'42'], 'int4', 1), + new ArrayExpression([1,-2,null,42], 'int4', 1), + ], + 'textarray2_col' => [ + new ArrayExpression([['text'], [null], [1]], 'text', 2), + new ArrayExpression([['text'], [null], ['1']], 'text', 2), + ], + 'json_col' => [['a' => 1, 'b' => null, 'c' => [1,3,5]]], + 'jsonb_col' => [[null, 'a', 'b', '\"', '{"af"}']], + 'jsonarray_col' => [new ArrayExpression([[',', 'null', true, 'false', 'f']], 'json')], + ]], + 'null arrays values' => [[ + 'intarray_col' => [ + null, + ], + 'textarray2_col' => [ + [null, null], + new ArrayExpression([null, null], 'text', 2), + ], + 'json_col' => [ + null, + ], + 'jsonarray_col' => [ + null, + ], + ]], + 'empty arrays values' => [[ + 'textarray2_col' => [ + [[], []], + new ArrayExpression([], 'text', 2), + ], + ]], + 'nested objects' => [[ + 'intarray_col' => [ + new ArrayExpression(new ArrayExpression([1,2,3]), 'int', 1), + new ArrayExpression([1,2,3], 'int4', 1), + ], + 'textarray2_col' => [ + new ArrayExpression([new ArrayExpression(['text']), [null], [1]], 'text', 2), + new ArrayExpression([['text'], [null], ['1']], 'text', 2), + ], + 'json_col' => [ + new JsonExpression(new JsonExpression(new JsonExpression(['a' => 1, 'b' => null, 'c' => new JsonExpression([1,3,5])]))), + ['a' => 1, 'b' => null, 'c' => [1,3,5]], + ], + 'jsonb_col' => [ + new JsonExpression(new ArrayExpression([1,2,3])), + [1,2,3], + ], + 'jsonarray_col' => [ + new ArrayExpression([new JsonExpression(['1', 2]), [3,4,5]], 'json'), + new ArrayExpression([['1', 2], [3,4,5]], 'json'), + ], + ]], + 'arrays packed in classes' => [[ + 'intarray_col' => [ + new ArrayExpression([1,-2,null,'42'], 'int', 1), + new ArrayExpression([1,-2,null,42], 'int4', 1), + ], + 'textarray2_col' => [ + new ArrayExpression([['text'], [null], [1]], 'text', 2), + new ArrayExpression([['text'], [null], ['1']], 'text', 2), + ], + 'json_col' => [ + new JsonExpression(['a' => 1, 'b' => null, 'c' => [1,3,5]]), + ['a' => 1, 'b' => null, 'c' => [1,3,5]], + ], + 'jsonb_col' => [ + new JsonExpression([null, 'a', 'b', '\"', '{"af"}']), + [null, 'a', 'b', '\"', '{"af"}'], + ], + 'jsonarray_col' => [ + new Expression("array['[\",\",\"null\",true,\"false\",\"f\"]'::json]::json[]"), + new ArrayExpression([[',', 'null', true, 'false', 'f']], 'json'), + ], + ]], + 'scalars' => [[ + 'json_col' => [ + '5.8', + ], + 'jsonb_col' => [ + M_PI, + ], + ]], + ]; + } + + /** + * @dataProvider arrayValuesProvider + */ + public function testArrayValues($attributes): void + { + $this->checkFixture($this->db, 'array_and_json_types', true); + + $type = new ArrayAndJsonTypes($this->db); + + foreach ($attributes as $attribute => $expected) { + $type->$attribute = $expected[0]; + } + + $type->save(); + + $typeQuery = new ActiveQuery($type::class, $this->db); + + $type = $typeQuery->onePopulate(); + + foreach ($attributes as $attribute => $expected) { + $expected = $expected[1] ?? $expected[0]; + $value = $type->$attribute; + + if ($expected instanceof ArrayExpression) { + $expected = $expected->getValue(); + } + + $this->assertEquals($expected, $value, 'In column ' . $attribute); + + if ($value instanceof ArrayExpression) { + $this->assertInstanceOf(ArrayAccess::class, $value); + $this->assertInstanceOf(Traversable::class, $value); + /** testing arrayaccess */ + foreach ($type->$attribute as $key => $v) { + $this->assertSame($expected[$key], $value[$key]); + } + } + } + + /** Testing update */ + foreach ($attributes as $attribute => $expected) { + $type->markAttributeDirty($attribute); + } + + $this->assertSame(1, $type->update(), 'The record got updated'); + } + + public function testToArray(): void + { + $this->checkFixture($this->db, 'customer', true); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + $customer = $customerQuery->findOne(1); + + $this->assertSame( + [ + 'id' => 1, + 'email' => 'user1@example.com', + 'name' => 'user1', + 'address' => 'address1', + 'status' => 1, + 'bool_status' => true, + 'profile_id' => 1, + ], + $customer->toArray(), + ); + } + + public function testToArrayWithClosure(): void + { + $this->checkFixture($this->db, 'customer', true); + + $customerQuery = new ActiveQuery(CustomerClosureField::class, $this->db); + $customer = $customerQuery->findOne(1); + + $this->assertSame( + [ + 'id' => 1, + 'email' => 'user1@example.com', + 'name' => 'user1', + 'address' => 'address1', + 'status' => 'active', + 'bool_status' => true, + 'profile_id' => 1, + ], + $customer->toArray(), + ); + } +} diff --git a/tests/Driver/Pgsql/Stubs/Type.php b/tests/Driver/Pgsql/Stubs/Type.php new file mode 100644 index 000000000..d0a8047b4 --- /dev/null +++ b/tests/Driver/Pgsql/Stubs/Type.php @@ -0,0 +1,17 @@ +db); - $customer->id = 1337; - $customer->email = 'user1337@example.com'; - $customer->name = 'user1337'; - $customer->address = 'address1337'; + $customer->setId(1337); + $customer->setEmail('user1337@example.com'); + $customer->setName('user1337'); + $customer->setAddress('address1337'); - $this->assertTrue($customer->isNewRecord); + $this->assertTrue($customer->getIsNewRecord()); $customer->save(); - $this->assertEquals(1337, $customer->id); - $this->assertFalse($customer->isNewRecord); + $this->assertEquals(1337, $customer->getId()); + $this->assertFalse($customer->getIsNewRecord()); } /** @@ -63,9 +63,9 @@ public function testEagerLoadingUsingStringIdentifiers(): void /** @var Beta[] $betas */ foreach ($betas as $beta) { - $this->assertNotNull($beta->alpha); - $this->assertEquals($beta->alpha_string_identifier, $beta->alpha->string_identifier); - $alphaIdentifiers[] = $beta->alpha->string_identifier; + $this->assertNotNull($beta->getAlpha()); + $this->assertEquals($beta->getAlphaStringIdentifier(), $beta->getAlpha()->getStringIdentifier()); + $alphaIdentifiers[] = $beta->getAlpha()->getStringIdentifier(); } $this->assertEquals(['1', '01', '001', '001', '2', '2b', '2b', '02'], $alphaIdentifiers); diff --git a/tests/Driver/Sqlite/MagicActiveRecordTest.php b/tests/Driver/Sqlite/MagicActiveRecordTest.php new file mode 100644 index 000000000..fa8dabbd7 --- /dev/null +++ b/tests/Driver/Sqlite/MagicActiveRecordTest.php @@ -0,0 +1,73 @@ +db = $sqliteHelper->createConnection(); + } + + protected function tearDown(): void + { + parent::tearDown(); + + $this->db->close(); + + unset($this->db); + } + + public function testExplicitPkOnAutoIncrement(): void + { + $this->checkFixture($this->db, 'customer', true); + + $customer = new Customer($this->db); + + $customer->id = 1337; + $customer->email = 'user1337@example.com'; + $customer->name = 'user1337'; + $customer->address = 'address1337'; + + $this->assertTrue($customer->isNewRecord); + $customer->save(); + + $this->assertEquals(1337, $customer->id); + $this->assertFalse($customer->isNewRecord); + } + + /** + * {@see https://github.com/yiisoft/yii2/issues/15482} + */ + public function testEagerLoadingUsingStringIdentifiers(): void + { + $this->checkFixture($this->db, 'beta'); + + $betaQuery = new ActiveQuery(Beta::class, $this->db); + + $betas = $betaQuery->with('alpha')->all(); + + $this->assertNotEmpty($betas); + + $alphaIdentifiers = []; + + /** @var Beta[] $betas */ + foreach ($betas as $beta) { + $this->assertNotNull($beta->alpha); + $this->assertEquals($beta->alpha_string_identifier, $beta->alpha->string_identifier); + $alphaIdentifiers[] = $beta->alpha->string_identifier; + } + + $this->assertEquals(['1', '01', '001', '001', '2', '2b', '2b', '02'], $alphaIdentifiers); + } +} diff --git a/tests/MagicActiveRecordTest.php b/tests/MagicActiveRecordTest.php new file mode 100644 index 000000000..a01177190 --- /dev/null +++ b/tests/MagicActiveRecordTest.php @@ -0,0 +1,951 @@ +checkFixture($this->db, 'null_values', true); + + $record = new NullValues($this->db); + + $this->assertNull($record->getAttribute('var1')); + $this->assertNull($record->getAttribute('var2')); + $this->assertNull($record->getAttribute('var3')); + $this->assertNull($record->getAttribute('stringcol')); + + $record->setAttribute('var1', 123); + $record->setAttribute('var2', 456); + $record->setAttribute('var3', 789); + $record->setAttribute('stringcol', 'hello!'); + $record->save(); + + $this->assertTrue($record->refresh()); + $this->assertEquals(123, $record->getAttribute('var1')); + $this->assertEquals(456, $record->getAttribute('var2')); + $this->assertEquals(789, $record->getAttribute('var3')); + $this->assertEquals('hello!', $record->getAttribute('stringcol')); + + $record->setAttribute('var1', null); + $record->setAttribute('var2', null); + $record->setAttribute('var3', null); + $record->setAttribute('stringcol', null); + $record->save(); + + $this->assertTrue($record->refresh()); + $this->assertNull($record->getAttribute('var1')); + $this->assertNull($record->getAttribute('var2')); + $this->assertNull($record->getAttribute('var3')); + $this->assertNull($record->getAttribute('>stringcol')); + + $record->setAttribute('var1', 0); + $record->setAttribute('var2', 0); + $record->setAttribute('var3', 0); + $record->setAttribute('stringcol', ''); + $record->save(); + + $this->assertTrue($record->refresh()); + $this->assertEquals(0, $record->getAttribute('var1')); + $this->assertEquals(0, $record->getAttribute('var2')); + $this->assertEquals(0, $record->getAttribute('var3')); + $this->assertEquals('', $record->getAttribute('stringcol')); + } + + public function testStoreEmpty(): void + { + $this->checkFixture($this->db, 'null_values'); + + $record = new NullValues($this->db); + + /** this is to simulate empty html form submission */ + $record->var1 = ''; + $record->var2 = ''; + $record->var3 = ''; + $record->stringcol = ''; + $record->save(); + + $this->assertTrue($record->refresh()); + + /** {@see https://github.com/yiisoft/yii2/commit/34945b0b69011bc7cab684c7f7095d837892a0d4#commitcomment-4458225} */ + $this->assertSame($record->var1, $record->var2); + $this->assertSame($record->var2, $record->var3); + } + + public function testIsPrimaryKey(): void + { + $this->checkFixture($this->db, 'customer'); + + $customer = new Customer($this->db); + $orderItem = new OrderItem($this->db); + + $this->assertTrue($customer->isPrimaryKey(['id'])); + $this->assertFalse($customer->isPrimaryKey([])); + $this->assertFalse($customer->isPrimaryKey(['id', 'name'])); + $this->assertFalse($customer->isPrimaryKey(['name'])); + $this->assertFalse($customer->isPrimaryKey(['name', 'email'])); + + $this->assertTrue($orderItem->isPrimaryKey(['order_id', 'item_id'])); + $this->assertFalse($orderItem->isPrimaryKey([])); + $this->assertFalse($orderItem->isPrimaryKey(['order_id'])); + $this->assertFalse($orderItem->isPrimaryKey(['item_id'])); + $this->assertFalse($orderItem->isPrimaryKey(['quantity'])); + $this->assertFalse($orderItem->isPrimaryKey(['quantity', 'subtotal'])); + $this->assertFalse($orderItem->isPrimaryKey(['order_id', 'item_id', 'quantity'])); + } + + public function testOutdatedRelationsAreResetForNewRecords(): void + { + $this->checkFixture($this->db, 'order_item'); + + $orderItem = new OrderItem($this->db); + + $orderItem->order_id = 1; + $orderItem->item_id = 3; + $this->assertEquals(1, $orderItem->order->id); + $this->assertEquals(3, $orderItem->item->id); + + /** test `__set()`. */ + $orderItem->order_id = 2; + $orderItem->item_id = 1; + $this->assertEquals(2, $orderItem->order->id); + $this->assertEquals(1, $orderItem->item->id); + + /** test `setAttribute()`. */ + $orderItem->setAttribute('order_id', 2); + $orderItem->setAttribute('item_id', 2); + $this->assertEquals(2, $orderItem->order->id); + $this->assertEquals(2, $orderItem->item->id); + } + + public function testDefaultValues(): void + { + $this->checkFixture($this->db, 'type'); + + $arClass = new Type($this->db); + + $arClass->loadDefaultValues(); + + $this->assertEquals(1, $arClass->int_col2); + $this->assertEquals('something', $arClass->char_col2); + $this->assertEquals(1.23, $arClass->float_col2); + $this->assertEquals(33.22, $arClass->numeric_col); + $this->assertEquals(true, $arClass->bool_col2); + $this->assertEquals('2002-01-01 00:00:00', $arClass->time); + + $arClass = new Type($this->db); + $arClass->char_col2 = 'not something'; + + $arClass->loadDefaultValues(); + $this->assertEquals('not something', $arClass->char_col2); + + $arClass = new Type($this->db); + $arClass->char_col2 = 'not something'; + + $arClass->loadDefaultValues(false); + $this->assertEquals('something', $arClass->char_col2); + } + + public function testCastValues(): void + { + $this->checkFixture($this->db, 'type'); + + $arClass = new Type($this->db); + + $arClass->int_col = 123; + $arClass->int_col2 = 456; + $arClass->smallint_col = 42; + $arClass->char_col = '1337'; + $arClass->char_col2 = 'test'; + $arClass->char_col3 = 'test123'; + $arClass->float_col = 3.742; + $arClass->float_col2 = 42.1337; + $arClass->bool_col = true; + $arClass->bool_col2 = false; + + $arClass->save(); + + /** @var $model Type */ + $aqClass = new ActiveQuery(Type::class, $this->db); + $query = $aqClass->onePopulate(); + + $this->assertSame(123, $query->int_col); + $this->assertSame(456, $query->int_col2); + $this->assertSame(42, $query->smallint_col); + $this->assertSame('1337', trim($query->char_col)); + $this->assertSame('test', $query->char_col2); + $this->assertSame('test123', $query->char_col3); + } + + public function testPopulateRecordCallWhenQueryingOnParentClass(): void + { + $this->checkFixture($this->db, 'cat'); + + $cat = new Cat($this->db); + $cat->save(); + + $dog = new Dog($this->db); + $dog->save(); + + $animal = new ActiveQuery(Animal::class, $this->db); + + $animals = $animal->where(['type' => Dog::class])->onePopulate(); + $this->assertEquals('bark', $animals->getDoes()); + + $animals = $animal->where(['type' => Cat::class])->onePopulate(); + $this->assertEquals('meow', $animals->getDoes()); + } + + public function testSaveEmpty(): void + { + $this->checkFixture($this->db, 'null_values', true); + + $record = new NullValues($this->db); + + $this->assertTrue($record->save()); + $this->assertEquals(1, $record->id); + } + + /** + * Verify that {{}} are not going to be replaced in parameters. + */ + public function testNoTablenameReplacement(): void + { + $this->checkFixture($this->db, 'customer'); + + $customer = new Customer($this->db); + + $customer->name = 'Some {{weird}} name'; + $customer->email = 'test@example.com'; + $customer->address = 'Some {{%weird}} address'; + $customer->insert(); + $customer->refresh(); + + $this->assertEquals('Some {{weird}} name', $customer->name); + $this->assertEquals('Some {{%weird}} address', $customer->address); + + $customer->name = 'Some {{updated}} name'; + $customer->address = 'Some {{%updated}} address'; + $customer->update(); + + $this->assertEquals('Some {{updated}} name', $customer->name); + $this->assertEquals('Some {{%updated}} address', $customer->address); + } + + public static function legalValuesForFindByCondition(): array + { + return [ + [Customer::class, ['id' => 1]], + [Customer::class, ['customer.id' => 1]], + [Customer::class, ['[[id]]' => 1]], + [Customer::class, ['{{customer}}.[[id]]' => 1]], + [Customer::class, ['{{%customer}}.[[id]]' => 1]], + [CustomerWithAlias::class, ['id' => 1]], + [CustomerWithAlias::class, ['customer.id' => 1]], + [CustomerWithAlias::class, ['[[id]]' => 1]], + [CustomerWithAlias::class, ['{{customer}}.[[id]]' => 1]], + [CustomerWithAlias::class, ['{{%customer}}.[[id]]' => 1]], + [CustomerWithAlias::class, ['csr.id' => 1], 'csr'], + [CustomerWithAlias::class, ['{{csr}}.[[id]]' => 1], 'csr'], + ]; + } + + /** + * @dataProvider legalValuesForFindByCondition + * + * @throws ReflectionException + */ + public function testLegalValuesForFindByCondition( + string $modelClassName, + array $validFilter, + ?string $alias = null + ): void { + $this->checkFixture($this->db, 'customer'); + + $activeQuery = new ActiveQuery($modelClassName, $this->db); + + if ($alias !== null) { + $activeQuery->alias('csr'); + } + + /** @var Query $query */ + $query = Assert::invokeMethod($activeQuery, 'findByCondition', [$validFilter]); + + + $this->db->getQueryBuilder()->build($query); + + $this->assertTrue(true); + } + + public static function illegalValuesForFindByCondition(): array + { + return [ + [Customer::class, [['`id`=`id` and 1' => 1]]], + [Customer::class, [[ + 'legal' => 1, + '`id`=`id` and 1' => 1, + ]]], + [Customer::class, [[ + 'nested_illegal' => [ + 'false or 1=' => 1, + ], + ]]], + [Customer::class, [['true--' => 1]]], + + [CustomerWithAlias::class, [['`csr`.`id`=`csr`.`id` and 1' => 1]]], + [CustomerWithAlias::class, [[ + 'legal' => 1, + '`csr`.`id`=`csr`.`id` and 1' => 1, + ]]], + [CustomerWithAlias::class, [[ + 'nested_illegal' => [ + 'false or 1=' => 1, + ], + ]]], + [CustomerWithAlias::class, [['true--' => 1]]], + ]; + } + + /** + * @dataProvider illegalValuesForFindByCondition + * + * @throws ReflectionException + */ + public function testValueEscapingInFindByCondition(string $modelClassName, array $filterWithInjection): void + { + $this->checkFixture($this->db, 'customer'); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageMatches( + '/^Key "(.+)?" is not a column name and can not be used as a filter$/' + ); + + $query = new ActiveQuery($modelClassName, $this->db); + + /** @var Query $query */ + $query = Assert::invokeMethod($query, 'findByCondition', $filterWithInjection); + + $this->db->getQueryBuilder()->build($query); + } + + public function testRefreshQuerySetAliasFindRecord(): void + { + $this->checkFixture($this->db, 'customer'); + + $customer = new CustomerWithAlias($this->db); + + $customer->id = 1; + $customer->refresh(); + + $this->assertEquals(1, $customer->id); + } + + public function testResetNotSavedRelation(): void + { + $this->checkFixture($this->db, 'order'); + + $order = new Order($this->db); + + $order->customer_id = 1; + $order->created_at = 1_325_502_201; + $order->total = 0; + + $orderItem = new OrderItem($this->db); + + $order->orderItems; + + $order->populateRelation('orderItems', [$orderItem]); + + $order->save(); + + $this->assertCount(1, $order->orderItems); + } + + public function testIssetException(): void + { + $this->checkFixture($this->db, 'cat'); + + $cat = new Cat($this->db); + + $this->expectException(Exception::class); + isset($cat->exception); + } + + public function testIssetThrowable(): void + { + $this->checkFixture($this->db, 'cat'); + + $cat = new Cat($this->db); + + $this->expectException(DivisionByZeroError::class); + isset($cat->throwable); + } + + public function testIssetNonExisting(): void + { + $this->checkFixture($this->db, 'cat'); + + $cat = new Cat($this->db); + + $this->assertFalse(isset($cat->non_existing)); + $this->assertFalse(isset($cat->non_existing_property)); + } + + public function testSetAttributes(): void + { + $attributes = []; + $this->checkFixture($this->db, 'customer'); + + $attributes['email'] = 'samdark@mail.ru'; + $attributes['name'] = 'samdark'; + $attributes['address'] = 'rusia'; + $attributes['status'] = 1; + + if ($this->db->getDriverName() === 'pgsql') { + $attributes['bool_status'] = true; + } + + $attributes['profile_id'] = null; + + $customer = new Customer($this->db); + + $customer->setAttributes($attributes); + + $this->assertTrue($customer->save()); + } + + public function testSetAttributeNoExist(): void + { + $this->checkFixture($this->db, 'cat'); + + $cat = new Cat($this->db); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + 'Yiisoft\ActiveRecord\Tests\Stubs\MagicActiveRecord\Cat has no attribute named "noExist"' + ); + + $cat->setAttribute('noExist', 1); + } + + public function testSetOldAttribute(): void + { + $this->checkFixture($this->db, 'customer'); + + $customer = new Customer($this->db); + + $this->assertEmpty($customer->getOldAttribute('name')); + + $customer->setOldAttribute('name', 'samdark'); + + $this->assertEquals('samdark', $customer->getOldAttribute('name')); + } + + public function testSetOldAttributeException(): void + { + $this->checkFixture($this->db, 'customer'); + + $customer = new Customer($this->db); + + $this->assertEmpty($customer->getOldAttribute('name')); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + 'Yiisoft\ActiveRecord\Tests\Stubs\MagicActiveRecord\Customer has no attribute named "noExist"' + ); + $customer->setOldAttribute('noExist', 'samdark'); + } + + public function testIsAttributeChangedNotChanged(): void + { + $this->checkFixture($this->db, 'customer'); + + $customer = new Customer($this->db); + + $this->assertEmpty($customer->getAttribute('name')); + $this->assertEmpty($customer->getOldAttribute('name')); + $this->assertFalse($customer->isAttributeChanged('name', false)); + } + + public function testTableSchemaException(): void + { + $noExist = new NoExist($this->db); + + $this->expectException(InvalidConfigException::class); + $this->expectExceptionMessage('The table does not exist: NoExist'); + $noExist->getTableSchema(); + } + + public function testInsert(): void + { + $this->checkFixture($this->db, 'customer'); + + $customer = new Customer($this->db); + + $customer->email = 'user4@example.com'; + $customer->name = 'user4'; + $customer->address = 'address4'; + + $this->assertNull($customer->id); + $this->assertTrue($customer->isNewRecord); + + $customer->save(); + + $this->assertNotNull($customer->id); + $this->assertFalse($customer->isNewRecord); + } + + /** + * Some PDO implementations (e.g. cubrid) do not support boolean values. + * + * Make sure this does not affect AR layer. + */ + public function testBooleanAttribute(): void + { + $this->checkFixture($this->db, 'customer', true); + + $customer = new Customer($this->db); + + $customer->name = 'boolean customer'; + $customer->email = 'mail@example.com'; + $customer->status = true; + + $customer->save(); + $customer->refresh(); + $this->assertEquals(1, $customer->status); + + $customer->status = false; + $customer->save(); + + $customer->refresh(); + $this->assertEquals(0, $customer->status); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + $customers = $customerQuery->where(['status' => true])->all(); + $this->assertCount(2, $customers); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + $customers = $customerQuery->where(['status' => false])->all(); + $this->assertCount(1, $customers); + } + + public function testAttributeAccess(): void + { + $this->checkFixture($this->db, 'customer'); + + $arClass = new Customer($this->db); + + $this->assertTrue($arClass->canSetProperty('name')); + $this->assertTrue($arClass->canGetProperty('name')); + $this->assertFalse($arClass->canSetProperty('unExistingColumn')); + $this->assertFalse(isset($arClass->name)); + + $arClass->name = 'foo'; + $this->assertTrue(isset($arClass->name)); + + unset($arClass->name); + $this->assertNull($arClass->name); + + /** {@see https://github.com/yiisoft/yii2-gii/issues/190} */ + $baseModel = new Customer($this->db); + $this->assertFalse($baseModel->hasProperty('unExistingColumn')); + + $customer = new Customer($this->db); + $this->assertInstanceOf(Customer::class, $customer); + $this->assertTrue($customer->canGetProperty('id')); + $this->assertTrue($customer->canSetProperty('id')); + + /** tests that we really can get and set this property */ + $this->assertNull($customer->id); + $customer->id = 10; + $this->assertNotNull($customer->id); + + /** Let's test relations */ + $this->assertTrue($customer->canGetProperty('orderItems')); + $this->assertFalse($customer->canSetProperty('orderItems')); + + /** Newly created model must have empty relation */ + $this->assertSame([], $customer->orderItems); + + /** does it still work after accessing the relation? */ + $this->assertTrue($customer->canGetProperty('orderItems')); + $this->assertFalse($customer->canSetProperty('orderItems')); + + $this->expectException(InvalidCallException::class); + $this->expectExceptionMessage('Setting read-only property: ' . Customer::class . '::orderItems'); + $customer->orderItems = [new Item($this->db)]; + + /** related attribute $customer->orderItems didn't change cause it's read-only */ + $this->assertSame([], $customer->orderItems); + $this->assertFalse($customer->canGetProperty('non_existing_property')); + $this->assertFalse($customer->canSetProperty('non_existing_property')); + + $this->expectException(UnknownPropertyException::class); + $this->expectExceptionMessage('Setting unknown property: ' . Customer::class . '::non_existing_property'); + $customer->non_existing_property = null; + } + + public function testHasAttribute(): void + { + $this->checkFixture($this->db, 'customer'); + + $customer = new Customer($this->db); + + $this->assertTrue($customer->hasAttribute('id')); + $this->assertTrue($customer->hasAttribute('email')); + $this->assertFalse($customer->hasAttribute('notExist')); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + $customer = $customerQuery->findOne(1); + $this->assertTrue($customer->hasAttribute('id')); + $this->assertTrue($customer->hasAttribute('email')); + $this->assertFalse($customer->hasAttribute('notExist')); + } + + public function testRefresh(): void + { + $this->checkFixture($this->db, 'customer'); + + $customer = new Customer($this->db); + + $this->assertFalse($customer->refresh()); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + $customer = $customerQuery->findOne(1); + $customer->name = 'to be refreshed'; + + $this->assertTrue($customer->refresh()); + $this->assertEquals('user1', $customer->name); + } + + public function testEquals(): void + { + $this->checkFixture($this->db, 'customer'); + + $customerA = new Customer($this->db); + $customerB = new Customer($this->db); + $this->assertFalse($customerA->equals($customerB)); + + $customerA = new Customer($this->db); + $customerB = new Item($this->db); + $this->assertFalse($customerA->equals($customerB)); + } + + public static function providerForUnlinkDelete() + { + return [ + 'with delete' => [true, 0], + 'without delete' => [false, 1], + ]; + } + + /** + * @dataProvider providerForUnlinkDelete + * + * @see https://github.com/yiisoft/yii2/issues/17174 + */ + public function testUnlinkWithViaOnCondition($delete, $count) + { + $this->checkFixture($this->db, 'order', true); + $this->checkFixture($this->db, 'order_item_with_null_fk', true); + + $orderQuery = new ActiveQuery(Order::class, $this->db); + $order = $orderQuery->findOne(2); + + $this->assertCount(1, $order->itemsFor8); + $order->unlink('itemsFor8', $order->itemsFor8[0], $delete); + + $order = $orderQuery->findOne(2); + $this->assertCount(0, $order->itemsFor8); + $this->assertCount(2, $order->orderItemsWithNullFK); + + $orderItemQuery = new ActiveQuery(OrderItemWithNullFK::class, $this->db); + $this->assertCount(1, $orderItemQuery->findAll([ + 'order_id' => 2, + 'item_id' => 5, + ])); + $this->assertCount($count, $orderItemQuery->findAll([ + 'order_id' => null, + 'item_id' => null, + ])); + } + + public function testVirtualRelation() + { + $this->checkFixture($this->db, 'order', true); + + $orderQuery = new ActiveQuery(Order::class, $this->db); + /** @var Order $order */ + $order = $orderQuery->findOne(2); + + $order->setVirtualCustomerId($order->customer_id); + $this->assertNotNull($order->getVirtualCustomerQuery()); + } + + /** + * Test joinWith eager loads via relation + * + * @see https://github.com/yiisoft/yii2/issues/19507 + */ + public function testJoinWithEager() + { + $this->checkFixture($this->db, 'customer', true); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + $eagerCustomers = $customerQuery->joinWith(['items2'])->all(); + $eagerItemsCount = 0; + foreach ($eagerCustomers as $customer) { + $eagerItemsCount += is_countable($customer->items2) ? count($customer->items2) : 0; + } + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + $lazyCustomers = $customerQuery->all(); + $lazyItemsCount = 0; + foreach ($lazyCustomers as $customer) { + $lazyItemsCount += is_countable($customer->items2) ? count($customer->items2) : 0; + } + + $this->assertEquals($eagerItemsCount, $lazyItemsCount); + } + + public function testToArray(): void + { + $this->checkFixture($this->db, 'customer', true); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + $customer = $customerQuery->findOne(1); + + $this->assertSame( + [ + 'id' => 1, + 'email' => 'user1@example.com', + 'name' => 'user1', + 'address' => 'address1', + 'status' => 1, + 'bool_status' => true, + 'profile_id' => 1, + ], + $customer->toArray(), + ); + } + + public function testToArrayWithClosure(): void + { + $this->checkFixture($this->db, 'customer', true); + + $customerQuery = new ActiveQuery(CustomerClosureField::class, $this->db); + $customer = $customerQuery->findOne(1); + + $this->assertSame( + [ + 'id' => 1, + 'email' => 'user1@example.com', + 'name' => 'user1', + 'address' => 'address1', + 'status' => 'active', + 'bool_status' => true, + 'profile_id' => 1, + ], + $customer->toArray(), + ); + } + + public function testToArrayForArrayable(): void + { + $this->checkFixture($this->db, 'customer', true); + + $customerQuery = new ActiveQuery(CustomerForArrayable::class, $this->db); + + /** @var CustomerForArrayable $customer */ + $customer = $customerQuery->findOne(1); + /** @var CustomerForArrayable $customer2 */ + $customer2 = $customerQuery->findOne(2); + /** @var CustomerForArrayable $customer3 */ + $customer3 = $customerQuery->findOne(3); + + $customer->setItem($customer2); + $customer->setItems($customer3); + + $this->assertSame( + [ + 'id' => 1, + 'email' => 'user1@example.com', + 'name' => 'user1', + 'address' => 'address1', + 'status' => 'active', + 'item' => [ + 'id' => 2, + 'email' => 'user2@example.com', + 'name' => 'user2', + 'status' => 'active', + ], + 'items' => [ + [ + 'id' => 3, + 'email' => 'user3@example.com', + 'name' => 'user3', + 'status' => 'inactive', + ], + ], + ], + $customer->toArray([ + 'id', + 'name', + 'email', + 'address', + 'status', + 'item.id', + 'item.name', + 'item.email', + 'items.0.id', + 'items.0.name', + 'items.0.email', + ]), + ); + } + + public function testSaveWithoutChanges(): void + { + $this->checkFixture($this->db, 'customer'); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + + $customer = $customerQuery->findOne(1); + + $this->assertTrue($customer->save()); + } + + public function testGetPrimaryKey(): void + { + $this->checkFixture($this->db, 'customer'); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + + $customer = $customerQuery->findOne(1); + + $this->assertSame(1, $customer->getPrimaryKey()); + $this->assertSame(['id' => 1], $customer->getPrimaryKey(true)); + } + + public function testGetOldPrimaryKey(): void + { + $this->checkFixture($this->db, 'customer'); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + + $customer = $customerQuery->findOne(1); + $customer->id = 2; + + $this->assertSame(1, $customer->getOldPrimaryKey()); + $this->assertSame(['id' => 1], $customer->getOldPrimaryKey(true)); + } + + public function testGetDirtyAttributesOnNewRecord(): void + { + $this->checkFixture($this->db, 'customer'); + + $customer = new Customer($this->db); + + $this->assertSame([], $customer->getDirtyAttributes()); + + $customer->setAttribute('name', 'Adam'); + $customer->setAttribute('email', 'adam@example.com'); + $customer->setAttribute('address', null); + + $this->assertEquals( + ['name' => 'Adam', 'email' => 'adam@example.com', 'address' => null], + $customer->getDirtyAttributes() + ); + $this->assertEquals( + ['email' => 'adam@example.com', 'address' => null], + $customer->getDirtyAttributes(['id', 'email', 'address', 'status', 'unknown']), + ); + + $this->assertTrue($customer->save()); + $this->assertSame([], $customer->getDirtyAttributes()); + + $customer->setAttribute('address', ''); + + $this->assertSame(['address' => ''], $customer->getDirtyAttributes()); + } + + public function testGetDirtyAttributesAfterFind(): void + { + $this->checkFixture($this->db, 'customer'); + + $customerQuery = new ActiveQuery(Customer::class, $this->db); + $customer = $customerQuery->findOne(1); + + $this->assertSame([], $customer->getDirtyAttributes()); + + $customer->setAttribute('name', 'Adam'); + $customer->setAttribute('email', 'adam@example.com'); + $customer->setAttribute('address', null); + + $this->assertEquals( + ['name' => 'Adam', 'email' => 'adam@example.com', 'address' => null], + $customer->getDirtyAttributes(), + ); + $this->assertEquals( + ['email' => 'adam@example.com', 'address' => null], + $customer->getDirtyAttributes(['id', 'email', 'address', 'status', 'unknown']), + ); + } + + public function testGetDirtyAttributesWithProperties(): void + { + $this->checkFixture($this->db, 'customer'); + + $customer = new CustomerWithProperties($this->db); + $this->assertSame([ + 'name' => null, + 'address' => null, + ], $customer->getDirtyAttributes()); + + $customerQuery = new ActiveQuery(CustomerWithProperties::class, $this->db); + $customer = $customerQuery->findOne(1); + + $this->assertSame([], $customer->getDirtyAttributes()); + + $customer->setEmail('adam@example.com'); + $customer->setName('Adam'); + $customer->setAddress(null); + $customer->setStatus(null); + + $this->assertEquals( + ['email' => 'adam@example.com', 'name' => 'Adam', 'address' => null, 'status' => null], + $customer->getDirtyAttributes(), + ); + $this->assertEquals( + ['email' => 'adam@example.com', 'address' => null], + $customer->getDirtyAttributes(['id', 'email', 'address', 'unknown']), + ); + } +} diff --git a/tests/Stubs/ActiveRecord.php b/tests/Stubs/ActiveRecord.php new file mode 100644 index 000000000..8c3b80568 --- /dev/null +++ b/tests/Stubs/ActiveRecord.php @@ -0,0 +1,39 @@ + + * @template-implements IteratorAggregate + * + * @see ActiveRecord for more information. + */ +class ActiveRecord extends \Yiisoft\ActiveRecord\ActiveRecord implements + ArrayableInterface, + ArrayAccess, + IteratorAggregate, + TransactionalInterface +{ + use ArrayableTrait; + use ArrayAccessTrait; + use ArrayIteratorTrait; + use TransactionalTrait; +} diff --git a/tests/Stubs/ActiveRecord/Alpha.php b/tests/Stubs/ActiveRecord/Alpha.php index 16f89e409..f60abab51 100644 --- a/tests/Stubs/ActiveRecord/Alpha.php +++ b/tests/Stubs/ActiveRecord/Alpha.php @@ -5,21 +5,45 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\ActiveRecord\ActiveQuery; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\ActiveQueryInterface; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -/** - * @property int $id - * @property string $string_identifier - */ final class Alpha extends ActiveRecord { public const TABLE_NAME = 'alpha'; + protected int $id; + + protected string $string_identifier; + public function getTableName(): string { return self::TABLE_NAME; } + public function getId(): int + { + return $this->id; + } + + public function getStringIdentifier(): string + { + return $this->string_identifier; + } + + public function relationQuery(string $name): ActiveQueryInterface + { + return match ($name) { + 'betas' => $this->getBetasQuery(), + default => parent::relationQuery($name), + }; + } + + public function getBetas(): array|null + { + return $this->relation('betas'); + } + public function getBetasQuery(): ActiveQuery { return $this->hasMany(Beta::class, ['alpha_string_identifier' => 'string_identifier']); diff --git a/tests/Stubs/ActiveRecord/Animal.php b/tests/Stubs/ActiveRecord/Animal.php index 35a68679e..d430c8924 100644 --- a/tests/Stubs/ActiveRecord/Animal.php +++ b/tests/Stubs/ActiveRecord/Animal.php @@ -4,20 +4,20 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\ActiveRecord\ActiveRecordInterface; use Yiisoft\Db\Connection\ConnectionInterface; /** * Class Animal. - * - * @property int $id - * @property string $type */ class Animal extends ActiveRecord { private string $does; + protected int $id; + protected string $type; + public function getTableName(): string { return 'animal'; diff --git a/tests/Stubs/ActiveRecord/ArrayAndJsonTypes.php b/tests/Stubs/ActiveRecord/ArrayAndJsonTypes.php index 686fa3794..7cdcfc7ea 100644 --- a/tests/Stubs/ActiveRecord/ArrayAndJsonTypes.php +++ b/tests/Stubs/ActiveRecord/ArrayAndJsonTypes.php @@ -4,8 +4,17 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; +use Yiisoft\Db\Expression\ArrayExpression; +use Yiisoft\Db\Expression\Expression; +use Yiisoft\Db\Expression\JsonExpression; final class ArrayAndJsonTypes extends ActiveRecord { + public int $id; + public array|ArrayExpression|null $intarray_col = null; + public array|ArrayExpression|null $textarray2_col = null; + public array|float|int|string|JsonExpression|null $json_col = null; + public array|float|int|string|JsonExpression|null $jsonb_col = null; + public array|ArrayExpression|Expression|null $jsonarray_col = null; } diff --git a/tests/Stubs/ActiveRecord/Beta.php b/tests/Stubs/ActiveRecord/Beta.php index 1bed7a3fd..9449aaaf0 100644 --- a/tests/Stubs/ActiveRecord/Beta.php +++ b/tests/Stubs/ActiveRecord/Beta.php @@ -5,20 +5,43 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\ActiveRecord\ActiveQuery; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\ActiveQueryInterface; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -/** - * @property int $id - * @property string $alpha_string_identifier - * @property Alpha $alpha - */ final class Beta extends ActiveRecord { + protected int $id; + protected string $alpha_string_identifier; + protected Alpha $alpha; + public function getTableName(): string { return 'beta'; } + public function getId(): int + { + return $this->id; + } + + public function getAlphaStringIdentifier(): string + { + return $this->alpha_string_identifier; + } + + public function relationQuery(string $name): ActiveQueryInterface + { + return match ($name) { + 'alpha' => $this->getAlphaQuery(), + default => parent::relationQuery($name), + }; + } + + public function getAlpha(): Alpha|null + { + return $this->relation('alpha'); + } + public function getAlphaQuery(): ActiveQuery { return $this->hasOne(Alpha::class, ['string_identifier' => 'alpha_string_identifier']); diff --git a/tests/Stubs/ActiveRecord/BitValues.php b/tests/Stubs/ActiveRecord/BitValues.php index 2c5a0a8c4..53758395f 100644 --- a/tests/Stubs/ActiveRecord/BitValues.php +++ b/tests/Stubs/ActiveRecord/BitValues.php @@ -4,14 +4,13 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * {@see https://github.com/yiisoft/yii2/issues/9006} - * - * @property int $id - * @property int $val */ final class BitValues extends ActiveRecord { + public int $id; + public bool|int $val; } diff --git a/tests/Stubs/ActiveRecord/BoolAR.php b/tests/Stubs/ActiveRecord/BoolAR.php index b1d612437..3f11de60d 100644 --- a/tests/Stubs/ActiveRecord/BoolAR.php +++ b/tests/Stubs/ActiveRecord/BoolAR.php @@ -4,10 +4,15 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; final class BoolAR extends ActiveRecord { + public int $id; + public bool|null $bool_col = null; + public bool $default_true = true; + public bool $default_false = false; + public function getTableName(): string { return 'bool_values'; diff --git a/tests/Stubs/ActiveRecord/Category.php b/tests/Stubs/ActiveRecord/Category.php index 5b6368d11..ceba7ac3e 100644 --- a/tests/Stubs/ActiveRecord/Category.php +++ b/tests/Stubs/ActiveRecord/Category.php @@ -5,36 +5,83 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\ActiveRecord\ActiveQuery; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\ActiveQueryInterface; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class Category. - * - * @property int $id - * @property string $name */ final class Category extends ActiveRecord { + protected int|null $id; + protected string $name; + public function getTableName(): string { return 'category'; } + public function relationQuery(string $name): ActiveQueryInterface + { + return match ($name) { + 'items' => $this->getItemsQuery(), + 'limitedItems' => $this->getLimitedItemsQuery(), + 'orderItems' => $this->getOrderItemsQuery(), + 'orders' => $this->getOrdersQuery(), + default => parent::relationQuery($name), + }; + } + + public function getId(): int|null + { + return $this->id; + } + + public function getName(): string + { + return $this->name; + } + + public function setId(int|null $id): void + { + $this->setAttribute('id', $id); + } + + public function getLimitedItems(): array + { + return $this->relation('limitedItems'); + } + public function getLimitedItemsQuery(): ActiveQuery { return $this->hasMany(Item::class, ['category_id' => 'id'])->onCondition(['item.id' => [1, 2, 3]]); } + public function getItems(): array + { + return $this->relation('items'); + } + public function getItemsQuery(): ActiveQuery { return $this->hasMany(Item::class, ['category_id' => 'id']); } + public function getOrderItems(): array + { + return $this->relation('orderItems'); + } + public function getOrderItemsQuery(): ActiveQuery { return $this->hasMany(OrderItem::class, ['item_id' => 'id'])->via('items'); } + public function getOrders(): array + { + return $this->relation('orders'); + } + public function getOrdersQuery(): ActiveQuery { return $this->hasMany(Order::class, ['id' => 'order_id'])->via('orderItems'); diff --git a/tests/Stubs/ActiveRecord/Customer.php b/tests/Stubs/ActiveRecord/Customer.php index 360ea5508..923c28b3e 100644 --- a/tests/Stubs/ActiveRecord/Customer.php +++ b/tests/Stubs/ActiveRecord/Customer.php @@ -5,24 +5,25 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\ActiveRecord\ActiveQuery; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\ActiveQueryInterface; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class Customer. - * - * @property int $id - * @property string $name - * @property string $email - * @property string $address - * @property int $status - * - * @method CustomerQuery findBySql($sql, $params = []) static. */ class Customer extends ActiveRecord { public const STATUS_ACTIVE = 1; public const STATUS_INACTIVE = 2; + protected int $id; + protected string $email; + protected string|null $name = null; + protected string|null $address = null; + protected int|null $status = 0; + protected bool|string|null $bool_status = false; + protected int|null $profile_id = null; + /** * @var int|string */ @@ -37,9 +38,98 @@ public function getTableName(): string return 'customer'; } - public function getName(): string + public function relationQuery(string $name): ActiveQueryInterface + { + return match ($name) { + 'profile' => $this->getProfileQuery(), + 'orders' => $this->getOrdersQuery(), + 'ordersPlain' => $this->getOrdersPlainQuery(), + 'ordersNoOrder' => $this->getOrdersNoOrderQuery(), + 'expensiveOrders' => $this->getExpensiveOrdersQuery(), + 'ordersWithItems' => $this->getOrdersWithItemsQuery(), + 'expensiveOrdersWithNullFK' => $this->getExpensiveOrdersWithNullFKQuery(), + 'ordersWithNullFK' => $this->getOrdersWithNullFKQuery(), + 'orders2' => $this->getOrders2Query(), + 'orderItems' => $this->getOrderItemsQuery(), + 'orderItems2' => $this->getOrderItems2Query(), + 'items2' => $this->getItems2Query(), + default => parent::relationQuery($name), + }; + } + + public function getId(): int + { + return $this->id; + } + + public function getEmail(): string + { + return $this->email; + } + + public function getName(): string|null + { + return $this->name; + } + + public function getAddress(): string|null + { + return $this->address; + } + + public function getStatus(): int|null + { + return $this->status; + } + + public function getBoolStatus(): bool|null + { + return $this->bool_status; + } + + public function getProfileId(): int|null + { + return $this->profile_id; + } + + public function setId(int $id): void + { + $this->id = $id; + } + + public function setEmail(string $email): void + { + $this->email = $email; + } + + public function setName(string|null $name): void + { + $this->name = $name; + } + + public function setAddress(string|null $address): void + { + $this->address = $address; + } + + public function setStatus(int|null $status): void + { + $this->status = $status; + } + + public function setBoolStatus(bool|null $bool_status): void + { + $this->bool_status = $bool_status; + } + + public function setProfileId(int|null $profile_id): void { - return $this->getAttribute('name'); + $this->setAttribute('profile_id', $profile_id); + } + + public function getProfile(): Profile|null + { + return $this->relation('profile'); } public function getProfileQuery(): ActiveQuery @@ -47,35 +137,65 @@ public function getProfileQuery(): ActiveQuery return $this->hasOne(Profile::class, ['id' => 'profile_id']); } + public function getOrdersPlain(): array + { + return $this->relation('ordersPlain'); + } + public function getOrdersPlainQuery(): ActiveQuery { return $this->hasMany(Order::class, ['customer_id' => 'id']); } + public function getOrders(): array + { + return $this->relation('orders'); + } + public function getOrdersQuery(): ActiveQuery { return $this->hasMany(Order::class, ['customer_id' => 'id'])->orderBy('[[id]]'); } + public function getOrdersNoOrder(): array + { + return $this->relation('ordersNoOrder'); + } + public function getOrdersNoOrderQuery(): ActiveQuery { return $this->hasMany(Order::class, ['customer_id' => 'id']); } + public function getExpensiveOrders(): array + { + return $this->relation('expensiveOrders'); + } + public function getExpensiveOrdersQuery(): ActiveQuery { return $this->hasMany(Order::class, ['customer_id' => 'id'])->andWhere('[[total]] > 50')->orderBy('id'); } - public function getItemQuery(): void + public function getItem(): void { } + public function getOrdersWithItems(): array + { + return $this->relation('ordersWithItems'); + } + public function getOrdersWithItemsQuery(): ActiveQuery { return $this->hasMany(Order::class, ['customer_id' => 'id'])->with('orderItems'); } + public function getExpensiveOrdersWithNullFK(): array + { + return $this->relation('expensiveOrdersWithNullFK'); + } + public function getExpensiveOrdersWithNullFKQuery(): ActiveQuery { return $this->hasMany( @@ -84,16 +204,31 @@ public function getExpensiveOrdersWithNullFKQuery(): ActiveQuery )->andWhere('[[total]] > 50')->orderBy('id'); } + public function getOrdersWithNullFK(): array + { + return $this->relation('ordersWithNullFK'); + } + public function getOrdersWithNullFKQuery(): ActiveQuery { return $this->hasMany(OrderWithNullFK::class, ['customer_id' => 'id'])->orderBy('id'); } + public function getOrders2(): array + { + return $this->relation('orders2'); + } + public function getOrders2Query(): ActiveQuery { return $this->hasMany(Order::class, ['customer_id' => 'id'])->inverseOf('customer2')->orderBy('id'); } + public function getOrderItems(): array + { + return $this->relation('orderItems'); + } + /** deeply nested table relation */ public function getOrderItemsQuery(): ActiveQuery { @@ -105,8 +240,9 @@ public function getOrderItemsQuery(): ActiveQuery })->orderBy('id'); } - public function setOrdersReadOnly(): void + public function getOrderItems2(): array { + return $this->relation('orderItems2'); } public function getOrderItems2Query(): ActiveQuery @@ -115,6 +251,11 @@ public function getOrderItems2Query(): ActiveQuery ->via('ordersNoOrder'); } + public function getItems2(): array + { + return $this->relation('items2'); + } + public function getItems2Query(): ActiveQuery { return $this->hasMany(Item::class, ['id' => 'item_id']) diff --git a/tests/Stubs/ActiveRecord/CustomerClosureField.php b/tests/Stubs/ActiveRecord/CustomerClosureField.php index ca5220a96..cb1567edf 100644 --- a/tests/Stubs/ActiveRecord/CustomerClosureField.php +++ b/tests/Stubs/ActiveRecord/CustomerClosureField.php @@ -4,19 +4,21 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class CustomerClosureField. - * - * @property int $id - * @property string $name - * @property string $email - * @property string $address - * @property int $status */ final class CustomerClosureField extends ActiveRecord { + protected int $id; + protected string $email; + protected string|null $name = null; + protected string|null $address = null; + protected int|null $status = 0; + protected bool|string|null $bool_status = false; + protected int|null $profile_id = null; + public function getTableName(): string { return 'customer'; diff --git a/tests/Stubs/ActiveRecord/CustomerForArrayable.php b/tests/Stubs/ActiveRecord/CustomerForArrayable.php index 591cd79cf..2abd3640f 100644 --- a/tests/Stubs/ActiveRecord/CustomerForArrayable.php +++ b/tests/Stubs/ActiveRecord/CustomerForArrayable.php @@ -4,16 +4,10 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class CustomerClosureField. - * - * @property int $id - * @property string $name - * @property string $email - * @property string $address - * @property int $status */ class CustomerForArrayable extends ActiveRecord { @@ -21,6 +15,14 @@ class CustomerForArrayable extends ActiveRecord public ?CustomerForArrayable $item = null; + protected int $id; + protected string $email; + protected string|null $name = null; + protected string|null $address = null; + protected int|null $status = 0; + protected bool|string|null $bool_status = false; + protected int|null $profile_id = null; + public function getTableName(): string { return 'customer'; diff --git a/tests/Stubs/ActiveRecord/CustomerWithAlias.php b/tests/Stubs/ActiveRecord/CustomerWithAlias.php index bb1914f6c..13c11163a 100644 --- a/tests/Stubs/ActiveRecord/CustomerWithAlias.php +++ b/tests/Stubs/ActiveRecord/CustomerWithAlias.php @@ -4,18 +4,10 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class Customer. - * - * @property int $id - * @property string $name - * @property string $email - * @property string $address - * @property int $status - * - * @method CustomerQuery findBySql($sql, $params = []) static */ final class CustomerWithAlias extends ActiveRecord { @@ -25,6 +17,14 @@ final class CustomerWithAlias extends ActiveRecord public int $status2; public float $sumTotal; + public int $id; + public string $email; + public string|null $name = null; + public string|null $address = null; + public int|null $status = null; + public bool|string|null $bool_status = null; + public int|null $profile_id = null; + public function getTableName(): string { return 'customer'; diff --git a/tests/Stubs/ActiveRecord/CustomerWithConstructor.php b/tests/Stubs/ActiveRecord/CustomerWithConstructor.php index 64f198392..29a310198 100644 --- a/tests/Stubs/ActiveRecord/CustomerWithConstructor.php +++ b/tests/Stubs/ActiveRecord/CustomerWithConstructor.php @@ -5,22 +5,24 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\ActiveRecord\ActiveQuery; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\ActiveQueryInterface; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\Aliases\Aliases; use Yiisoft\Db\Connection\ConnectionInterface; /** * CustomerWithConstructor. - * - * @property int $id - * @property string $name - * @property string $email - * @property string $address - * @property int $status - * @property ProfileWithConstructor $profile */ final class CustomerWithConstructor extends ActiveRecord { + protected int $id; + protected string $email; + protected string|null $name = null; + protected string|null $address = null; + protected int|null $status = 0; + protected bool|string|null $bool_status = false; + protected int|null $profile_id = null; + public function __construct(ConnectionInterface $db, private Aliases $aliases) { parent::__construct($db); @@ -31,6 +33,19 @@ public function getTableName(): string return 'customer'; } + public function relationQuery(string $name): ActiveQueryInterface + { + return match ($name) { + 'profile' => $this->getProfileQuery(), + default => parent::relationQuery($name), + }; + } + + public function getProfile(): Profile|null + { + return $this->relation('profile'); + } + public function getProfileQuery(): ActiveQuery { return $this->hasOne(Profile::class, ['id' => 'profile_id']); diff --git a/tests/Stubs/ActiveRecord/DefaultPk.php b/tests/Stubs/ActiveRecord/DefaultPk.php index 73cae8e7e..73d4f71af 100644 --- a/tests/Stubs/ActiveRecord/DefaultPk.php +++ b/tests/Stubs/ActiveRecord/DefaultPk.php @@ -4,10 +4,13 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; final class DefaultPk extends ActiveRecord { + public int $id; + public string $type; + public function getTableName(): string { return 'default_pk'; diff --git a/tests/Stubs/ActiveRecord/Department.php b/tests/Stubs/ActiveRecord/Department.php index 32fd4f8e2..cbe9e0f8c 100644 --- a/tests/Stubs/ActiveRecord/Department.php +++ b/tests/Stubs/ActiveRecord/Department.php @@ -5,22 +5,36 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\ActiveRecord\ActiveQuery; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\ActiveQueryInterface; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; +use Yiisoft\ActiveRecord\ActiveRecordInterface; /** * Class Department - * - * @property int $id - * @property string $title - * @property Employee[] $employees */ final class Department extends ActiveRecord { + protected int $id; + protected string $title; + public function getTableName(): string { return 'department'; } + public function relationQuery(string $name): ActiveQueryInterface + { + return match ($name) { + 'employees' => $this->getEmployeesQuery(), + default => parent::relationQuery($name), + }; + } + + public function getEmployees(): ActiveRecordInterface + { + return $this->relation('employees'); + } + public function getEmployeesQuery(): ActiveQuery { return $this->hasMany( diff --git a/tests/Stubs/ActiveRecord/Document.php b/tests/Stubs/ActiveRecord/Document.php index 4ba1f7a2e..ab74651fc 100644 --- a/tests/Stubs/ActiveRecord/Document.php +++ b/tests/Stubs/ActiveRecord/Document.php @@ -4,17 +4,16 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -/** - * @property int $id - * @property string $title - * @property string $content - * @property int $version - * @property array $properties - */ final class Document extends ActiveRecord { + public int $id; + public string $title; + public string $content; + public int $version; + public array $properties; + public function optimisticLock(): ?string { return 'version'; diff --git a/tests/Stubs/ActiveRecord/Dossier.php b/tests/Stubs/ActiveRecord/Dossier.php index fb6851850..1caa605e4 100644 --- a/tests/Stubs/ActiveRecord/Dossier.php +++ b/tests/Stubs/ActiveRecord/Dossier.php @@ -5,24 +5,77 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\ActiveRecord\ActiveQuery; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\ActiveQueryInterface; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class Dossier - * - * @property int $id - * @property int $department_id - * @property int $employee_id - * @property string $summary - * @property Employee $employee */ final class Dossier extends ActiveRecord { + protected int $id; + protected int $department_id; + protected int $employee_id; + protected string $summary; + public function getTableName(): string { return 'dossier'; } + public function getId(): int + { + return $this->id; + } + + public function getDepartmentId(): int + { + return $this->department_id; + } + + public function getEmployeeId(): int + { + return $this->employee_id; + } + + public function getSummary(): string + { + return $this->summary; + } + + public function setId(int $id): void + { + $this->id = $id; + } + + public function setDepartmentId(int $departmentId): void + { + $this->setAttribute('department_id', $departmentId); + } + + public function setEmployeeId(int $employeeId): void + { + $this->setAttribute('employee_id', $employeeId); + } + + public function setSummary(string $summary): void + { + $this->summary = $summary; + } + + public function relationQuery(string $name): ActiveQueryInterface + { + return match ($name) { + 'employee' => $this->getEmployeeQuery(), + default => parent::relationQuery($name), + }; + } + + public function getEmployee(): Employee|null + { + return $this->relation('employee'); + } + public function getEmployeeQuery(): ActiveQuery { return $this->hasOne( diff --git a/tests/Stubs/ActiveRecord/Employee.php b/tests/Stubs/ActiveRecord/Employee.php index 422bc053d..b3e02ffe2 100644 --- a/tests/Stubs/ActiveRecord/Employee.php +++ b/tests/Stubs/ActiveRecord/Employee.php @@ -5,31 +5,43 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\ActiveRecord\ActiveQuery; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\ActiveQueryInterface; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class Employee - * - * @property int $id - * @property int $department_id - * @property string $first_name - * @property string $last_name - * @property string $fullName - * @property Department $department - * @property Dossier $dossier */ final class Employee extends ActiveRecord { + protected int $id; + protected int $department_id; + protected string $first_name; + protected string $last_name; + public function getTableName(): string { return 'employee'; } + public function relationQuery(string $name): ActiveQueryInterface + { + return match ($name) { + 'department' => $this->getDepartmentQuery(), + 'dossier' => $this->getDossierQuery(), + default => parent::relationQuery($name), + }; + } + public function getFullName(): string { return $this->first_name . ' ' . $this->last_name; } + public function getDepartment(): Department + { + return $this->relation('department'); + } + public function getDepartmentQuery(): ActiveQuery { return $this @@ -40,6 +52,11 @@ public function getDepartmentQuery(): ActiveQuery ; } + public function getDossier(): Dossier + { + return $this->relation('dossier'); + } + public function getDossierQuery(): ActiveQuery { return $this->hasOne( diff --git a/tests/Stubs/ActiveRecord/Item.php b/tests/Stubs/ActiveRecord/Item.php index 7e0e33bb8..27c4635fb 100644 --- a/tests/Stubs/ActiveRecord/Item.php +++ b/tests/Stubs/ActiveRecord/Item.php @@ -5,22 +5,51 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\ActiveRecord\ActiveQuery; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\ActiveQueryInterface; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class Item. - * - * @property int $id - * @property string $name - * @property int $category_id */ final class Item extends ActiveRecord { + protected int $id; + protected string $name; + protected int $category_id; + public function getTableName(): string { return 'item'; } + public function relationQuery(string $name): ActiveQueryInterface + { + return match ($name) { + 'category' => $this->getCategoryQuery(), + default => parent::relationQuery($name), + }; + } + + public function getId(): int + { + return $this->id; + } + + public function getName(): string + { + return $this->name; + } + + public function getCategoryId(): int + { + return $this->category_id; + } + + public function getCategory(): Category + { + return $this->relation('category'); + } + public function getCategoryQuery(): ActiveQuery { return $this->hasOne(Category::class, ['id' => 'category_id']); diff --git a/tests/Stubs/ActiveRecord/NoExist.php b/tests/Stubs/ActiveRecord/NoExist.php index 4f9d1c248..d2c02c6ca 100644 --- a/tests/Stubs/ActiveRecord/NoExist.php +++ b/tests/Stubs/ActiveRecord/NoExist.php @@ -4,7 +4,7 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; final class NoExist extends ActiveRecord { diff --git a/tests/Stubs/ActiveRecord/NullValues.php b/tests/Stubs/ActiveRecord/NullValues.php index d2d07800a..13247c796 100644 --- a/tests/Stubs/ActiveRecord/NullValues.php +++ b/tests/Stubs/ActiveRecord/NullValues.php @@ -4,7 +4,7 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class NullValues. @@ -15,6 +15,7 @@ * @property int $var3 * @property string $stringcol */ +#[\AllowDynamicProperties] final class NullValues extends ActiveRecord { public function getTableName(): string diff --git a/tests/Stubs/ActiveRecord/Order.php b/tests/Stubs/ActiveRecord/Order.php index 3df48d377..83494c81d 100644 --- a/tests/Stubs/ActiveRecord/Order.php +++ b/tests/Stubs/ActiveRecord/Order.php @@ -5,47 +5,143 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\ActiveRecord\ActiveQuery; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\ActiveQueryInterface; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class Order. - * - * @property int $id - * @property int $customer_id - * @property int $created_at - * @property string $total */ class Order extends ActiveRecord { public const TABLE_NAME = 'order'; - private string|int|null $virtualCustomerId = null; + protected int|null $id; + protected int $customer_id; + protected int $created_at; + protected float $total; + + protected string|int|null $virtualCustomerId = null; public function getTableName(): string { return self::TABLE_NAME; } + public function getId(): int|null + { + return $this->id; + } + + public function getCustomerId(): int + { + return $this->customer_id; + } + + public function getCreatedAt(): int + { + return $this->created_at; + } + + public function getTotal(): float + { + return $this->total; + } + + public function setId(int|null $id): void + { + $this->setAttribute('id', $id); + } + + public function setCustomerId(int $customerId): void + { + $this->setAttribute('customer_id', $customerId); + } + + public function setCreatedAt(int $createdAt): void + { + $this->created_at = $createdAt; + } + + public function setTotal(float $total): void + { + $this->total = $total; + } + + public function relationQuery(string $name): ActiveQueryInterface + { + return match ($name) { + 'virtualCustomer' => $this->getVirtualCustomerQuery(), + 'customer' => $this->getCustomerQuery(), + 'customerJoinedWithProfile' => $this->getCustomerJoinedWithProfileQuery(), + 'customerJoinedWithProfileIndexOrdered' => $this->getCustomerJoinedWithProfileIndexOrderedQuery(), + 'customer2' => $this->getCustomer2Query(), + 'orderItems' => $this->getOrderItemsQuery(), + 'orderItems2' => $this->getOrderItems2Query(), + 'orderItems3' => $this->getOrderItems3Query(), + 'orderItemsWithNullFK' => $this->getOrderItemsWithNullFKQuery(), + 'items' => $this->getItemsQuery(), + 'itemsIndexed' => $this->getItemsIndexedQuery(), + 'itemsWithNullFK' => $this->getItemsWithNullFKQuery(), + 'itemsInOrder1' => $this->getItemsInOrder1Query(), + 'itemsInOrder2' => $this->getItemsInOrder2Query(), + 'books' => $this->getBooksQuery(), + 'booksWithNullFK' => $this->getBooksWithNullFKQuery(), + 'booksViaTable' => $this->getBooksViaTableQuery(), + 'booksWithNullFKViaTable' => $this->getBooksWithNullFKViaTableQuery(), + 'books2' => $this->getBooks2Query(), + 'booksExplicit' => $this->getBooksExplicitQuery(), + 'booksExplicitA' => $this->getBooksExplicitAQuery(), + 'bookItems' => $this->getBookItemsQuery(), + 'movieItems' => $this->getMovieItemsQuery(), + 'limitedItems' => $this->getLimitedItemsQuery(), + 'expensiveItemsUsingViaWithCallable' => $this->getExpensiveItemsUsingViaWithCallableQuery(), + 'cheapItemsUsingViaWithCallable' => $this->getCheapItemsUsingViaWithCallableQuery(), + 'orderItemsFor8' => $this->getOrderItemsFor8Query(), + 'itemsFor8' => $this->getItemsFor8Query(), + default => parent::relationQuery($name), + }; + } + public function setVirtualCustomerId(string|int|null $virtualCustomerId = null): void { $this->virtualCustomerId = $virtualCustomerId; } - public function getVirtualCustomerQuery() + public function getVirtualCustomer(): Customer|null + { + return $this->relation('virtualCustomer'); + } + + public function getVirtualCustomerQuery(): ActiveQuery { return $this->hasOne(Customer::class, ['id' => 'virtualCustomerId']); } + public function getCustomer(): Customer|null + { + return $this->relation('customer'); + } + public function getCustomerQuery(): ActiveQuery { return $this->hasOne(Customer::class, ['id' => 'customer_id']); } + public function getCustomerJoinedWithProfile(): Customer|null + { + return $this->relation('customerJoinedWithProfile'); + } + public function getCustomerJoinedWithProfileQuery(): ActiveQuery { return $this->hasOne(Customer::class, ['id' => 'customer_id'])->joinWith('profile'); } + public function getCustomerJoinedWithProfileIndexOrdered(): array + { + return $this->relation('customerJoinedWithProfileIndexOrdered'); + } + public function getCustomerJoinedWithProfileIndexOrderedQuery(): ActiveQuery { return $this->hasMany( @@ -54,21 +150,41 @@ public function getCustomerJoinedWithProfileIndexOrderedQuery(): ActiveQuery )->joinWith('profile')->orderBy(['profile.description' => SORT_ASC])->indexBy('name'); } + public function getCustomer2(): Customer|null + { + return $this->relation('customer2'); + } + public function getCustomer2Query(): ActiveQuery { return $this->hasOne(Customer::class, ['id' => 'customer_id'])->inverseOf('orders2'); } + public function getOrderItems(): array + { + return $this->relation('orderItems'); + } + public function getOrderItemsQuery(): ActiveQuery { return $this->hasMany(OrderItem::class, ['order_id' => 'id']); } + public function getOrderItems2(): array + { + return $this->relation('orderItems2'); + } + public function getOrderItems2Query(): ActiveQuery { return $this->hasMany(OrderItem::class, ['order_id' => 'id'])->indexBy('item_id'); } + public function getOrderItems3(): array + { + return $this->relation('orderItems3'); + } + public function getOrderItems3Query(): ActiveQuery { return $this->hasMany( @@ -77,11 +193,21 @@ public function getOrderItems3Query(): ActiveQuery )->indexBy(fn ($row) => $row['order_id'] . '_' . $row['item_id']); } + public function getOrderItemsWithNullFK(): array + { + return $this->relation('orderItemsWithNullFK'); + } + public function getOrderItemsWithNullFKQuery(): ActiveQuery { return $this->hasMany(OrderItemWithNullFK::class, ['order_id' => 'id']); } + public function getItems(): array + { + return $this->relation('items'); + } + public function getItemsQuery(): ActiveQuery { return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems', static function ($q) { @@ -89,11 +215,21 @@ public function getItemsQuery(): ActiveQuery })->orderBy('item.id'); } + public function getItemsIndexed(): array + { + return $this->relation('itemsIndexed'); + } + public function getItemsIndexedQuery(): ActiveQuery { return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems')->indexBy('id'); } + public function getItemsWithNullFK(): array + { + return $this->relation('itemsWithNullFK'); + } + public function getItemsWithNullFKQuery(): ActiveQuery { return $this->hasMany( @@ -102,6 +238,11 @@ public function getItemsWithNullFKQuery(): ActiveQuery )->viaTable('order_item_with_null_fk', ['order_id' => 'id']); } + public function getItemsInOrder1(): array + { + return $this->relation('itemsInOrder1'); + } + public function getItemsInOrder1Query(): ActiveQuery { return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems', static function ($q) { @@ -109,6 +250,11 @@ public function getItemsInOrder1Query(): ActiveQuery })->orderBy('name'); } + public function getItemsInOrder2(): array + { + return $this->relation('itemsInOrder2'); + } + public function getItemsInOrder2Query(): ActiveQuery { return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems', static function ($q) { @@ -116,11 +262,21 @@ public function getItemsInOrder2Query(): ActiveQuery })->orderBy('name'); } + public function getBooks(): array + { + return $this->relation('books'); + } + public function getBooksQuery(): ActiveQuery { return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems')->where(['category_id' => 1]); } + public function getBooksWithNullFK(): array + { + return $this->relation('booksWithNullFK'); + } + public function getBooksWithNullFKQuery(): ActiveQuery { return $this->hasMany( @@ -129,6 +285,11 @@ public function getBooksWithNullFKQuery(): ActiveQuery )->via('orderItemsWithNullFK')->where(['category_id' => 1]); } + public function getBooksViaTable(): array + { + return $this->relation('booksViaTable'); + } + public function getBooksViaTableQuery(): ActiveQuery { return $this->hasMany( @@ -137,6 +298,11 @@ public function getBooksViaTableQuery(): ActiveQuery )->viaTable('order_item', ['order_id' => 'id'])->where(['category_id' => 1]); } + public function getBooksWithNullFKViaTable(): array + { + return $this->relation('booksWithNullFKViaTable'); + } + public function getBooksWithNullFKViaTableQuery(): ActiveQuery { return $this->hasMany( @@ -145,6 +311,11 @@ public function getBooksWithNullFKViaTableQuery(): ActiveQuery )->viaTable('order_item_with_null_fk', ['order_id' => 'id'])->where(['category_id' => 1]); } + public function getBooks2(): array + { + return $this->relation('books2'); + } + public function getBooks2Query(): ActiveQuery { return $this->hasMany( @@ -153,6 +324,11 @@ public function getBooks2Query(): ActiveQuery )->onCondition(['category_id' => 1])->viaTable('order_item', ['order_id' => 'id']); } + public function getBooksExplicit(): array + { + return $this->relation('booksExplicit'); + } + public function getBooksExplicitQuery(): ActiveQuery { return $this->hasMany( @@ -161,6 +337,11 @@ public function getBooksExplicitQuery(): ActiveQuery )->onCondition(['category_id' => 1])->viaTable('order_item', ['order_id' => 'id']); } + public function getBooksExplicitA(): array + { + return $this->relation('booksExplicitA'); + } + public function getBooksExplicitAQuery(): ActiveQuery { return $this->hasMany( @@ -169,6 +350,11 @@ public function getBooksExplicitAQuery(): ActiveQuery )->alias('bo')->onCondition(['bo.category_id' => 1])->viaTable('order_item', ['order_id' => 'id']); } + public function getBookItems(): array + { + return $this->relation('bookItems'); + } + public function getBookItemsQuery(): ActiveQuery { return $this->hasMany( @@ -177,6 +363,11 @@ public function getBookItemsQuery(): ActiveQuery )->alias('books')->onCondition(['books.category_id' => 1])->viaTable('order_item', ['order_id' => 'id']); } + public function getMovieItems(): array + { + return $this->relation('movieItems'); + } + public function getMovieItemsQuery(): ActiveQuery { return $this->hasMany( @@ -185,11 +376,21 @@ public function getMovieItemsQuery(): ActiveQuery )->alias('movies')->onCondition(['movies.category_id' => 2])->viaTable('order_item', ['order_id' => 'id']); } + public function getLimitedItems(): array + { + return $this->relation('limitedItems'); + } + public function getLimitedItemsQuery(): ActiveQuery { return $this->hasMany(Item::class, ['id' => 'item_id'])->onCondition(['item.id' => [3, 5]])->via('orderItems'); } + public function getExpensiveItemsUsingViaWithCallable(): array + { + return $this->relation('expensiveItemsUsingViaWithCallable'); + } + public function getExpensiveItemsUsingViaWithCallableQuery(): ActiveQuery { return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems', function (ActiveQuery $q) { @@ -197,6 +398,11 @@ public function getExpensiveItemsUsingViaWithCallableQuery(): ActiveQuery }); } + public function getCheapItemsUsingViaWithCallable(): array + { + return $this->relation('cheapItemsUsingViaWithCallable'); + } + public function getCheapItemsUsingViaWithCallableQuery(): ActiveQuery { return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems', function (ActiveQuery $q) { @@ -204,11 +410,21 @@ public function getCheapItemsUsingViaWithCallableQuery(): ActiveQuery }); } + public function getOrderItemsFor8(): array + { + return $this->relation('orderItemsFor8'); + } + public function getOrderItemsFor8Query(): ActiveQuery { return $this->hasMany(OrderItemWithNullFK::class, ['order_id' => 'id'])->andOnCondition(['subtotal' => 8.0]); } + public function getItemsFor8(): array + { + return $this->relation('itemsFor8'); + } + public function getItemsFor8Query(): ActiveQuery { return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItemsFor8'); diff --git a/tests/Stubs/ActiveRecord/OrderItem.php b/tests/Stubs/ActiveRecord/OrderItem.php index 8af8b7cca..3c25586a7 100644 --- a/tests/Stubs/ActiveRecord/OrderItem.php +++ b/tests/Stubs/ActiveRecord/OrderItem.php @@ -5,18 +5,19 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\ActiveRecord\ActiveQuery; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\ActiveQueryInterface; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class OrderItem. - * - * @property int $order_id - * @property int $item_id - * @property int $quantity - * @property string $subtotal */ final class OrderItem extends ActiveRecord { + protected int $order_id; + protected int $item_id; + protected int $quantity; + protected float $subtotal; + public function getTableName(): string { return 'order_item'; @@ -26,6 +27,7 @@ public function fields(): array { $fields = parent::fields(); + // Wrong fields. Should be without values. $fields['order_id'] = $this->getAttribute('order_id'); $fields['item_id'] = $this->getAttribute('item_id'); $fields['price'] = $this->getAttribute('subtotal') / $this->getAttribute('quantity'); @@ -35,27 +37,104 @@ public function fields(): array return $fields; } + public function getOrderId(): int + { + return $this->order_id; + } + + public function getItemId(): int + { + return $this->item_id; + } + + public function getQuantity(): int + { + return $this->quantity; + } + + public function getSubtotal(): float + { + return $this->subtotal; + } + + public function setOrderId(int $orderId): void + { + $this->setAttribute('order_id', $orderId); + } + + public function setItemId(int $itemId): void + { + $this->setAttribute('item_id', $itemId); + } + + public function setQuantity(int $quantity): void + { + $this->quantity = $quantity; + } + + public function setSubtotal(float $subtotal): void + { + $this->subtotal = $subtotal; + } + + public function relationQuery(string $name): ActiveQueryInterface + { + return match ($name) { + 'order' => $this->getOrderQuery(), + 'item' => $this->getItemQuery(), + 'orderItemCompositeWithJoin' => $this->getOrderItemCompositeWithJoinQuery(), + 'orderItemCompositeNoJoin' => $this->getOrderItemCompositeNoJoinQuery(), + 'custom' => $this->getCustomQuery(), + default => parent::relationQuery($name), + }; + } + + public function getOrder(): Order|null + { + return $this->relation('order'); + } + public function getOrderQuery(): ActiveQuery { return $this->hasOne(Order::class, ['id' => 'order_id']); } + public function getItem(): Item|null + { + return $this->relation('item'); + } + public function getItemQuery(): ActiveQuery { return $this->hasOne(Item::class, ['id' => 'item_id']); } + public function getOrderItemCompositeWithJoin(): self|null + { + return $this->relation('orderItemCompositeWithJoin'); + } + public function getOrderItemCompositeWithJoinQuery(): ActiveQuery { /** relations used by testFindCompositeWithJoin() */ return $this->hasOne(self::class, ['item_id' => 'item_id', 'order_id' => 'order_id' ])->joinWith('item'); } + public function getOrderItemCompositeNoJoin(): self|null + { + return $this->relation('orderItemCompositeNoJoin'); + } + public function getOrderItemCompositeNoJoinQuery(): ActiveQuery { return $this->hasOne(self::class, ['item_id' => 'item_id', 'order_id' => 'order_id' ]); } + public function getCustom(): Order|null + { + return $this->relation('custom'); + } + public function getCustomQuery(): ActiveQuery { return new ActiveQuery(Order::class, $this->db()); diff --git a/tests/Stubs/ActiveRecord/OrderItemWithNullFK.php b/tests/Stubs/ActiveRecord/OrderItemWithNullFK.php index d848bc59a..6d13984ef 100644 --- a/tests/Stubs/ActiveRecord/OrderItemWithNullFK.php +++ b/tests/Stubs/ActiveRecord/OrderItemWithNullFK.php @@ -4,18 +4,18 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class OrderItem. - * - * @property int $order_id - * @property int $item_id - * @property int $quantity - * @property string $subtotal */ final class OrderItemWithNullFK extends ActiveRecord { + protected int|null $order_id = null; + protected int|null $item_id = null; + protected int $quantity; + protected float $subtotal; + public function getTableName(): string { return 'order_item_with_null_fk'; diff --git a/tests/Stubs/ActiveRecord/OrderWithNullFK.php b/tests/Stubs/ActiveRecord/OrderWithNullFK.php index 2f76b5324..4d36dd3a4 100644 --- a/tests/Stubs/ActiveRecord/OrderWithNullFK.php +++ b/tests/Stubs/ActiveRecord/OrderWithNullFK.php @@ -4,20 +4,40 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class Order. - * - * @property int $id - * @property int $customer_id - * @property int $created_at - * @property string $total */ final class OrderWithNullFK extends ActiveRecord { + protected int $id; + protected int|null $customer_id = null; + protected int $created_at; + protected float $total; + public function getTableName(): string { return 'order_with_null_fk'; } + + public function getId(): int + { + return $this->id; + } + + public function getCustomerId(): int|null + { + return $this->customer_id; + } + + public function getCreatedAt(): int + { + return $this->created_at; + } + + public function getTotal(): float + { + return $this->total; + } } diff --git a/tests/Stubs/ActiveRecord/Profile.php b/tests/Stubs/ActiveRecord/Profile.php index 0d5e06e14..1a5f0bcb4 100644 --- a/tests/Stubs/ActiveRecord/Profile.php +++ b/tests/Stubs/ActiveRecord/Profile.php @@ -4,18 +4,18 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class Profile. - * - * @property int $id - * @property string $description */ final class Profile extends ActiveRecord { public const TABLE_NAME = 'profile'; + protected int $id; + protected string $description; + public function getTableName(): string { return self::TABLE_NAME; diff --git a/tests/Stubs/ActiveRecord/ProfileWithConstructor.php b/tests/Stubs/ActiveRecord/ProfileWithConstructor.php index c6b87e0fe..64a6be799 100644 --- a/tests/Stubs/ActiveRecord/ProfileWithConstructor.php +++ b/tests/Stubs/ActiveRecord/ProfileWithConstructor.php @@ -4,18 +4,18 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; use Yiisoft\Aliases\Aliases; use Yiisoft\Db\Connection\ConnectionInterface; /** * Class Profile. - * - * @property int $id - * @property string $description */ final class ProfileWithConstructor extends ActiveRecord { + protected int $id; + protected string $description; + public function __construct(ConnectionInterface $db, private Aliases $aliases) { parent::__construct($db); diff --git a/tests/Stubs/ActiveRecord/TestTrigger.php b/tests/Stubs/ActiveRecord/TestTrigger.php index b60400ffd..cc9cc0dea 100644 --- a/tests/Stubs/ActiveRecord/TestTrigger.php +++ b/tests/Stubs/ActiveRecord/TestTrigger.php @@ -4,16 +4,16 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class TestTrigger. - * - * @property int $id - * @property string $stringcol */ final class TestTrigger extends ActiveRecord { + public int $id; + public string $stringcol; + public function getTableName(): string { return 'test_trigger'; diff --git a/tests/Stubs/ActiveRecord/TestTriggerAlert.php b/tests/Stubs/ActiveRecord/TestTriggerAlert.php index f66538934..1e62ebf03 100644 --- a/tests/Stubs/ActiveRecord/TestTriggerAlert.php +++ b/tests/Stubs/ActiveRecord/TestTriggerAlert.php @@ -4,16 +4,16 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; /** * Class TestTriggerAlert. - * - * @property int $id - * @property string $stringcol */ final class TestTriggerAlert extends ActiveRecord { + public int $id; + public string $stringcol; + public function getTableName(): string { return 'test_trigger_alert'; diff --git a/tests/Stubs/ActiveRecord/Type.php b/tests/Stubs/ActiveRecord/Type.php index ec8c47849..f146835aa 100644 --- a/tests/Stubs/ActiveRecord/Type.php +++ b/tests/Stubs/ActiveRecord/Type.php @@ -4,27 +4,32 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; +use Yiisoft\Db\Expression\Expression; /** * Model representing type table. - * - * @property int $int_col - * @property int $int_col2 DEFAULT 1 - * @property int $smallint_col DEFAULT 1 - * @property string $char_col - * @property string $char_col2 DEFAULT 'something' - * @property string $char_col3 - * @property float $float_col - * @property float $float_col2 DEFAULT '1.23' - * @property string $blob_col - * @property float $numeric_col DEFAULT '33.22' - * @property string $time DEFAULT '2002-01-01 00:00:00' - * @property bool $bool_col - * @property bool $bool_col2 DEFAULT 1 */ -final class Type extends ActiveRecord +class Type extends ActiveRecord { + public int $int_col; + public int|null $int_col2 = 1; + public int|null $tinyint_col = 1; + public int|null $smallint_col = 1; + public string $char_col; + public string|null $char_col2 = 'something'; + public string|null $char_col3 = null; + public float $float_col; + public float|null $float_col2 = 1.23; + public mixed $blob_col; + public float|null $numeric_col = 33.22; + public string $time = '2002-01-01 00:00:00'; + public bool|int $bool_col; + public bool|int|string|null $bool_col2 = true; + public string|Expression $ts_default; + public int|string $bit_col = 0b1000_0010; + public array|null $json_col = null; + public function getTableName(): string { return 'type'; diff --git a/tests/Stubs/ActiveRecord/UserAR.php b/tests/Stubs/ActiveRecord/UserAR.php index 1ab979966..08b72d5a6 100644 --- a/tests/Stubs/ActiveRecord/UserAR.php +++ b/tests/Stubs/ActiveRecord/UserAR.php @@ -4,7 +4,7 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; final class UserAR extends ActiveRecord { @@ -12,6 +12,18 @@ final class UserAR extends ActiveRecord public const STATUS_ACTIVE = 10; public const ROLE_USER = 10; + public int $id; + public string $username; + public string $auth_key; + public string $password_hash; + public string|null $password_reset_token = null; + public string $email; + public int $role = 10; + public int $status = 10; + public int $created_at; + public int $updated_at; + public bool $is_deleted = false; + public function getTableName(): string { return '{{%bool_user}}'; diff --git a/tests/Stubs/MagicActiveRecord.php b/tests/Stubs/MagicActiveRecord.php new file mode 100644 index 000000000..fba7d7164 --- /dev/null +++ b/tests/Stubs/MagicActiveRecord.php @@ -0,0 +1,47 @@ + + * @template-implements IteratorAggregate + * + * @see ActiveRecord for more information. + */ +class MagicActiveRecord extends ActiveRecord implements + ArrayableInterface, + ArrayAccess, + IteratorAggregate, + TransactionalInterface +{ + use ArrayableTrait; + use ArrayAccessTrait; + use ArrayIteratorTrait; + use MagicPropertiesTrait; + use MagicRelationsTrait; + use TransactionalTrait; +} diff --git a/tests/Stubs/MagicActiveRecord/Alpha.php b/tests/Stubs/MagicActiveRecord/Alpha.php new file mode 100644 index 000000000..1392800c3 --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/Alpha.php @@ -0,0 +1,27 @@ +hasMany(Beta::class, ['alpha_string_identifier' => 'string_identifier']); + } +} diff --git a/tests/Stubs/MagicActiveRecord/Animal.php b/tests/Stubs/MagicActiveRecord/Animal.php new file mode 100644 index 000000000..9d628b901 --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/Animal.php @@ -0,0 +1,49 @@ +type = static::class; + } + + public function getDoes() + { + return $this->does; + } + + public function instantiate($row): ActiveRecordInterface + { + $class = $row['type']; + + return new $class($this->db()); + } + + public function setDoes(string $value): void + { + $this->does = $value; + } +} diff --git a/tests/Stubs/MagicActiveRecord/ArrayAndJsonTypes.php b/tests/Stubs/MagicActiveRecord/ArrayAndJsonTypes.php new file mode 100644 index 000000000..13dadbf38 --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/ArrayAndJsonTypes.php @@ -0,0 +1,11 @@ +hasOne(Alpha::class, ['string_identifier' => 'alpha_string_identifier']); + } +} diff --git a/tests/Stubs/MagicActiveRecord/BitValues.php b/tests/Stubs/MagicActiveRecord/BitValues.php new file mode 100644 index 000000000..70284169d --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/BitValues.php @@ -0,0 +1,17 @@ +setDoes('meow'); + } + + public function getException(): void + { + throw new Exception('no'); + } + + /** + * This is to test if __isset catches the error. + * + * @throw DivisionByZeroError + */ + public function getThrowable(): float|int + { + return 5 / 0; + } + + public function setNonExistingProperty(string $value): void + { + } +} diff --git a/tests/Stubs/MagicActiveRecord/Category.php b/tests/Stubs/MagicActiveRecord/Category.php new file mode 100644 index 000000000..d8cc2fc5a --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/Category.php @@ -0,0 +1,42 @@ +hasMany(Item::class, ['category_id' => 'id'])->onCondition(['item.id' => [1, 2, 3]]); + } + + public function getItemsQuery(): ActiveQuery + { + return $this->hasMany(Item::class, ['category_id' => 'id']); + } + + public function getOrderItemsQuery(): ActiveQuery + { + return $this->hasMany(OrderItem::class, ['item_id' => 'id'])->via('items'); + } + + public function getOrdersQuery(): ActiveQuery + { + return $this->hasMany(Order::class, ['id' => 'order_id'])->via('orderItems'); + } +} diff --git a/tests/Stubs/MagicActiveRecord/Customer.php b/tests/Stubs/MagicActiveRecord/Customer.php new file mode 100644 index 000000000..1c24001ad --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/Customer.php @@ -0,0 +1,121 @@ +getAttribute('name'); + } + + public function getProfileQuery(): ActiveQuery + { + return $this->hasOne(Profile::class, ['id' => 'profile_id']); + } + + public function getOrdersPlainQuery(): ActiveQuery + { + return $this->hasMany(Order::class, ['customer_id' => 'id']); + } + + public function getOrdersQuery(): ActiveQuery + { + return $this->hasMany(Order::class, ['customer_id' => 'id'])->orderBy('[[id]]'); + } + + public function getOrdersNoOrderQuery(): ActiveQuery + { + return $this->hasMany(Order::class, ['customer_id' => 'id']); + } + + public function getExpensiveOrdersQuery(): ActiveQuery + { + return $this->hasMany(Order::class, ['customer_id' => 'id'])->andWhere('[[total]] > 50')->orderBy('id'); + } + + public function getItemQuery(): void + { + } + + public function getOrdersWithItemsQuery(): ActiveQuery + { + return $this->hasMany(Order::class, ['customer_id' => 'id'])->with('orderItems'); + } + + public function getExpensiveOrdersWithNullFKQuery(): ActiveQuery + { + return $this->hasMany( + OrderWithNullFK::class, + ['customer_id' => 'id'] + )->andWhere('[[total]] > 50')->orderBy('id'); + } + + public function getOrdersWithNullFKQuery(): ActiveQuery + { + return $this->hasMany(OrderWithNullFK::class, ['customer_id' => 'id'])->orderBy('id'); + } + + public function getOrders2Query(): ActiveQuery + { + return $this->hasMany(Order::class, ['customer_id' => 'id'])->inverseOf('customer2')->orderBy('id'); + } + + /** deeply nested table relation */ + public function getOrderItemsQuery(): ActiveQuery + { + $rel = $this->hasMany(Item::class, ['id' => 'item_id']); + + return $rel->viaTable('order_item', ['order_id' => 'id'], function ($q) { + /* @var $q ActiveQuery */ + $q->viaTable('order', ['customer_id' => 'id']); + })->orderBy('id'); + } + + public function setOrdersReadOnly(): void + { + } + + public function getOrderItems2Query(): ActiveQuery + { + return $this->hasMany(OrderItem::class, ['order_id' => 'id']) + ->via('ordersNoOrder'); + } + + public function getItems2Query(): ActiveQuery + { + return $this->hasMany(Item::class, ['id' => 'item_id']) + ->via('orderItems2'); + } +} diff --git a/tests/Stubs/MagicActiveRecord/CustomerClosureField.php b/tests/Stubs/MagicActiveRecord/CustomerClosureField.php new file mode 100644 index 000000000..b5d8f7ebc --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/CustomerClosureField.php @@ -0,0 +1,33 @@ + $customer->status === 1 ? 'active' : 'inactive'; + + return $fields; + } +} diff --git a/tests/Stubs/MagicActiveRecord/CustomerForArrayable.php b/tests/Stubs/MagicActiveRecord/CustomerForArrayable.php new file mode 100644 index 000000000..49d8d66e7 --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/CustomerForArrayable.php @@ -0,0 +1,57 @@ +item = $item; + } + + public function setItems(self ...$items) + { + $this->items = $items; + } + + public function toArray(array $fields = [], array $expand = [], bool $recursive = true): array + { + $data = parent::toArray($fields, $expand, $recursive); + + $data['status'] = $this->status == 1 ? 'active' : 'inactive'; + + return $data; + } +} diff --git a/tests/Stubs/MagicActiveRecord/CustomerQuery.php b/tests/Stubs/MagicActiveRecord/CustomerQuery.php new file mode 100644 index 000000000..cc4744023 --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/CustomerQuery.php @@ -0,0 +1,19 @@ +andWhere('[[status]]=1'); + + return $this; + } +} diff --git a/tests/Stubs/MagicActiveRecord/CustomerWithAlias.php b/tests/Stubs/MagicActiveRecord/CustomerWithAlias.php new file mode 100644 index 000000000..ed21bb297 --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/CustomerWithAlias.php @@ -0,0 +1,32 @@ +hasOne(Profile::class, ['id' => 'profile_id']); + } +} diff --git a/tests/Stubs/ActiveRecord/CustomerWithProperties.php b/tests/Stubs/MagicActiveRecord/CustomerWithProperties.php similarity index 89% rename from tests/Stubs/ActiveRecord/CustomerWithProperties.php rename to tests/Stubs/MagicActiveRecord/CustomerWithProperties.php index f0dc90109..79c2c581f 100644 --- a/tests/Stubs/ActiveRecord/CustomerWithProperties.php +++ b/tests/Stubs/MagicActiveRecord/CustomerWithProperties.php @@ -2,15 +2,15 @@ declare(strict_types=1); -namespace Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord; +namespace Yiisoft\ActiveRecord\Tests\Stubs\MagicActiveRecord; use Yiisoft\ActiveRecord\ActiveQuery; -use Yiisoft\ActiveRecord\ActiveRecord; +use Yiisoft\ActiveRecord\Tests\Stubs\MagicActiveRecord; /** * Class Customer with defined properties. */ -class CustomerWithProperties extends ActiveRecord +class CustomerWithProperties extends MagicActiveRecord { protected int $id; protected string $email; diff --git a/tests/Stubs/MagicActiveRecord/DefaultPk.php b/tests/Stubs/MagicActiveRecord/DefaultPk.php new file mode 100644 index 000000000..57d5d1061 --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/DefaultPk.php @@ -0,0 +1,15 @@ +hasMany( + Employee::class, + [ + 'department_id' => 'id', + ] + )->inverseOf('department'); + } +} diff --git a/tests/Stubs/MagicActiveRecord/Document.php b/tests/Stubs/MagicActiveRecord/Document.php new file mode 100644 index 000000000..4fe8ec961 --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/Document.php @@ -0,0 +1,22 @@ +setDoes('bark'); + } +} diff --git a/tests/Stubs/MagicActiveRecord/Dossier.php b/tests/Stubs/MagicActiveRecord/Dossier.php new file mode 100644 index 000000000..ba52aebdc --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/Dossier.php @@ -0,0 +1,36 @@ +hasOne( + Employee::class, + [ + 'department_id' => 'department_id', + 'id' => 'employee_id', + ] + )->inverseOf('dossier'); + } +} diff --git a/tests/Stubs/MagicActiveRecord/Employee.php b/tests/Stubs/MagicActiveRecord/Employee.php new file mode 100644 index 000000000..9410b6a8c --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/Employee.php @@ -0,0 +1,53 @@ +first_name . ' ' . $this->last_name; + } + + public function getDepartmentQuery(): ActiveQuery + { + return $this + ->hasOne(Department::class, [ + 'id' => 'department_id', + ]) + ->inverseOf('employees') + ; + } + + public function getDossierQuery(): ActiveQuery + { + return $this->hasOne( + Dossier::class, + [ + 'department_id' => 'department_id', + 'employee_id' => 'id', + ] + )->inverseOf('employee'); + } +} diff --git a/tests/Stubs/MagicActiveRecord/Item.php b/tests/Stubs/MagicActiveRecord/Item.php new file mode 100644 index 000000000..9ca235d99 --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/Item.php @@ -0,0 +1,28 @@ +hasOne(Category::class, ['id' => 'category_id']); + } +} diff --git a/tests/Stubs/MagicActiveRecord/NoExist.php b/tests/Stubs/MagicActiveRecord/NoExist.php new file mode 100644 index 000000000..8af4b385b --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/NoExist.php @@ -0,0 +1,15 @@ +virtualCustomerId = $virtualCustomerId; + } + + public function getVirtualCustomerQuery() + { + return $this->hasOne(Customer::class, ['id' => 'virtualCustomerId']); + } + + public function getCustomerQuery(): ActiveQuery + { + return $this->hasOne(Customer::class, ['id' => 'customer_id']); + } + + public function getCustomerJoinedWithProfileQuery(): ActiveQuery + { + return $this->hasOne(Customer::class, ['id' => 'customer_id'])->joinWith('profile'); + } + + public function getCustomerJoinedWithProfileIndexOrderedQuery(): ActiveQuery + { + return $this->hasMany( + Customer::class, + ['id' => 'customer_id'] + )->joinWith('profile')->orderBy(['profile.description' => SORT_ASC])->indexBy('name'); + } + + public function getCustomer2Query(): ActiveQuery + { + return $this->hasOne(Customer::class, ['id' => 'customer_id'])->inverseOf('orders2'); + } + + public function getOrderItemsQuery(): ActiveQuery + { + return $this->hasMany(OrderItem::class, ['order_id' => 'id']); + } + + public function getOrderItems2Query(): ActiveQuery + { + return $this->hasMany(OrderItem::class, ['order_id' => 'id'])->indexBy('item_id'); + } + + public function getOrderItems3Query(): ActiveQuery + { + return $this->hasMany( + OrderItem::class, + ['order_id' => 'id'] + )->indexBy(fn ($row) => $row['order_id'] . '_' . $row['item_id']); + } + + public function getOrderItemsWithNullFKQuery(): ActiveQuery + { + return $this->hasMany(OrderItemWithNullFK::class, ['order_id' => 'id']); + } + + public function getItemsQuery(): ActiveQuery + { + return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems', static function ($q) { + // additional query configuration + })->orderBy('item.id'); + } + + public function getItemsIndexedQuery(): ActiveQuery + { + return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems')->indexBy('id'); + } + + public function getItemsWithNullFKQuery(): ActiveQuery + { + return $this->hasMany( + Item::class, + ['id' => 'item_id'] + )->viaTable('order_item_with_null_fk', ['order_id' => 'id']); + } + + public function getItemsInOrder1Query(): ActiveQuery + { + return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems', static function ($q) { + $q->orderBy(['subtotal' => SORT_ASC]); + })->orderBy('name'); + } + + public function getItemsInOrder2Query(): ActiveQuery + { + return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems', static function ($q) { + $q->orderBy(['subtotal' => SORT_DESC]); + })->orderBy('name'); + } + + public function getBooksQuery(): ActiveQuery + { + return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems')->where(['category_id' => 1]); + } + + public function getBooksWithNullFKQuery(): ActiveQuery + { + return $this->hasMany( + Item::class, + ['id' => 'item_id'] + )->via('orderItemsWithNullFK')->where(['category_id' => 1]); + } + + public function getBooksViaTableQuery(): ActiveQuery + { + return $this->hasMany( + Item::class, + ['id' => 'item_id'] + )->viaTable('order_item', ['order_id' => 'id'])->where(['category_id' => 1]); + } + + public function getBooksWithNullFKViaTableQuery(): ActiveQuery + { + return $this->hasMany( + Item::class, + ['id' => 'item_id'] + )->viaTable('order_item_with_null_fk', ['order_id' => 'id'])->where(['category_id' => 1]); + } + + public function getBooks2Query(): ActiveQuery + { + return $this->hasMany( + Item::class, + ['id' => 'item_id'] + )->onCondition(['category_id' => 1])->viaTable('order_item', ['order_id' => 'id']); + } + + public function getBooksExplicitQuery(): ActiveQuery + { + return $this->hasMany( + Item::class, + ['id' => 'item_id'] + )->onCondition(['category_id' => 1])->viaTable('order_item', ['order_id' => 'id']); + } + + public function getBooksExplicitAQuery(): ActiveQuery + { + return $this->hasMany( + Item::class, + ['id' => 'item_id'] + )->alias('bo')->onCondition(['bo.category_id' => 1])->viaTable('order_item', ['order_id' => 'id']); + } + + public function getBookItemsQuery(): ActiveQuery + { + return $this->hasMany( + Item::class, + ['id' => 'item_id'] + )->alias('books')->onCondition(['books.category_id' => 1])->viaTable('order_item', ['order_id' => 'id']); + } + + public function getMovieItemsQuery(): ActiveQuery + { + return $this->hasMany( + Item::class, + ['id' => 'item_id'] + )->alias('movies')->onCondition(['movies.category_id' => 2])->viaTable('order_item', ['order_id' => 'id']); + } + + public function getLimitedItemsQuery(): ActiveQuery + { + return $this->hasMany(Item::class, ['id' => 'item_id'])->onCondition(['item.id' => [3, 5]])->via('orderItems'); + } + + public function getExpensiveItemsUsingViaWithCallableQuery(): ActiveQuery + { + return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems', function (ActiveQuery $q) { + $q->where(['>=', 'subtotal', 10]); + }); + } + + public function getCheapItemsUsingViaWithCallableQuery(): ActiveQuery + { + return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItems', function (ActiveQuery $q) { + $q->where(['<', 'subtotal', 10]); + }); + } + + public function getOrderItemsFor8Query(): ActiveQuery + { + return $this->hasMany(OrderItemWithNullFK::class, ['order_id' => 'id'])->andOnCondition(['subtotal' => 8.0]); + } + + public function getItemsFor8Query(): ActiveQuery + { + return $this->hasMany(Item::class, ['id' => 'item_id'])->via('orderItemsFor8'); + } +} diff --git a/tests/Stubs/MagicActiveRecord/OrderItem.php b/tests/Stubs/MagicActiveRecord/OrderItem.php new file mode 100644 index 000000000..928685cc4 --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/OrderItem.php @@ -0,0 +1,63 @@ +getAttribute('order_id'); + $fields['item_id'] = $this->getAttribute('item_id'); + $fields['price'] = $this->getAttribute('subtotal') / $this->getAttribute('quantity'); + $fields['quantity'] = $this->getAttribute('quantity'); + $fields['subtotal'] = $this->getAttribute('subtotal'); + + return $fields; + } + + public function getOrderQuery(): ActiveQuery + { + return $this->hasOne(Order::class, ['id' => 'order_id']); + } + + public function getItemQuery(): ActiveQuery + { + return $this->hasOne(Item::class, ['id' => 'item_id']); + } + + public function getOrderItemCompositeWithJoinQuery(): ActiveQuery + { + /** relations used by testFindCompositeWithJoin() */ + return $this->hasOne(self::class, ['item_id' => 'item_id', 'order_id' => 'order_id' ])->joinWith('item'); + } + + public function getOrderItemCompositeNoJoinQuery(): ActiveQuery + { + return $this->hasOne(self::class, ['item_id' => 'item_id', 'order_id' => 'order_id' ]); + } + + public function getCustomQuery(): ActiveQuery + { + return new ActiveQuery(Order::class, $this->db()); + } +} diff --git a/tests/Stubs/MagicActiveRecord/OrderItemWithNullFK.php b/tests/Stubs/MagicActiveRecord/OrderItemWithNullFK.php new file mode 100644 index 000000000..9247dc417 --- /dev/null +++ b/tests/Stubs/MagicActiveRecord/OrderItemWithNullFK.php @@ -0,0 +1,23 @@ +