From 8315493e376692e61aa7ee464f2948306f32618d Mon Sep 17 00:00:00 2001 From: Theodore Brown Date: Fri, 22 Dec 2023 15:37:27 -0600 Subject: [PATCH] Support counting the rows that match a filter without selecting them --- CHANGELOG.md | 8 +++++++- src/Entities.php | 18 ++++++++++++++++++ src/RouteHandler.php | 29 +++++++++++++++++++++++++++++ test/EntitiesDbTest.php | 20 ++++++++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3847383..91b7ab6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.8.0] - 2023-12-22 +### Added +- `countEntities()` method and corresponding `count()` route handler. +This allows counting the rows that match a query/filter without selecting them. + ## [2.7.0] - 2023-09-27 ### Added - `writableId` bool property to optionally make the ID column writable. @@ -146,7 +151,8 @@ return early if passed an empty IDs array. ### Changed - Initial stable release -[Unreleased]: https://github.com/theodorejb/phaster/compare/v2.7.0...HEAD +[Unreleased]: https://github.com/theodorejb/phaster/compare/v2.8.0...HEAD +[2.8.0]: https://github.com/theodorejb/phaster/compare/v2.7.0...v2.8.0 [2.7.0]: https://github.com/theodorejb/phaster/compare/v2.6.0...v2.7.0 [2.6.0]: https://github.com/theodorejb/phaster/compare/v2.5.0...v2.6.0 [2.5.0]: https://github.com/theodorejb/phaster/compare/v2.4.0...v2.5.0 diff --git a/src/Entities.php b/src/Entities.php index 745eae1..31cda30 100644 --- a/src/Entities.php +++ b/src/Entities.php @@ -345,6 +345,24 @@ public function getEntities(array $filter = [], array $fields = [], array $sort return Helpers::mapRows($select->query()->getIterator(), $fieldProps); } + public function countEntities(array $filter = []): int + { + $processedFilter = $this->processFilter($filter); + $selectMap = Helpers::propMapToSelectMap($this->fullPropMap);; + + $prop = new Prop('count', 'COUNT(*)', false, true, 'count'); + $queryOptions = new QueryOptions($processedFilter, $filter, [], [$prop]); + + /** @psalm-suppress MixedArgumentTypeCoercion */ + $select = $this->db->select($this->getBaseSelect($queryOptions)) + ->where(self::propertiesToColumns($selectMap, $processedFilter)); + + /** @var array{count: int} $row */ + $row = $select->query()->getFirst(); + + return $row['count']; + } + /** * Converts nested properties to an array of columns and values using a map. * @return array diff --git a/src/RouteHandler.php b/src/RouteHandler.php index 5b1c6c1..b935cd2 100644 --- a/src/RouteHandler.php +++ b/src/RouteHandler.php @@ -101,6 +101,35 @@ public function search(string $class, int $defaultLimit = 25, int $maxLimit = 10 }; } + /** + * @param class-string $class + */ + public function count(string $class): callable + { + $factory = $this->entitiesFactory; + + return function (ServerRequestInterface $request, ResponseInterface $response) use ($class, $factory): ResponseInterface { + $query = []; + + foreach ($request->getQueryParams() as $param => $value) { + if ($param !== 'q') { + throw new HttpException("Unrecognized parameter '{$param}'", StatusCode::BAD_REQUEST); + } + if (!is_array($value)) { + throw new HttpException("Parameter '{$param}' must be an array", StatusCode::BAD_REQUEST); + } + + $query = $value; + } + + $instance = $factory->createEntities($class); + $count = $instance->countEntities($query); + + $response->getBody()->write(json_encode(['count' => $count])); + return $response->withHeader('Content-Type', 'application/json'); + }; + } + /** * @param class-string $class */ diff --git a/test/EntitiesDbTest.php b/test/EntitiesDbTest.php index 1f28dfb..ff3da39 100644 --- a/test/EntitiesDbTest.php +++ b/test/EntitiesDbTest.php @@ -291,6 +291,26 @@ public function testGetEntities(Entities $entities): void $this->assertSame($expected, $actual); } + /** + * @dataProvider entitiesProvider + */ + public function testCountEntities(Entities $entities): void + { + $users = []; + + for ($i = 1; $i <= 20; $i++) { + $users[] = [ + 'name' => "Count user {$i}", + 'birthday' => '2000-03-04', + 'weight' => $i * 10, + ]; + } + + $entities->addEntities($users); + $actual = $entities->countEntities(['weight' => ['le' => 100], 'name' => ['lk' => 'Count user %']]); + $this->assertSame(10, $actual); + } + /** * @dataProvider legacyUsersProvider */