Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/composer-require-checker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,3 @@ jobs:
['8.1', '8.2', '8.3', '8.4', '8.5']
required-packages: >-
['db']
composer-root-version: 2.0.0
1 change: 0 additions & 1 deletion .github/workflows/db-mssql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ jobs:
- name: Install required yiisoft/db and yiisoft/db-mssql
uses: yiisoft/actions/install-packages@master
with:
composer-root-version: 2.0.0
packages: >-
['db', 'db-mssql']

Expand Down
1 change: 0 additions & 1 deletion .github/workflows/db-mysql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ jobs:
- name: Install required yiisoft/db and yiisoft/db-mysql
uses: yiisoft/actions/install-packages@master
with:
composer-root-version: 2.0.0
packages: >-
['db', 'db-mysql']

Expand Down
1 change: 0 additions & 1 deletion .github/workflows/db-oracle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ jobs:
- name: Install required yiisoft/db and yiisoft/db-oracle
uses: yiisoft/actions/install-packages@master
with:
composer-root-version: 2.0.0
packages: >-
['db', 'db-oracle']

Expand Down
1 change: 0 additions & 1 deletion .github/workflows/db-pgsql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ jobs:
- name: Install required yiisoft/db and yiisoft/db-pgsql
uses: yiisoft/actions/install-packages@master
with:
composer-root-version: 2.0.1
packages: >-
['db', 'db-pgsql']

Expand Down
1 change: 0 additions & 1 deletion .github/workflows/db-sqlite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ jobs:
- name: Install required yiisoft/db and yiisoft/db-sqlite
uses: yiisoft/actions/install-packages@master
with:
composer-root-version: 2.0.0
packages: >-
['db', 'db-sqlite']

Expand Down
1 change: 0 additions & 1 deletion .github/workflows/mutation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ jobs:
- name: Install required yiisoft/db and yiisoft/db-pgsql
uses: yiisoft/actions/install-packages@master
with:
composer-root-version: 2.0.1
packages: >-
['db', 'db-pgsql']

Expand Down
1 change: 0 additions & 1 deletion .github/workflows/rector-cs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,3 @@ jobs:
php: '8.1'
required-packages: >-
['db']
composer-root-version: 2.0.0
1 change: 0 additions & 1 deletion .github/workflows/static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,3 @@ jobs:
['8.1', '8.2', '8.3', '8.4']
required-packages: >-
['db']
composer-root-version: 2.0.0
29 changes: 6 additions & 23 deletions docs/create-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,34 +231,17 @@ final class User extends ActiveRecord
public ?int $id;

