From 28506bebfd1d222207a01d9cd2e65174f461a54b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Sat, 18 Jun 2022 00:15:41 +0200 Subject: [PATCH] Add Model::executeCountQuery() method (#1008) --- README.md | 3 +- docs/persistence.rst | 6 ++-- docs/quickstart.rst | 8 ++--- docs/sql.rst | 2 ++ src/Model.php | 12 +++++++ src/Persistence/Sql/Query.php | 4 +-- tests/ConditionSqlTest.php | 4 +-- tests/ModelWithoutIdTest.php | 6 ++-- tests/Persistence/ArrayTest.php | 37 ++++++++++----------- tests/Persistence/Sql/WithDb/SelectTest.php | 14 ++++---- tests/Persistence/SqlTest.php | 2 +- tests/RandomTest.php | 4 +-- tests/Schema/ModelTest.php | 2 +- tests/ScopeTest.php | 10 +++--- tests/Util/DeepCopyTest.php | 6 ++-- 15 files changed, 66 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 4e4ef4c95..ed0fd4b3d 100644 --- a/README.md +++ b/README.md @@ -609,8 +609,7 @@ Now you can explore. Try typing: > $m->loadBy('email', 'example@example.com') > $m->get() > $m->export(['email', 'name']) -> $m->action('count') -> $m->action('count')->getOne() +> $m->executeCountQuery() ``` ## Agile Core and DSQL diff --git a/docs/persistence.rst b/docs/persistence.rst index dc3918fd1..b8acc1dd4 100644 --- a/docs/persistence.rst +++ b/docs/persistence.rst @@ -415,7 +415,7 @@ Create copy of existing record // Now you have 2 records: // one with ID = 123 and another with ID = {next db generated id} - echo $m->action('count')->getOne(); + echo $m->executeCountQuery(); Duplicate then save under a new ID ---------------------------------- @@ -757,7 +757,7 @@ rows of data. Action can be executed at any time and that will return an expected result:: $m = Model_Invoice(); - $val = $m->action('count')->getOne(); + $val = (int) $m->action('count')->getOne(); // same as $val = $m->executeCountQuery() Most actions are sufficiently smart to understand what type of result you are expecting, so you can have the following code:: @@ -801,7 +801,7 @@ There are ability to execute aggregation functions:: and finally you can also use count:: - echo $m->action('count')->getOne(); + echo $m->executeCountQuery(); // same as echo $m->action('count')->getOne() SQL Actions on Linked Records diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 354d66d17..bdf6f69c8 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -138,7 +138,7 @@ inside console:: $m->addCondition('address_1', 'not', null); $m = $m->loadAny(); $m->get(); - $m->action('count')->getOne(); + $m->executeCountQuery(); // same as ((int) $m->action('count')->getOne()) Next, exit and create file `src/Model_ContactInfo.php`:: @@ -454,7 +454,7 @@ corresponding to all Systems that belong to user john. You can use the following to see number of records in DataSet or export DataSet:: $s->isLoaded(); - $s->action('count')->getOne(); + $s->executeCountQuery(); $s->export(); $s->action('count')->getDebugQuery(); @@ -471,7 +471,7 @@ the Clients that are contained in all of the Systems that belong to user john. You can examine the this model further:: $c->isLoaded(); - $c->action('count')->getOne(); + $c->executeCountQuery(); $c->export(); $c->action('count')->getDebugQuery(); @@ -529,7 +529,7 @@ basic aggregation without grouping. This type of aggregation provides some specific value from a data-set. SQL persistence implements some of the operations:: $m = new Model_Invoice($db); - $m->action('count')->getOne(); + $m->executeCountQuery(); $m->action('fx', ['sum', 'total'])->getOne(); $m->action('fx', ['max', 'shipping'])->getOne(); diff --git a/docs/sql.rst b/docs/sql.rst index 4c164e99f..315822b76 100644 --- a/docs/sql.rst +++ b/docs/sql.rst @@ -247,6 +247,8 @@ Returns query for `count(*)`:: $action = $model->action('count'); $cnt = $action->getOne(); + // for materialized count use: + $cnt = $model->executeCountQuery(); You can also specify alias:: diff --git a/src/Model.php b/src/Model.php index d0927877a..779fa9da3 100644 --- a/src/Model.php +++ b/src/Model.php @@ -1926,6 +1926,18 @@ public function action(string $mode, array $args = []) return $this->persistence->action($this, $mode, $args); } + public function executeCountQuery(): int + { + $this->assertIsModel(); + + $res = $this->action('count')->getOne(); + if (is_string($res) && $res === (string) (int) $res) { + $res = (int) $res; + } + + return $res; + } + // }}} // {{{ Expressions diff --git a/src/Persistence/Sql/Query.php b/src/Persistence/Sql/Query.php index 58dc06f80..374cc1a4a 100644 --- a/src/Persistence/Sql/Query.php +++ b/src/Persistence/Sql/Query.php @@ -116,7 +116,7 @@ protected function _render_field($add_alias = true): string if ( $add_alias === false || (is_string($field) && $alias === $field) - || is_numeric($alias) + || is_int($alias) ) { $alias = null; } @@ -225,7 +225,7 @@ protected function _render_table($add_alias = true): ?string if ( $add_alias === false || (is_string($table) && $alias === $table) - || is_numeric($alias) + || is_int($alias) ) { $alias = null; } diff --git a/tests/ConditionSqlTest.php b/tests/ConditionSqlTest.php index ecac3bb7a..e850bbb2c 100644 --- a/tests/ConditionSqlTest.php +++ b/tests/ConditionSqlTest.php @@ -383,13 +383,13 @@ public function testOrConditions(): void ['name', 'Peter'], )); - $this->assertSame('2', $u->action('count')->getOne()); + $this->assertSame(2, $u->executeCountQuery()); $u->addCondition(Model\Scope::createOr( ['name', 'Peter'], ['name', 'Joe'], )); - $this->assertSame('1', $u->action('count')->getOne()); + $this->assertSame(1, $u->executeCountQuery()); } /** diff --git a/tests/ModelWithoutIdTest.php b/tests/ModelWithoutIdTest.php index d904bbcb3..13e32c5a1 100644 --- a/tests/ModelWithoutIdTest.php +++ b/tests/ModelWithoutIdTest.php @@ -86,7 +86,7 @@ public function testInsert(): void } $this->m->insert(['name' => 'Joe']); - $this->assertSame('3', $this->m->action('count')->getOne()); + $this->assertSame(3, $this->m->executeCountQuery()); } /** @@ -101,7 +101,7 @@ public function testSave1(): void $m = $this->m->tryLoadAny(); $m->saveAndUnload(); - $this->assertSame('3', $this->m->action('count')->getOne()); + $this->assertSame(3, $this->m->executeCountQuery()); } /** @@ -116,7 +116,7 @@ public function testSave2(): void $m = $this->m->tryLoadAny(); $m->save(); - $this->assertSame('3', $this->m->action('count')->getOne()); + $this->assertSame(3, $this->m->executeCountQuery()); } /** diff --git a/tests/Persistence/ArrayTest.php b/tests/Persistence/ArrayTest.php index 2994e7458..e4c9185d9 100644 --- a/tests/Persistence/ArrayTest.php +++ b/tests/Persistence/ArrayTest.php @@ -244,6 +244,7 @@ public function testActionCount(): void $m->addField('surname'); $this->assertSame(2, $m->action('count')->getOne()); + $this->assertSame(2, $m->executeCountQuery()); } /** @@ -259,8 +260,6 @@ public function testActionField(): void $m->addField('name'); $m->addField('surname'); - $this->assertSame(2, $m->action('count')->getOne()); - // use alias as array key if it is set $q = $m->action('field', ['name', 'alias' => 'first_name']); $this->assertSame([ @@ -643,40 +642,40 @@ public function testImportAndAutoincrement(): void ['id' => 1, 'f1' => 'A'], ['id' => 2, 'f1' => 'B'], ]); - $this->assertSame(2, $m->action('count')->getOne()); + $this->assertSame(2, $m->executeCountQuery()); $m->import([ ['f1' => 'C'], ['f1' => 'D'], ]); - $this->assertSame(4, $m->action('count')->getOne()); + $this->assertSame(4, $m->executeCountQuery()); $m->import([ ['id' => 6, 'f1' => 'E'], ['id' => 7, 'f1' => 'F'], ]); - $this->assertSame(6, $m->action('count')->getOne()); + $this->assertSame(6, $m->executeCountQuery()); $m->delete(6); - $this->assertSame(5, $m->action('count')->getOne()); + $this->assertSame(5, $m->executeCountQuery()); $m->import([ ['f1' => 'G'], ['f1' => 'H'], ]); - $this->assertSame(7, $m->action('count')->getOne()); + $this->assertSame(7, $m->executeCountQuery()); $m->import([ ['id' => 99, 'f1' => 'I'], ['id' => 20, 'f1' => 'J'], ]); - $this->assertSame(9, $m->action('count')->getOne()); + $this->assertSame(9, $m->executeCountQuery()); $m->import([ ['f1' => 'K'], ['f1' => 'L'], ]); - $this->assertSame(11, $m->action('count')->getOne()); + $this->assertSame(11, $m->executeCountQuery()); $m->delete(100); $m->createEntity()->set('f1', 'M')->save(); @@ -711,10 +710,10 @@ public function testLimit(): void $m = new Model($p); $m->addField('f1'); - $this->assertSame(4, $m->action('count')->getOne()); + $this->assertSame(4, $m->executeCountQuery()); $m->setLimit(3); - $this->assertSame(3, $m->action('count')->getOne()); + $this->assertSame(3, $m->executeCountQuery()); $this->assertSame([ ['id' => 1, 'f1' => 'A'], ['id' => 2, 'f1' => 'D'], @@ -722,7 +721,7 @@ public function testLimit(): void ], array_values($m->export())); $m->setLimit(2, 1); - $this->assertSame(2, $m->action('count')->getOne()); + $this->assertSame(2, $m->executeCountQuery()); $this->assertSame([ ['id' => 2, 'f1' => 'D'], ['id' => 3, 'f1' => 'E'], @@ -731,7 +730,7 @@ public function testLimit(): void // well, this is strange, that you can actually change limit on-the-fly and then previous // limit is not taken into account, but most likely you will never set it multiple times $m->setLimit(3); - $this->assertSame(3, $m->action('count')->getOne()); + $this->assertSame(3, $m->executeCountQuery()); } /** @@ -749,19 +748,19 @@ public function testCondition(): void $m->addField('name'); $m->addField('surname'); - $this->assertSame(4, $m->action('count')->getOne()); + $this->assertSame(4, $m->executeCountQuery()); $this->assertSame(['data' => $dbData], $this->getInternalPersistenceData($p)); $m->addCondition('name', 'Sarah'); - $this->assertSame(3, $m->action('count')->getOne()); + $this->assertSame(3, $m->executeCountQuery()); $m->addCondition('surname', 'Smith'); - $this->assertSame(1, $m->action('count')->getOne()); + $this->assertSame(1, $m->executeCountQuery()); $this->assertSame([4 => ['id' => 4, 'name' => 'Sarah', 'surname' => 'Smith']], $m->export()); $this->assertSame([4 => ['id' => 4, 'name' => 'Sarah', 'surname' => 'Smith']], $m->action('select')->getRows()); $m->addCondition('surname', 'Siiiith'); - $this->assertSame(0, $m->action('count')->getOne()); + $this->assertSame(0, $m->executeCountQuery()); } public function testUnsupportedAction(): void @@ -863,10 +862,10 @@ public function testHasMany(): void $user->hasOne('country_id', ['model' => $country]); $cc = $country->load(1); - $this->assertSame(2, $cc->ref('Users')->action('count')->getOne()); + $this->assertSame(2, $cc->ref('Users')->executeCountQuery()); $cc = $country->load(2); - $this->assertSame(1, $cc->ref('Users')->action('count')->getOne()); + $this->assertSame(1, $cc->ref('Users')->executeCountQuery()); } public function testLoadAnyThrowsExceptionOnRecordNotFound(): void diff --git a/tests/Persistence/Sql/WithDb/SelectTest.php b/tests/Persistence/Sql/WithDb/SelectTest.php index 1af29e306..de59e6caf 100644 --- a/tests/Persistence/Sql/WithDb/SelectTest.php +++ b/tests/Persistence/Sql/WithDb/SelectTest.php @@ -326,46 +326,46 @@ public function testImportAndAutoincrement(): void ['id' => 1, 'f1' => 'A'], ['id' => 2, 'f1' => 'B'], ]); - $this->assertSame('2', $m->action('count')->getOne()); + $this->assertSame(2, $m->executeCountQuery()); $this->assertSame(2, $getLastAiFx()); $m->import([ ['f1' => 'C'], ['f1' => 'D'], ]); - $this->assertSame('4', $m->action('count')->getOne()); + $this->assertSame(4, $m->executeCountQuery()); $this->assertSame(4, $getLastAiFx()); $m->import([ ['id' => 6, 'f1' => 'E'], ['id' => 7, 'f1' => 'F'], ]); - $this->assertSame('6', $m->action('count')->getOne()); + $this->assertSame(6, $m->executeCountQuery()); $this->assertSame(7, $getLastAiFx()); $m->delete(6); - $this->assertSame('5', $m->action('count')->getOne()); + $this->assertSame(5, $m->executeCountQuery()); $this->assertSame(7, $getLastAiFx()); $m->import([ ['f1' => 'G'], ['f1' => 'H'], ]); - $this->assertSame('7', $m->action('count')->getOne()); + $this->assertSame(7, $m->executeCountQuery()); $this->assertSame(9, $getLastAiFx()); $m->import([ ['id' => 99, 'f1' => 'I'], ['id' => 20, 'f1' => 'J'], ]); - $this->assertSame('9', $m->action('count')->getOne()); + $this->assertSame(9, $m->executeCountQuery()); $this->assertSame(99, $getLastAiFx()); $m->import([ ['f1' => 'K'], ['f1' => 'L'], ]); - $this->assertSame('11', $m->action('count')->getOne()); + $this->assertSame(11, $m->executeCountQuery()); $this->assertSame(101, $getLastAiFx()); $m->delete(100); diff --git a/tests/Persistence/SqlTest.php b/tests/Persistence/SqlTest.php index c6b2c3c42..6488b2d71 100644 --- a/tests/Persistence/SqlTest.php +++ b/tests/Persistence/SqlTest.php @@ -177,7 +177,7 @@ public function testModelInsertRows(): void $this->assertSame('1', $m->action('exists')->getOne()); - $this->assertSame('2', $m->action('count')->getOne()); + $this->assertSame(2, $m->executeCountQuery()); } public function testPersistenceDelete(): void diff --git a/tests/RandomTest.php b/tests/RandomTest.php index e076cc535..c334bc2c0 100644 --- a/tests/RandomTest.php +++ b/tests/RandomTest.php @@ -88,7 +88,7 @@ public function testRate(): void $m = new Model_Rate($this->db); - $this->assertSame('2', $m->action('count')->getOne()); + $this->assertSame(2, $m->executeCountQuery()); } public function testTitleImport(): void @@ -234,7 +234,7 @@ public function testSameTable3(): void $m->load(2)->get() ); - $this->assertSame('1', $m->load(2)->ref('Child', ['table_alias' => 'pp'])->action('count')->getOne()); + $this->assertSame(1, $m->load(2)->ref('Child', ['table_alias' => 'pp'])->executeCountQuery()); $this->assertSame('John', $m->load(2)->ref('parent_item_id', ['table_alias' => 'pp'])->get('name')); } diff --git a/tests/Schema/ModelTest.php b/tests/Schema/ModelTest.php index 0f686ef95..b41a08ddf 100644 --- a/tests/Schema/ModelTest.php +++ b/tests/Schema/ModelTest.php @@ -158,7 +158,7 @@ private function makePseudoRandomString(bool $isBinary, int $length): string for ($i = 0; $i <= 0x10FFFF; $i = $i * 1.001 + 1) { $iInt = (int) $i; if ($iInt < 0xD800 || $iInt > 0xDFFF) { - $baseChars[crc32($length . '_' . $i)] = mb_chr($iInt); + $baseChars[crc32($length . '_' . $iInt)] = mb_chr($iInt); } } } diff --git a/tests/ScopeTest.php b/tests/ScopeTest.php index 1f56144b9..ea27847f9 100644 --- a/tests/ScopeTest.php +++ b/tests/ScopeTest.php @@ -204,7 +204,7 @@ public function testConditionOnReferencedRecords(): void $user->addCondition('country_id/code', 'LV'); - $this->assertSame('1', $user->action('count')->getOne()); + $this->assertSame(1, $user->executeCountQuery()); foreach ($user as $u) { $this->assertEquals('LV', $u->get('country_code')); @@ -215,7 +215,7 @@ public function testConditionOnReferencedRecords(): void // users that have no ticket $user->addCondition('Tickets/#', 0); - $this->assertSame('1', $user->action('count')->getOne()); + $this->assertSame(1, $user->executeCountQuery()); foreach ($user as $u) { $this->assertTrue(in_array($u->get('name'), ['Alain', 'Aerton', 'Rubens'], true)); @@ -253,7 +253,7 @@ public function testConditionOnReferencedRecords(): void // countries with users that have any tickets $country->addCondition('Users/Tickets/#', '>', 0); - $this->assertSame('3', $country->action('count')->getOne()); + $this->assertSame(3, $country->executeCountQuery()); foreach ($country as $c) { $this->assertTrue(in_array($c->get('code'), ['LV', 'CA', 'BR'], true)); @@ -264,7 +264,7 @@ public function testConditionOnReferencedRecords(): void // countries with users that have no tickets $country->addCondition('Users/Tickets/#', 0); - $this->assertSame('1', $country->action('count')->getOne()); + $this->assertSame(1, $country->executeCountQuery()); foreach ($country as $c) { $this->assertTrue(in_array($c->get('code'), ['FR'], true)); @@ -286,7 +286,7 @@ public function testConditionOnReferencedRecords(): void $user->addCondition('Tickets/user/country_id/Users/country_id/Users/name', '!=', null); // should be always true } - $this->assertSame('2', $user->action('count')->getOne()); + $this->assertSame(2, $user->executeCountQuery()); foreach ($user as $u) { $this->assertTrue(in_array($u->get('name'), ['Aerton', 'Rubens'], true)); } diff --git a/tests/Util/DeepCopyTest.php b/tests/Util/DeepCopyTest.php index be16ae5c1..5282a6688 100644 --- a/tests/Util/DeepCopyTest.php +++ b/tests/Util/DeepCopyTest.php @@ -243,9 +243,9 @@ public function testBasic(): void $this->assertEquals(3, $client3->getId()); // We should have one of each records for this new client - $this->assertSame('1', $client3->ref('Invoices')->action('count')->getOne()); - $this->assertSame('1', $client3->ref('Quotes')->action('count')->getOne()); - $this->assertSame('1', $client3->ref('Payments')->action('count')->getOne()); + $this->assertSame(1, $client3->ref('Invoices')->executeCountQuery()); + $this->assertSame(1, $client3->ref('Quotes')->executeCountQuery()); + $this->assertSame(1, $client3->ref('Payments')->executeCountQuery()); if ($this->getDatabasePlatform() instanceof SQLServerPlatform) { $this->markTestIncomplete('TODO MSSQL: Cannot perform an aggregate function on an expression containing an aggregate or a subquery');