diff --git a/README.md b/README.md index 8d3311d69..d02169f2c 100644 --- a/README.md +++ b/README.md @@ -675,7 +675,7 @@ $salary->where('emp_no', $employees); // join with another table for more data $salary - ->join('employees.emp_id', 'emp_id') + ->join('employees.emp_id') ->field('employees.first_name'); // finally, fetch result diff --git a/docs/advanced.md b/docs/advanced.md index 4abde6cbd..939bebe49 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -143,12 +143,12 @@ protected function init(): void $this->getOwner()->addField('created_dts', ['type' => 'datetime', 'default' => new \DateTime()]); - $this->getOwner()->hasOne('created_by_user_id', 'User'); + $this->getOwner()->hasOne('created_by_user_id', ['model' => [User::class]]); if (isset($this->getApp()->user) && $this->getApp()->user->isLoaded()) { $this->getOwner()->getField('created_by_user_id')->default = $this->getApp()->user->getId(); } - $this->getOwner()->hasOne('updated_by_user_id', 'User'); + $this->getOwner()->hasOne('updated_by_user_id', ['model' => [User::class]]); $this->getOwner()->addField('updated_dts', ['type' => 'datetime']); @@ -457,8 +457,8 @@ class Model_InvoicePayment extends \Atk4\Data\Model { parent::init(); - $this->hasOne('invoice_id', 'Model_Invoice'); - $this->hasOne('payment_id', 'Model_Payment'); + $this->hasOne('invoice_id', ['model' => [Model_Invoice::class]]); + $this->hasOne('payment_id', ['model' => [Model_Payment::class]]); $this->addField('amount_closed'); } } @@ -469,13 +469,13 @@ class Model_InvoicePayment extends \Atk4\Data\Model Next we need to define reference. Inside Model_Invoice add: ``` -$this->hasMany('InvoicePayment'); +$this->hasMany('InvoicePayment', ['model' => [Model_InvoicePayment::class]]); $this->hasMany('Payment', ['model' => function (self $m) { $p = new Model_Payment($m->getPersistence()); $j = $p->join('invoice_payment.payment_id'); $j->addField('amount_closed'); - $j->hasOne('invoice_id', 'Model_Invoice'); + $j->hasOne('invoice_id', ['model' => [Model_Invoice::class]]); }, 'theirField' => 'invoice_id']); $this->onHookShort(Model::HOOK_BEFORE_DELETE, function () { @@ -506,7 +506,7 @@ that shows how much amount is closed and `amount_due`: ``` // define field to see closed amount on invoice -$this->hasMany('InvoicePayment') +$this->hasMany('InvoicePayment', ['model' => [Model_InvoicePayment::class]]) ->addField('total_payments', ['aggregate' => 'sum', 'field' => 'amount_closed']); $this->addExpression('amount_due', ['expr' => '[total] - coalesce([total_payments], 0)']); ``` @@ -574,7 +574,7 @@ class Model_Invoice extends \Atk4\Data\Model ... - $this->hasOne('category_id', 'Model_Category'); + $this->hasOne('category_id', ['model' => [Model_Category::class]]); ... } @@ -711,7 +711,7 @@ In theory Document's 'contact_id' can be any Contact, however when you create define Model_Document: ``` -$this->hasOne('client_id', 'Model_Contact'); +$this->hasOne('client_id', ['model' => [Model_Contact::class]]); ``` One option here is to move 'Model_Contact' into model property, which will be @@ -743,7 +743,7 @@ add 'payment_invoice_id' that points to 'Model_Payment'. However we want this field only to offer payments made by the same client. Inside Model_Invoice add: ``` -$this->hasOne('client_id', 'Client'); +$this->hasOne('client_id', ['model' => [Model_Client::class]]); $this->hasOne('payment_invoice_id', ['model' => function (self $m) { return $m->ref('client_id')->ref('Payment'); @@ -773,7 +773,7 @@ Agile Data allow you to define multiple references between same entities, but sometimes that can be quite useful. Consider adding this inside your Model_Contact: ``` -$this->hasMany('Invoice', 'Model_Invoice'); +$this->hasMany('Invoice', ['model' => [Model_Invoice::class]]); $this->hasMany('OverdueInvoice', ['model' => function (self $m) { return $m->ref('Invoice')->addCondition('due', '<', date('Y-m-d')) }]); diff --git a/docs/joins.md b/docs/joins.md index c5eeba5d7..6d1e2a746 100644 --- a/docs/joins.md +++ b/docs/joins.md @@ -23,7 +23,7 @@ $user->addField('username'); $jContact = $user->join('contact'); $jContact->addField('address'); $jContact->addField('county'); -$jContact->hasOne('Country'); +$jContact->hasOne('Country', ['model' => [Country::class]]); ``` This code will load data from two tables simultaneously and if you do change any diff --git a/docs/model.md b/docs/model.md index 8b9d5187a..862d65027 100644 --- a/docs/model.md +++ b/docs/model.md @@ -122,7 +122,8 @@ $model->addField('secret', ['neverPersist' => true]); $model->addField('old_field', ['actual' => 'new_field']); // or even into a different table -$model->join('new_table')->addField('extra_field'); +$model->join('new_table') + ->addField('extra_field'); ``` Model also has a property `$table`, which indicate name of default table/collection/file to be diff --git a/docs/persistence.md b/docs/persistence.md index 70a57c756..1f6a04c38 100644 --- a/docs/persistence.md +++ b/docs/persistence.md @@ -298,7 +298,7 @@ protected function init(): void $this->getOwner()->hasOne( $f . '_currency_id', [ - 'model' => $this->currency_model ?? new Currency(), + 'model' => [Currency::class], 'system' => true, ] ); @@ -783,7 +783,7 @@ In conjunction with Model::refLink() you can produce expressions for creating sub-selects. The functionality is nicely wrapped inside HasMany::addField(): ``` -$client->hasMany('Invoice') +$client->hasMany('Invoice', ['model' => [Model_Invoice::class]]) ->addField('total_gross', ['aggregate' => 'sum', 'field' => 'gross']); ``` @@ -798,7 +798,7 @@ This operation is actually consisting of 3 following operations: Here is a way how to intervene with the process: ``` -$client->hasMany('Invoice'); +$client->hasMany('Invoice', ['model' => [Model_Invoice::class]]); $client->addExpression('last_sale', ['expr' => function (Model $m) { return $m->refLink('Invoice') ->setOrder('date desc') diff --git a/docs/persistence/sql/queries.md b/docs/persistence/sql/queries.md index 09c6fbf62..179cd81c0 100644 --- a/docs/persistence/sql/queries.md +++ b/docs/persistence/sql/queries.md @@ -541,34 +541,6 @@ $q->join('address.user_id'); // address.user_id = id // JOIN `address` ON `address`.`user_id`=`id` ``` -You can also pass array as a first argument, to join multiple tables: - -``` -$q->table('user u'); -$q->join(['a' => 'address', 'c' => 'credit_card', 'preferences']); -``` - -The above code will join 3 tables using the following query syntax: - -```sql -join - address as a on a.id = u.address_id - credit_card as c on c.id = u.credit_card_id - preferences on preferences.id = u.preferences_id -``` - -However normally you would have `user_id` field defined in your supplementary -tables so you need a different syntax: - -``` -$q->table('user u'); -$q->join([ - 'a' => 'address.user_id', - 'c' => 'credit_card.user_id', - 'preferences.user_id', -]); -``` - The second argument to join specifies which existing table/field is used in `on` condition: diff --git a/docs/persistence/sql/quickstart.md b/docs/persistence/sql/quickstart.md index e5d3fab39..61ca5b59c 100644 --- a/docs/persistence/sql/quickstart.md +++ b/docs/persistence/sql/quickstart.md @@ -98,7 +98,7 @@ $salary->where('emp_no', $employees); // join with another table for more data $salary - ->join('employees.emp_id', 'emp_id') + ->join('employees.emp_id') ->field('employees.first_name'); // finally, fetch result diff --git a/docs/quickstart.md b/docs/quickstart.md index ddccbc875..efbe2973a 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -17,7 +17,8 @@ unions but only if the database server supports those operations. Developer would normally create a declaration like this: ``` -$user->hasMany('Order')->addField('total', ['aggregate' => 'sum']); +$user->hasMany('Order', ['model' => [Order::class]]) + ->addField('total', ['aggregate' => 'sum']); ``` It is up to Agile Data to decide what's the most efficient way to implement @@ -273,7 +274,7 @@ class Model_User extends Model $j->addField('address_1'); $j->addField('address_2'); $j->addField('address_3'); - $j->hasOne('country_id', 'Country'); + $j->hasOne('country_id', ['model' => [Model_Country::class]]); } } ``` @@ -441,7 +442,7 @@ As per our database design - one user can have multiple 'system' records: ``` $m = new Model_User($db); -$m->hasMany('System'); +$m->hasMany('System', ['model' => [Model_System::class]]); ``` Next you can load a specific user and traverse into System model: diff --git a/docs/references.md b/docs/references.md index db6a02f35..a4e4e7235 100644 --- a/docs/references.md +++ b/docs/references.md @@ -118,7 +118,7 @@ There are several ways how to link models with hasMany: ``` $m->hasMany('Orders', ['model' => [Model_Order::class]]); // using seed -$m->hasMany('Order', ['model' => function (Model $m, $r) { // using callback +$m->hasMany('Order', ['model' => static function (Model $m, Reference $r, array $defaults) { // using callback return new Model_Order(); }]); ``` @@ -154,10 +154,7 @@ available. Both models will relate through `currency.code = exchange.currency_co ``` $c = new Model_Currency(); -$e = new Model_ExchangeRate(); - -$c->hasMany('Exchanges', ['model' => $e, 'theirField' => 'currency_code', 'ourField' => 'code']); - +$c->hasMany('Exchanges', ['model' => [Model_ExchangeRate::class], 'theirField' => 'currency_code', 'ourField' => 'code']); $c->addCondition('is_convertible', true); $e = $c->ref('Exchanges'); ``` diff --git a/docs/typecasting.md b/docs/typecasting.md index 9ae97bff5..903e1041a 100644 --- a/docs/typecasting.md +++ b/docs/typecasting.md @@ -99,8 +99,6 @@ Many databases will allow you to use different types for ID fields. In SQL the 'id' column will usually be "integer", but sometimes it can be of a different type. -The same applies for references ($m->hasOne()). - ### Supported types - 'string' - for storing short strings, such as name of a person. Normalize will trim the value. diff --git a/src/Model/Join.php b/src/Model/Join.php index 1a6c37360..0db351fc5 100644 --- a/src/Model/Join.php +++ b/src/Model/Join.php @@ -54,7 +54,7 @@ abstract class Join * * If you are using the following syntax: * - * $user->join('contact', 'default_contact_id') + * $user->join('contact.default_contact_id') * * Then the ID connecting tables is stored in foreign table and the order * of saving and delete needs to be reversed. In this case $reverse @@ -371,7 +371,7 @@ public function leftJoin(string $foreignTable, array $defaults = []): self * * @return Reference\HasOne */ - public function hasOne(string $link, array $defaults = []) + public function hasOne(string $link, array $defaults): Reference { $defaults['joinName'] = $this->getJoinNameFromShortName(); @@ -385,7 +385,7 @@ public function hasOne(string $link, array $defaults = []) * * @return Reference\HasMany */ - public function hasMany(string $link, array $defaults = []) + public function hasMany(string $link, array $defaults): Reference { return $this->getOwner()->hasMany($link, $defaults); } @@ -400,7 +400,7 @@ public function hasMany(string $link, array $defaults = []) * * @return Reference\ContainsOne *X/ - public function containsOne(string $link, array $defaults = []) // : Reference + public function containsOne(string $link, array $defaults): Reference { $defaults['joinName'] = $this->getJoinNameFromShortName(); @@ -416,7 +416,7 @@ public function containsOne(string $link, array $defaults = []) // : Reference * * @return Reference\ContainsMany *X/ - public function containsMany(string $link, array $defaults = []) // : Reference + public function containsMany(string $link, array $defaults): Reference { return $this->getOwner()->containsMany($link, $defaults); } diff --git a/src/Model/ReferencesTrait.php b/src/Model/ReferencesTrait.php index 611fa9c78..fc1b20505 100644 --- a/src/Model/ReferencesTrait.php +++ b/src/Model/ReferencesTrait.php @@ -32,7 +32,7 @@ trait ReferencesTrait * @param array $seed * @param array $defaults */ - protected function _addReference(array $seed, string $link, array $defaults = []): Reference + protected function _addReference(array $seed, string $link, array $defaults): Reference { $this->assertIsModel(); @@ -69,7 +69,7 @@ public function addReference(string $link, array $defaults): Reference * * @return Reference\HasOne|Reference\HasOneSql */ - public function hasOne(string $link, array $defaults = []) // : Reference + public function hasOne(string $link, array $defaults): Reference { return $this->_addReference($this->_defaultSeedHasOne, $link, $defaults); // @phpstan-ignore-line } @@ -81,7 +81,7 @@ public function hasOne(string $link, array $defaults = []) // : Reference * * @return Reference\HasMany */ - public function hasMany(string $link, array $defaults = []) // : Reference + public function hasMany(string $link, array $defaults): Reference { return $this->_addReference($this->_defaultSeedHasMany, $link, $defaults); // @phpstan-ignore-line } @@ -93,7 +93,7 @@ public function hasMany(string $link, array $defaults = []) // : Reference * * @return Reference\ContainsOne */ - public function containsOne(string $link, array $defaults = []) // : Reference + public function containsOne(string $link, array $defaults): Reference { return $this->_addReference($this->_defaultSeedContainsOne, $link, $defaults); // @phpstan-ignore-line } @@ -105,7 +105,7 @@ public function containsOne(string $link, array $defaults = []) // : Reference * * @return Reference\ContainsMany */ - public function containsMany(string $link, array $defaults = []) // : Reference + public function containsMany(string $link, array $defaults): Reference { return $this->_addReference($this->_defaultSeedContainsMany, $link, $defaults); // @phpstan-ignore-line } diff --git a/src/Persistence/Sql/Query.php b/src/Persistence/Sql/Query.php index bb885e79d..f93e36b1a 100644 --- a/src/Persistence/Sql/Query.php +++ b/src/Persistence/Sql/Query.php @@ -291,7 +291,8 @@ protected function _renderWith(): ?string * $q->join('address.code', 'user.code', 'inner'); * * You can use expression for more complex joins - * $q->join('address', + * $q->join( + * 'address', * $q->orExpr() * ->where('user.billing_id', 'address.id') * ->where('user.technical_id', 'address.id') diff --git a/src/Reference.php b/src/Reference.php index 5798d2e8f..ec28001b7 100644 --- a/src/Reference.php +++ b/src/Reference.php @@ -5,7 +5,6 @@ namespace Atk4\Data; use Atk4\Core\DiContainerTrait; -use Atk4\Core\Factory; use Atk4\Core\InitializerTrait; use Atk4\Core\TrackableTrait; @@ -263,16 +262,16 @@ protected function createTheirModelBeforeInit(array $defaults): Model } if (is_object($m)) { - $theirModel = Factory::factory(clone $m, $defaults); + $theirModelSeed = clone $m; } else { - $modelDefaults = $m; - $theirModelSeed = [$modelDefaults[0]]; - unset($modelDefaults[0]); - $defaults = array_merge($modelDefaults, $defaults); - - $theirModel = Factory::factory($theirModelSeed, $defaults); + \Closure::bind(static fn () => Model::_fromSeedPrecheck($m, false), null, Model::class)(); + $theirModelSeed = [$m[0]]; + unset($m[0]); + $defaults = array_merge($m, $defaults); } + $theirModel = Model::fromSeed($theirModelSeed, $defaults); + return $theirModel; } diff --git a/src/Reference/HasOneSql.php b/src/Reference/HasOneSql.php index 39bada075..1159d5be2 100644 --- a/src/Reference/HasOneSql.php +++ b/src/Reference/HasOneSql.php @@ -28,8 +28,7 @@ private function _addField(string $fieldName, bool $theirFieldIsTitle, ?string $ return $theirModel->action('field', [$theirFieldName]); }, ], $defaults, [ - // allow to set our field value by an imported foreign field, but only when - // the our field value is null + // allow to set our field value by an imported foreign field, but only when the our field value is null 'readOnly' => false, ])); @@ -159,7 +158,8 @@ public function refLink(array $defaults = []): Model /** * Add a title of related entity as expression to our field. * - * $order->hasOne('user_id', 'User')->addTitle(); + * $order->hasOne('user_id', ['model' => [User::class]]) + * ->addTitle(); * * This will add expression 'user' equal to ref('user_id')['name']; * diff --git a/src/Type/LocalObjectHandle.php b/src/Type/LocalObjectHandle.php index d2d8c1825..f314ecdd3 100644 --- a/src/Type/LocalObjectHandle.php +++ b/src/Type/LocalObjectHandle.php @@ -4,6 +4,9 @@ namespace Atk4\Data\Type; +/** + * @internal + */ class LocalObjectHandle { private int $localUid; diff --git a/tests/ConditionSqlTest.php b/tests/ConditionSqlTest.php index 847f45ccd..3701edc31 100644 --- a/tests/ConditionSqlTest.php +++ b/tests/ConditionSqlTest.php @@ -306,7 +306,8 @@ public function testExpressionJoin(): void $m->addField('gender'); $m->addField('surname'); - $m->join('contact')->addField('contact_phone'); + $m->join('contact') + ->addField('contact_phone'); $mm2 = $m->load(1); self::assertSame('John', $mm2->get('name')); diff --git a/tests/ModelNestedArrayTest.php b/tests/ModelNestedArrayTest.php index 0232635cc..acde1463e 100644 --- a/tests/ModelNestedArrayTest.php +++ b/tests/ModelNestedArrayTest.php @@ -12,7 +12,7 @@ class ModelNestedArrayTest extends TestCase { /** @var list}> */ - public array $hookLog = []; + public array $hookLogs = []; #[\Override] protected function setUp(): void @@ -60,7 +60,7 @@ protected function convertValueToLog($v) public function hook(string $spot, array $args = [], HookBreaker &$brokenBy = null) { if (!str_starts_with($spot, '__atk4__dynamic_method__') && $spot !== Model::HOOK_NORMALIZE) { - $this->testCaseWeakRef->get()->hookLog[] = [$this->convertValueToLog($this), $spot, $this->convertValueToLog($args)]; + $this->testCaseWeakRef->get()->hookLogs[] = [$this->convertValueToLog($this), $spot, $this->convertValueToLog($args)]; } return parent::hook($spot, $args, $brokenBy); @@ -69,11 +69,11 @@ public function hook(string $spot, array $args = [], HookBreaker &$brokenBy = nu #[\Override] public function atomic(\Closure $fx) { - $this->testCaseWeakRef->get()->hookLog[] = [$this->convertValueToLog($this), '>>>']; + $this->testCaseWeakRef->get()->hookLogs[] = [$this->convertValueToLog($this), '>>>']; $res = parent::atomic($fx); - $this->testCaseWeakRef->get()->hookLog[] = [$this->convertValueToLog($this), '<<<']; + $this->testCaseWeakRef->get()->hookLogs[] = [$this->convertValueToLog($this), '<<<']; return $res; } @@ -114,7 +114,7 @@ public function testSelectExport(): void ['name' => 'Sue', 'birthday' => new \DateTime('2005-4-3 UTC')], ], $m->export()); - self::assertSame([], $this->hookLog); + self::assertSame([], $this->hookLogs); } public function testInsert(): void @@ -146,7 +146,7 @@ public function testInsert(): void ['main', Model::HOOK_AFTER_LOAD, []], ['main', Model::HOOK_AFTER_SAVE, [false]], ['main', '<<<'], - ], $this->hookLog); + ], $this->hookLogs); self::assertSame(4, $m->table->loadBy('name', 'Karl')->getId()); self::assertSameExportUnordered([[new \DateTime('2000-6-1 UTC')]], [[$entity->getId()]]); @@ -199,7 +199,7 @@ public function testUpdate(): void ['main', Model::HOOK_AFTER_SAVE, [true]], ['main', '<<<'], - ], $this->hookLog); + ], $this->hookLogs); self::assertSameExportUnordered([ ['name' => 'John', 'birthday' => new \DateTime('1980-2-1 UTC')], @@ -231,7 +231,7 @@ public function testDelete(): void ['main', '<<<'], ['main', Model::HOOK_BEFORE_UNLOAD, []], ['main', Model::HOOK_AFTER_UNLOAD, []], - ], $this->hookLog); + ], $this->hookLogs); self::assertSameExportUnordered([ ['name' => 'John', 'birthday' => new \DateTime('1980-2-1 UTC')], diff --git a/tests/ModelNestedSqlTest.php b/tests/ModelNestedSqlTest.php index b3f92f123..42f7c63d1 100644 --- a/tests/ModelNestedSqlTest.php +++ b/tests/ModelNestedSqlTest.php @@ -13,7 +13,7 @@ class ModelNestedSqlTest extends TestCase { /** @var list}> */ - public array $hookLog = []; + public array $hookLogs = []; #[\Override] protected function setUp(): void @@ -61,7 +61,7 @@ protected function convertValueToLog($v) public function hook(string $spot, array $args = [], HookBreaker &$brokenBy = null) { if (!str_starts_with($spot, '__atk4__dynamic_method__') && $spot !== Model::HOOK_NORMALIZE) { - $this->testCaseWeakRef->get()->hookLog[] = [$this->convertValueToLog($this), $spot, $this->convertValueToLog($args)]; + $this->testCaseWeakRef->get()->hookLogs[] = [$this->convertValueToLog($this), $spot, $this->convertValueToLog($args)]; } return parent::hook($spot, $args, $brokenBy); @@ -70,11 +70,11 @@ public function hook(string $spot, array $args = [], HookBreaker &$brokenBy = nu #[\Override] public function atomic(\Closure $fx) { - $this->testCaseWeakRef->get()->hookLog[] = [$this->convertValueToLog($this), '>>>']; + $this->testCaseWeakRef->get()->hookLogs[] = [$this->convertValueToLog($this), '>>>']; $res = parent::atomic($fx); - $this->testCaseWeakRef->get()->hookLog[] = [$this->convertValueToLog($this), '<<<']; + $this->testCaseWeakRef->get()->hookLogs[] = [$this->convertValueToLog($this), '<<<']; return $res; } @@ -135,7 +135,7 @@ public function testSelectSql(): void self::assertSame([ ['inner', Persistence\Sql::HOOK_INIT_SELECT_QUERY, [Query::class, 'select']], ['main', Persistence\Sql::HOOK_INIT_SELECT_QUERY, [Query::class, 'select']], - ], $this->hookLog); + ], $this->hookLogs); } public function testSelectExport(): void @@ -150,7 +150,7 @@ public function testSelectExport(): void self::assertSame([ ['inner', Persistence\Sql::HOOK_INIT_SELECT_QUERY, [Query::class, 'select']], ['main', Persistence\Sql::HOOK_INIT_SELECT_QUERY, [Query::class, 'select']], - ], $this->hookLog); + ], $this->hookLogs); } public function testInsert(): void @@ -187,7 +187,7 @@ public function testInsert(): void ['main', Model::HOOK_AFTER_LOAD, []], ['main', Model::HOOK_AFTER_SAVE, [false]], ['main', '<<<'], - ], $this->hookLog); + ], $this->hookLogs); self::assertSame(4, $m->table->loadBy('name', 'Karl')->getId()); self::assertSameExportUnordered([[new \DateTime('2000-6-1 UTC')]], [[$entity->getId()]]); @@ -249,7 +249,7 @@ public function testUpdate(): void ['main', Model::HOOK_AFTER_SAVE, [true]], ['main', '<<<'], - ], $this->hookLog); + ], $this->hookLogs); self::assertSameExportUnordered([ ['name' => 'John', 'birthday' => new \DateTime('1980-2-1 UTC')], @@ -287,7 +287,7 @@ public function testDelete(): void ['main', '<<<'], ['main', Model::HOOK_BEFORE_UNLOAD, []], ['main', Model::HOOK_AFTER_UNLOAD, []], - ], $this->hookLog); + ], $this->hookLogs); self::assertSameExportUnordered([ ['name' => 'John', 'birthday' => new \DateTime('1980-2-1 UTC')], diff --git a/tests/ModelWithCteTest.php b/tests/ModelWithCteTest.php index 523926e45..85003e6fa 100644 --- a/tests/ModelWithCteTest.php +++ b/tests/ModelWithCteTest.php @@ -36,9 +36,9 @@ public function testWith(): void $mInvoice->addCondition('net', '>', 100); $m = clone $mUser; - $m->addCteModel('i', $mInvoice); // add cursor - $jInvoice = $m->join('i.user_id'); // join cursor - $jInvoice->addField('invoiced', ['type' => 'integer', 'actual' => 'net']); // add field from joined cursor + $m->addCteModel('i', $mInvoice); + $jInvoice = $m->join('i.user_id'); + $jInvoice->addField('invoiced', ['type' => 'integer', 'actual' => 'net']); // add field from joined CTE $this->assertSameSql( 'with `i` as (select `id`, `net`, `user_id` from `invoice` where `net` > :a)' . "\n" diff --git a/tests/RandomTest.php b/tests/RandomTest.php index aa5a02228..d31b5cf82 100644 --- a/tests/RandomTest.php +++ b/tests/RandomTest.php @@ -358,9 +358,11 @@ public function testAddTitleDuplicateNameException(): void { $m = new Model_Item($this->db); + $r = $m->hasOne('foo', ['model' => [Model_Item::class]]); + $this->expectException(Exception::class); $this->expectExceptionMessage('Field with such name already exists'); - $m->hasOne('foo', ['model' => [Model_Item::class]])->addTitle(); + $r->addTitle(); } public function testModelCaption(): void @@ -598,7 +600,8 @@ public function testTableWithSchema(): void $doc = new Model($this->db, ['table' => $docSchema . '.doc']); $doc->addField('name'); - $doc->hasOne('user_id', ['model' => $user])->addTitle(); + $doc->hasOne('user_id', ['model' => $user]) + ->addTitle(); $doc->addCondition('user', 'Sarah'); $user->hasMany('Documents', ['model' => $doc]); diff --git a/tests/ReferenceSqlTest.php b/tests/ReferenceSqlTest.php index b98adb75f..10f48728c 100644 --- a/tests/ReferenceSqlTest.php +++ b/tests/ReferenceSqlTest.php @@ -206,10 +206,11 @@ public function testAddOneField(): void $o = new Model($this->db, ['table' => 'order']); $o->addField('amount'); - $o->hasOne('user_id', ['model' => $u])->addFields([ - 'username' => 'name', - ['date', 'type' => 'date'], - ]); + $o->hasOne('user_id', ['model' => $u]) + ->addFields([ + 'username' => 'name', + ['date', 'type' => 'date'], + ]); self::assertSame('John', $o->load(1)->get('username')); self::{'assertEquals'}(new \DateTime('2001-01-02 UTC'), $o->load(1)->get('date')); @@ -221,16 +222,18 @@ public function testAddOneField(): void // few more tests $o = new Model($this->db, ['table' => 'order']); $o->addField('amount'); - $o->hasOne('user_id', ['model' => $u])->addFields([ - 'username' => 'name', - 'thedate' => ['date', 'type' => 'date'], - ]); + $o->hasOne('user_id', ['model' => $u]) + ->addFields([ + 'username' => 'name', + 'thedate' => ['date', 'type' => 'date'], + ]); self::assertSame('John', $o->load(1)->get('username')); self::{'assertEquals'}(new \DateTime('2001-01-02 UTC'), $o->load(1)->get('thedate')); $o = new Model($this->db, ['table' => 'order']); $o->addField('amount'); - $o->hasOne('user_id', ['model' => $u])->addField('date', null, ['type' => 'date']); + $o->hasOne('user_id', ['model' => $u]) + ->addField('date', null, ['type' => 'date']); self::{'assertEquals'}(new \DateTime('2001-01-02 UTC'), $o->load(1)->get('date')); } @@ -456,11 +459,12 @@ public function testAggregateHasMany(): void $l->addField('total_vat', ['type' => 'atk4_money']); $l->addField('total_gross', ['type' => 'atk4_money']); - $i->hasMany('line', ['model' => $l])->addFields([ - 'total_net' => ['aggregate' => 'sum'], - 'total_vat' => ['aggregate' => 'sum', 'type' => 'atk4_money'], - 'total_gross' => ['aggregate' => 'sum', 'type' => 'atk4_money'], - ]); + $i->hasMany('line', ['model' => $l]) + ->addFields([ + 'total_net' => ['aggregate' => 'sum'], + 'total_vat' => ['aggregate' => 'sum', 'type' => 'atk4_money'], + 'total_gross' => ['aggregate' => 'sum', 'type' => 'atk4_money'], + ]); $i = $i->load('1'); // type was set explicitly @@ -524,16 +528,17 @@ public function testOtherAggregates(): void $i->addField('name'); $i->addField('code'); - $l->hasMany('Items', ['model' => $i])->addFields([ - 'items_name' => ['aggregate' => 'count', 'field' => 'name', 'type' => 'integer'], - 'items_code' => ['aggregate' => 'count', 'field' => 'code', 'type' => 'integer'], // counts only not-null values - 'items_star' => ['aggregate' => 'count', 'type' => 'integer'], // no field set, counts all rows with count(*) - 'items_c:' => ['concat' => '::', 'field' => 'name'], - 'items_c-' => ['aggregate' => $i->dsql()->groupConcat($i->expr('[name]'), '-')], - 'len' => ['aggregate' => $i->expr('SUM(' . $makeLengthSqlFx('[name]') . ')'), 'type' => 'integer'], - 'len2' => ['expr' => 'SUM(' . $makeLengthSqlFx('[name]') . ')', 'type' => 'integer'], - 'chicken5' => ['expr' => 'SUM([])', 'args' => [5], 'type' => 'integer'], - ]); + $l->hasMany('Items', ['model' => $i]) + ->addFields([ + 'items_name' => ['aggregate' => 'count', 'field' => 'name', 'type' => 'integer'], + 'items_code' => ['aggregate' => 'count', 'field' => 'code', 'type' => 'integer'], // counts only not-null values + 'items_star' => ['aggregate' => 'count', 'type' => 'integer'], // no field set, counts all rows with count(*) + 'items_c:' => ['concat' => '::', 'field' => 'name'], + 'items_c-' => ['aggregate' => $i->dsql()->groupConcat($i->expr('[name]'), '-')], + 'len' => ['aggregate' => $i->expr('SUM(' . $makeLengthSqlFx('[name]') . ')'), 'type' => 'integer'], + 'len2' => ['expr' => 'SUM(' . $makeLengthSqlFx('[name]') . ')', 'type' => 'integer'], + 'chicken5' => ['expr' => 'SUM([])', 'args' => [5], 'type' => 'integer'], + ]); $ll = $l->load(1); self::assertSame(2, $ll->get('items_name')); // 2 not-null values @@ -791,7 +796,8 @@ public function testHasOneTitleSet(): void $u->addField('last_name'); $o = (new Model($this->db, ['table' => 'order'])); - $o->hasOne('user_id', ['model' => $u])->addTitle(); + $o->hasOne('user_id', ['model' => $u]) + ->addTitle(); // change order user by changing titleField value $o = $o->load(1); @@ -812,7 +818,8 @@ public function testHasOneTitleSet(): void $u->addField('last_name'); $o = (new Model($this->db, ['table' => 'order'])); - $o->hasOne('user_id', ['model' => $u])->addTitle(); + $o->hasOne('user_id', ['model' => $u]) + ->addTitle(); // change order user by changing titleField value $o = $o->load(1); @@ -833,7 +840,8 @@ public function testHasOneTitleSet(): void $u->addField('last_name'); $o = (new Model($this->db, ['table' => 'order'])); - $o->hasOne('my_user', ['model' => $u, 'ourField' => 'user_id'])->addTitle(); + $o->hasOne('my_user', ['model' => $u, 'ourField' => 'user_id']) + ->addTitle(); // change order user by changing reference field value $o = $o->load(1); @@ -854,7 +862,8 @@ public function testHasOneTitleSet(): void $u->addField('last_name'); $o = (new Model($this->db, ['table' => 'order'])); - $o->hasOne('my_user', ['model' => $u, 'ourField' => 'user_id'])->addTitle(); + $o->hasOne('my_user', ['model' => $u, 'ourField' => 'user_id']) + ->addTitle(); // change order user by changing ref field and titleField value - same $o = $o->load(1); diff --git a/tests/ReferenceTest.php b/tests/ReferenceTest.php index 5907bfc2c..d077f7648 100644 --- a/tests/ReferenceTest.php +++ b/tests/ReferenceTest.php @@ -4,6 +4,7 @@ namespace Atk4\Data\Tests; +use Atk4\Core\Exception as CoreException; use Atk4\Data\Exception; use Atk4\Data\Model; use Atk4\Data\Schema\TestCase; @@ -12,14 +13,12 @@ class ReferenceTest extends TestCase { public function testBasicReferences(): void { - $user = new Model(null, ['table' => 'user']); - $user->addField('id', ['type' => 'integer']); + $user = new Model($this->db, ['table' => 'user']); $user->addField('name'); $user = $user->createEntity(); $user->setId(1); - $order = new Model(); - $order->addField('id', ['type' => 'integer']); + $order = new Model(null, ['table' => 'order']); $order->addField('amount', ['default' => 20]); $order->addField('user_id', ['type' => 'integer']); @@ -30,7 +29,7 @@ public function testBasicReferences(): void self::assertSame(1, $o->get('user_id')); $r2 = $user->getModel()->hasMany('BigOrders', ['model' => static function () { - $m = new Model(); + $m = new Model(null, ['table' => 'big_order']); $m->addField('amount', ['default' => 100]); $m->addField('user_id', ['type' => 'integer']); @@ -51,14 +50,12 @@ public function testBasicReferences(): void public function testModelCaption(): void { - $user = new Model(null, ['table' => 'user']); - $user->addField('id', ['type' => 'integer']); + $user = new Model($this->db, ['table' => 'user']); $user->addField('name'); $user = $user->createEntity(); $user->setId(1); - $order = new Model(); - $order->addField('id', ['type' => 'integer']); + $order = new Model(null, ['table' => 'order']); $order->addField('amount', ['default' => 20]); $order->addField('user_id', ['type' => 'integer']); @@ -172,4 +169,24 @@ public function testRefTypeMismatchWithDisabledCheck(): void self::assertSame('string', $order->getField('placed_by_user_id')->type); self::assertSame('integer', $order->ref('placed_by')->getIdField()->type); } + + public function testCreateTheirModelMissingModelSeedException(): void + { + $m = new Model($this->db, ['table' => 'user']); + + $this->expectException(CoreException::class); + $this->expectExceptionMessage('Seed must be an array or an object'); + $m->hasOne('foo', []) + ->createTheirModel(); + } + + public function testCreateTheirModelInvalidModelSeedException(): void + { + $m = new Model($this->db, ['table' => 'user']); + + $this->expectException(CoreException::class); + $this->expectExceptionMessage('Seed must be an array or an object'); + $m->hasOne('foo', ['model' => Model::class]) + ->createTheirModel(); + } } diff --git a/tests/SubTypesTest.php b/tests/SubTypesTest.php index f9959ec60..dc6f05da3 100644 --- a/tests/SubTypesTest.php +++ b/tests/SubTypesTest.php @@ -135,8 +135,6 @@ protected function init(): void parent::init(); $this->hasOne('link_id', ['model' => [StTransaction_TransferIn::class]]); - - // $this->join('transaction', 'linked_transaction'); } }