public function __construct(
public ?string $username = null,
public ?string $email = null,
public string $username,
public string $email,
public string $status = 'active',
) {}
}
```

### Limitations

When using the constructor, you should either specify default values or `null` for the arguments, or avoid using the static
`ActiveRecord::query()` method. It will not work correctly. Instead, create a new model instance and create a new query
object by calling the `createQuery()` method on the model instance.

```php
// If the constructor arguments do not have default values
$user = new User('admin', 'admin@example.net', 'active');
/** @var Yiisoft\ActiveRecord\ActiveQueryInterface $query */
$query = $user->createQuery();
```

Then you can use the active query object as usual, for example:

```php
$users = $query->where(['status' => 'active'])->all();
```

Also, if the constructor arguments do not have default values, you cannot use `RepositoryTrait`, because it uses static
`ActiveRecord::query()` method.
> [!IMPORTANT]
> When using `ActiveRecord::query()` method to create a query or `RepositoryTrait` methods, the constructor is not called.
> If you need to call the constructor, override the `ActiveRecord::instantiate()` method to create a new instance with
> calling the constructor. For example, `return new static();`.
Comment thread
Tigrov marked this conversation as resolved.
Outdated

## Relations

Expand Down
5 changes: 5 additions & 0 deletions src/AbstractActiveRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@

public function equals(ActiveRecordInterface $record): bool
{
if ($this->isNew() || $record->isNew()) {

Check warning on line 84 in src/AbstractActiveRecord.php

View workflow job for this annotation

GitHub Actions / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "LogicalOr": @@ @@ public function equals(ActiveRecordInterface $record): bool { - if ($this->isNew() || $record->isNew()) { + if ($this->isNew() && $record->isNew()) { return false; }
return false;
}

Expand Down Expand Up @@ -148,7 +148,7 @@
return match (count($keys)) {
1 => $this->oldValues[$keys[0]] ?? null,
0 => throw new LogicException(
static::class . ' does not have a primary key. You should either define a primary key for '

Check warning on line 151 in src/AbstractActiveRecord.php

View workflow job for this annotation

GitHub Actions / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ 1 => $this->oldValues[$keys[0]] ?? null, 0 => throw new LogicException( static::class . ' does not have a primary key. You should either define a primary key for ' - . $this->tableName() . ' table or override the primaryKey() method.', + . $this->tableName(), ), default => throw new LogicException( static::class . ' has multiple primary keys. Use primaryKeyOldValues() method instead.',

Check warning on line 151 in src/AbstractActiveRecord.php

View workflow job for this annotation

GitHub Actions / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "Concat": @@ @@ return match (count($keys)) { 1 => $this->oldValues[$keys[0]] ?? null, 0 => throw new LogicException( - static::class . ' does not have a primary key. You should either define a primary key for ' - . $this->tableName() . ' table or override the primaryKey() method.', + static::class . ' does not have a primary key. You should either define a primary key for ' . ' table or override the primaryKey() method.' . $this->tableName(), ), default => throw new LogicException( static::class . ' has multiple primary keys. Use primaryKeyOldValues() method instead.',

Check warning on line 151 in src/AbstractActiveRecord.php

View workflow job for this annotation

GitHub Actions / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ return match (count($keys)) { 1 => $this->oldValues[$keys[0]] ?? null, 0 => throw new LogicException( - static::class . ' does not have a primary key. You should either define a primary key for ' - . $this->tableName() . ' table or override the primaryKey() method.', + static::class . ' does not have a primary key. You should either define a primary key for ' . ' table or override the primaryKey() method.', ), default => throw new LogicException( static::class . ' has multiple primary keys. Use primaryKeyOldValues() method instead.',
. $this->tableName() . ' table or override the primaryKey() method.',
),
default => throw new LogicException(
Expand All @@ -163,7 +163,7 @@

if (empty($keys)) {
throw new LogicException(
static::class . ' does not have a primary key. You should either define a primary key for '

Check warning on line 166 in src/AbstractActiveRecord.php

View workflow job for this annotation

GitHub Actions / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ if (empty($keys)) { throw new LogicException( static::class . ' does not have a primary key. You should either define a primary key for ' - . $this->tableName() . ' table or override the primaryKey() method.', + . $this->tableName(), ); }

Check warning on line 166 in src/AbstractActiveRecord.php

View workflow job for this annotation

GitHub Actions / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "Concat": @@ @@ if (empty($keys)) { throw new LogicException( - static::class . ' does not have a primary key. You should either define a primary key for ' - . $this->tableName() . ' table or override the primaryKey() method.', + static::class . ' does not have a primary key. You should either define a primary key for ' . ' table or override the primaryKey() method.' . $this->tableName(), ); }

Check warning on line 166 in src/AbstractActiveRecord.php

View workflow job for this annotation

GitHub Actions / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ if (empty($keys)) { throw new LogicException( - static::class . ' does not have a primary key. You should either define a primary key for ' - . $this->tableName() . ' table or override the primaryKey() method.', + static::class . ' does not have a primary key. You should either define a primary key for ' . ' table or override the primaryKey() method.', ); }
. $this->tableName() . ' table or override the primaryKey() method.',
);
}
Expand All @@ -188,7 +188,7 @@
return match (count($keys)) {
1 => $this->get($keys[0]),
0 => throw new LogicException(
static::class . ' does not have a primary key. You should either define a primary key for '

Check warning on line 191 in src/AbstractActiveRecord.php

View workflow job for this annotation

GitHub Actions / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ 1 => $this->get($keys[0]), 0 => throw new LogicException( static::class . ' does not have a primary key. You should either define a primary key for ' - . $this->tableName() . ' table or override the primaryKey() method.', + . $this->tableName(), ), default => throw new LogicException( static::class . ' has multiple primary keys. Use primaryKeyValues() method instead.',

Check warning on line 191 in src/AbstractActiveRecord.php

View workflow job for this annotation

GitHub Actions / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "Concat": @@ @@ return match (count($keys)) { 1 => $this->get($keys[0]), 0 => throw new LogicException( - static::class . ' does not have a primary key. You should either define a primary key for ' - . $this->tableName() . ' table or override the primaryKey() method.', + static::class . ' does not have a primary key. You should either define a primary key for ' . ' table or override the primaryKey() method.' . $this->tableName(), ), default => throw new LogicException( static::class . ' has multiple primary keys. Use primaryKeyValues() method instead.',

Check warning on line 191 in src/AbstractActiveRecord.php

View workflow job for this annotation

GitHub Actions / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ return match (count($keys)) { 1 => $this->get($keys[0]), 0 => throw new LogicException( - static::class . ' does not have a primary key. You should either define a primary key for ' - . $this->tableName() . ' table or override the primaryKey() method.', + static::class . ' does not have a primary key. You should either define a primary key for ' . ' table or override the primaryKey() method.', ), default => throw new LogicException( static::class . ' has multiple primary keys. Use primaryKeyValues() method instead.',
. $this->tableName() . ' table or override the primaryKey() method.',
),
default => throw new LogicException(
Expand Down Expand Up @@ -243,6 +243,11 @@
$this->insertInternal($properties);
}

public static function instantiate(): static
{
return (new ReflectionClass(static::class))->newInstanceWithoutConstructor();
}

public function isChanged(): bool
{
return !empty($this->newValues());
Expand Down
2 changes: 1 addition & 1 deletion src/ActiveQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ final public function __construct(
) {
$this->model = $modelClass instanceof ActiveRecordInterface
? $modelClass
: new $modelClass();
: $modelClass::instantiate();

parent::__construct($this->model->db());
}
Expand Down
7 changes: 7 additions & 0 deletions src/ActiveRecordInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,13 @@ public function hasOne(self|string $modelClass, array $link): ActiveQueryInterfa
*/
public function insert(?array $properties = null): void;

/**
* Creates a new instance of the active record class.
* The method is used by {@see ActiveQuery} class and {@see EventsTrait} trait when an active record model passed
* as a string class name. Usually, it happens when calling {@see ActiveRecordInterface::query()} method.
*/
public static function instantiate(): static;

/**
* Checks if any property returned by {@see ActiveRecordInterface::propertyNames()} method has changed.
* A new active record instance is considered changed if any property has been set including default values.
Expand Down
2 changes: 1 addition & 1 deletion src/Trait/EventsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public static function query(ActiveRecordInterface|string|null $modelClass = nul
{
$model = $modelClass instanceof ActiveRecordInterface
? $modelClass
: new ($modelClass ?? static::class)();
: ($modelClass !== null ? $modelClass::instantiate() : static::instantiate());

$eventDispatcher = EventDispatcherProvider::get($model::class);
$eventDispatcher->dispatch($event = new BeforeCreateQuery($model));
Expand Down
29 changes: 17 additions & 12 deletions tests/ActiveRecordTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\NullValues;
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Order;
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\OrderItem;
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\OrderItemWithConstructor;
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\OrderItemWithNullFK;
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\OrderWithConstructor;
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\OrderWithFactory;
Expand Down Expand Up @@ -1070,10 +1071,9 @@ public function testWithFactoryNonInitiated(): void

$this->assertInstanceOf(Customer::class, $customer);

$this->expectException(ArgumentCountError::class);
$this->expectExceptionMessage('Too few arguments to function');
$customer = $order->getCustomerWithFactory();

$order->getCustomerWithFactory();
$this->assertInstanceOf(Customer::class, $customer);
}

public function testSerialization(): void
Expand Down Expand Up @@ -1942,26 +1942,31 @@ public function testGetAllWithHasOneAndArrayValue(): void

public function testWithConstructorQuery(): void
{
$this->expectException(ArgumentCountError::class);
$this->expectExceptionMessage('Too few arguments to function');
$orders = OrderWithConstructor::query()->all();

$this->assertCount(3, $orders);

$orderItems = OrderItemWithConstructor::query()->all();

OrderWithConstructor::query()->all();
$this->assertCount(6, $orderItems);
}

public function testWithConstructorRelations(): void
{
$this->expectException(ArgumentCountError::class);
$this->expectExceptionMessage('Too few arguments to function');
$orderItems = (new OrderWithConstructor(1))->createQuery()->findByPk(1)->getOrderItems();

(new OrderWithConstructor(1))->createQuery()->findByPk(1)->getOrderItems();
$this->assertCount(2, $orderItems);
}

public function testWithConstructorRepositoryTrait(): void
{
$this->expectException(ArgumentCountError::class);
$this->expectExceptionMessage('Too few arguments to function');
$orders = OrderWithConstructor::findAll();

$this->assertCount(3, $orders);

$order = OrderWithConstructor::findByPk(1);

OrderWithConstructor::findAll();
$this->assertSame(1, $order->getId());
}

public function testWithConstructorNewInstance(): void
Expand Down
Loading