From 384610ff107d2971fd377d85c854dd69ad4a7774 Mon Sep 17 00:00:00 2001 From: Composite PHP Date: Sat, 28 Oct 2023 11:55:42 +0100 Subject: [PATCH 01/12] Add CombinedTransaction saveMany and deleteMany --- src/CombinedTransaction.php | 62 ++++++++++++++++--------- tests/Table/CombinedTransactionTest.php | 28 ++++++++++- 2 files changed, 67 insertions(+), 23 deletions(-) diff --git a/src/CombinedTransaction.php b/src/CombinedTransaction.php index 1b53586..bb8a4a5 100644 --- a/src/CombinedTransaction.php +++ b/src/CombinedTransaction.php @@ -19,18 +19,29 @@ class CombinedTransaction */ public function save(AbstractTable $table, AbstractEntity &$entity): void { - try { - $connectionName = $table->getConnectionName(); - if (empty($this->transactions[$connectionName])) { - $connection = ConnectionManager::getConnection($connectionName); - $connection->beginTransaction(); - $this->transactions[$connectionName] = $connection; - } + $this->doInTransaction($table, function () use ($table, &$entity) { $table->save($entity); - } catch (\Throwable $e) { - $this->rollback(); - throw new Exceptions\DbException($e->getMessage(), 500, $e); - } + }); + } + + /** + * @param AbstractTable $table + * @param AbstractEntity[] $entities + * @throws DbException + */ + public function saveMany(AbstractTable $table, array $entities): void + { + $this->doInTransaction($table, fn () => $table->saveMany($entities)); + } + + /** + * @param AbstractTable $table + * @param AbstractEntity[] $entities + * @throws DbException + */ + public function deleteMany(AbstractTable $table, array $entities): void + { + $this->doInTransaction($table, fn () => $table->deleteMany($entities)); } /** @@ -38,18 +49,9 @@ public function save(AbstractTable $table, AbstractEntity &$entity): void */ public function delete(AbstractTable $table, AbstractEntity &$entity): void { - try { - $connectionName = $table->getConnectionName(); - if (empty($this->transactions[$connectionName])) { - $connection = ConnectionManager::getConnection($connectionName); - $connection->beginTransaction(); - $this->transactions[$connectionName] = $connection; - } + $this->doInTransaction($table, function () use ($table, &$entity) { $table->delete($entity); - } catch (\Throwable $e) { - $this->rollback(); - throw new Exceptions\DbException($e->getMessage(), 500, $e); - } + }); } public function rollback(): void @@ -107,6 +109,22 @@ public function releaseLock(): void $this->cache->delete($this->lockKey); } + private function doInTransaction(AbstractTable $table, callable $callback): void + { + try { + $connectionName = $table->getConnectionName(); + if (empty($this->transactions[$connectionName])) { + $connection = ConnectionManager::getConnection($connectionName); + $connection->beginTransaction(); + $this->transactions[$connectionName] = $connection; + } + $callback(); + } catch (\Throwable $e) { + $this->rollback(); + throw new Exceptions\DbException($e->getMessage(), 500, $e); + } + } + /** * @param string[] $keyParts * @return string diff --git a/tests/Table/CombinedTransactionTest.php b/tests/Table/CombinedTransactionTest.php index 63ffff5..1e67a2c 100644 --- a/tests/Table/CombinedTransactionTest.php +++ b/tests/Table/CombinedTransactionTest.php @@ -20,7 +20,7 @@ public function test_transactionCommit(): void $e1 = new Entities\TestAutoincrementEntity(name: 'Foo'); $saveTransaction->save($autoIncrementTable, $e1); - $e2 = new Entities\TestCompositeEntity(user_id: $e1->id, post_id: mt_rand(1, 1000), message: 'Bar'); + $e2 = new Entities\TestCompositeEntity(user_id: $e1->id, post_id: mt_rand(1, 1000000), message: 'Bar'); $saveTransaction->save($compositeTable, $e2); $saveTransaction->commit(); @@ -37,6 +37,32 @@ public function test_transactionCommit(): void $this->assertNull($compositeTable->findOne($e2->user_id, $e2->post_id)); } + public function test_saveDeleteMany(): void + { + $autoIncrementTable = new Tables\TestAutoincrementTable(); + $compositeTable = new Tables\TestCompositeTable(); + + $saveTransaction = new CombinedTransaction(); + + $e1 = new Entities\TestAutoincrementEntity(name: 'Foo'); + $saveTransaction->save($autoIncrementTable, $e1); + + $e2 = new Entities\TestCompositeEntity(user_id: $e1->id, post_id: mt_rand(1, 1000000), message: 'Foo'); + $e3 = new Entities\TestCompositeEntity(user_id: $e1->id, post_id: mt_rand(1, 1000000), message: 'Bar'); + $saveTransaction->saveMany($compositeTable, [$e2, $e3]); + + $saveTransaction->commit(); + + $this->assertNotNull($autoIncrementTable->findByPk($e1->id)); + $this->assertNotNull($compositeTable->findOne($e2->user_id, $e2->post_id)); + $this->assertNotNull($compositeTable->findOne($e3->user_id, $e3->post_id)); + + $deleteTransaction = new CombinedTransaction(); + $deleteTransaction->delete($autoIncrementTable, $e1); + $deleteTransaction->deleteMany($compositeTable, [$e2, $e3]); + $deleteTransaction->commit(); + } + public function test_transactionRollback(): void { $autoIncrementTable = new Tables\TestAutoincrementTable(); From 5e9e64294e87daa83a7eeaedcecafb1eaf6454ed Mon Sep 17 00:00:00 2001 From: Composite PHP Date: Sat, 28 Oct 2023 12:11:19 +0100 Subject: [PATCH 02/12] Get rid of "Internal" word in find...() and count...() AbstractTable protected methods --- doc/cache.md | 8 ++++---- doc/example.md | 6 +++--- doc/table.md | 8 ++++---- src/AbstractCachedTable.php | 20 +++++++++---------- src/AbstractTable.php | 14 ++++++------- .../Tables/TestAutoincrementCachedTable.php | 12 +++++------ .../Tables/TestAutoincrementSdCachedTable.php | 10 +++++----- .../Tables/TestAutoincrementSdTable.php | 8 ++++---- .../Tables/TestAutoincrementTable.php | 12 +++++------ .../Tables/TestCompositeCachedTable.php | 6 +++--- .../Tables/TestCompositeSdCachedTable.php | 6 +++--- .../TestStand/Tables/TestCompositeSdTable.php | 6 +++--- tests/TestStand/Tables/TestCompositeTable.php | 8 ++++---- .../Tables/TestOptimisticLockTable.php | 2 +- .../Tables/TestUniqueCachedTable.php | 6 +++--- .../Tables/TestUniqueSdCachedTable.php | 6 +++--- tests/TestStand/Tables/TestUniqueSdTable.php | 4 ++-- tests/TestStand/Tables/TestUniqueTable.php | 6 +++--- tests/TestStand/Tables/TestUpdateAtTable.php | 2 +- 19 files changed, 75 insertions(+), 75 deletions(-) diff --git a/doc/cache.md b/doc/cache.md index e3ca6ab..7b64d1f 100644 --- a/doc/cache.md +++ b/doc/cache.md @@ -6,7 +6,7 @@ To start using auto-cache feature you need: to `Composite\DB\AbstractCachedTable` 3. Implement method `getFlushCacheKeys()` 4. Change all internal select methods to their cached versions (example: `findByPkInternal()` -to `findByPkCachedInternal()` etc.) +to `_findByPkCached()` etc.) You can also generate cached version of your table with console command: @@ -46,7 +46,7 @@ class PostsTable extends AbstractCachedTable public function findByPk(int $id): ?Post { - return $this->createEntity($this->findByPkInternalCached($id)); + return $this->createEntity($this->_findByPkCached($id)); } /** @@ -54,7 +54,7 @@ class PostsTable extends AbstractCachedTable */ public function findAllFeatured(): array { - return $this->createEntities($this->findAllInternal( + return $this->createEntities($this->_findAll( 'is_featured = :is_featured', ['is_featured' => true], )); @@ -62,7 +62,7 @@ class PostsTable extends AbstractCachedTable public function countAllFeatured(): int { - return $this->countAllCachedInternal( + return $this->_countAllCached( 'is_featured = :is_featured', ['is_featured' => true], ); diff --git a/doc/example.md b/doc/example.md index 096ea6f..22a63fc 100644 --- a/doc/example.md +++ b/doc/example.md @@ -43,7 +43,7 @@ class UsersTable extends \Composite\DB\AbstractTable public function findByPk(int $id): ?User { - return $this->createEntity($this->findByPkInternal($id)); + return $this->createEntity($this->_findByPk($id)); } /** @@ -51,7 +51,7 @@ class UsersTable extends \Composite\DB\AbstractTable */ public function findAllActive(): array { - return $this->createEntities($this->findAllInternal( + return $this->createEntities($this->_findAll( 'status = :status', ['status' => Status::ACTIVE->name], )); @@ -59,7 +59,7 @@ class UsersTable extends \Composite\DB\AbstractTable public function countAllActive(): int { - return $this->countAllInternal( + return $this->_countAll( 'status = :status', ['status' => Status::ACTIVE->name], ); diff --git a/doc/table.md b/doc/table.md index c80f0d8..fbd7d1b 100644 --- a/doc/table.md +++ b/doc/table.md @@ -38,7 +38,7 @@ class UsersTable extends AbstractTable public function findOne(int $id): ?User { - return $this->createEntity($this->findOneInternal($id)); + return $this->createEntity($this->_findOne($id)); } /** @@ -46,12 +46,12 @@ class UsersTable extends AbstractTable */ public function findAll(): array { - return $this->createEntities($this->findAllInternal()); + return $this->createEntities($this->_findAll()); } public function countAll(): int { - return $this->countAllInternal(); + return $this->_countAll(); } } ``` @@ -67,7 +67,7 @@ Example with internal helper: */ public function findAllActiveAdults(): array { - $rows = $this->findAllInternal( + $rows = $this->_findAll( 'age > :age AND status = :status', ['age' => 18, 'status' => Status::ACTIVE->name], ); diff --git a/src/AbstractCachedTable.php b/src/AbstractCachedTable.php index 857f9b9..cca50ad 100644 --- a/src/AbstractCachedTable.php +++ b/src/AbstractCachedTable.php @@ -95,9 +95,9 @@ private function collectCacheKeysByEntity(AbstractEntity $entity): array /** * @return array|null */ - protected function findByPkCachedInternal(mixed $pk, null|int|\DateInterval $ttl = null): ?array + protected function _findByPkCached(mixed $pk, null|int|\DateInterval $ttl = null): ?array { - return $this->findOneCachedInternal($this->getPkCondition($pk), $ttl); + return $this->_findOneCached($this->getPkCondition($pk), $ttl); } /** @@ -105,11 +105,11 @@ protected function findByPkCachedInternal(mixed $pk, null|int|\DateInterval $ttl * @param int|\DateInterval|null $ttl * @return array|null */ - protected function findOneCachedInternal(array $condition, null|int|\DateInterval $ttl = null): ?array + protected function _findOneCached(array $condition, null|int|\DateInterval $ttl = null): ?array { return $this->getCached( $this->getOneCacheKey($condition), - fn() => $this->findOneInternal($condition), + fn() => $this->_findOne($condition), $ttl, ) ?: null; } @@ -119,7 +119,7 @@ protected function findOneCachedInternal(array $condition, null|int|\DateInterva * @param array|string $orderBy * @return array[] */ - protected function findAllCachedInternal( + protected function _findAllCached( string $whereString = '', array $whereParams = [], array|string $orderBy = [], @@ -129,7 +129,7 @@ protected function findAllCachedInternal( { return $this->getCached( $this->getListCacheKey($whereString, $whereParams, $orderBy, $limit), - fn() => $this->findAllInternal(whereString: $whereString, whereParams: $whereParams, orderBy: $orderBy, limit: $limit), + fn() => $this->_findAll(whereString: $whereString, whereParams: $whereParams, orderBy: $orderBy, limit: $limit), $ttl, ); } @@ -137,7 +137,7 @@ protected function findAllCachedInternal( /** * @param array $whereParams */ - protected function countAllCachedInternal( + protected function _countAllCached( string $whereString = '', array $whereParams = [], null|int|\DateInterval $ttl = null, @@ -145,7 +145,7 @@ protected function countAllCachedInternal( { return (int)$this->getCached( $this->getCountCacheKey($whereString, $whereParams), - fn() => $this->countAllInternal(whereString: $whereString, whereParams: $whereParams), + fn() => $this->_countAll(whereString: $whereString, whereParams: $whereParams), $ttl, ); } @@ -169,7 +169,7 @@ protected function getCached(string $cacheKey, callable $dataCallback, null|int| * @return array> * @throws \Psr\SimpleCache\InvalidArgumentException */ - protected function findMultiCachedInternal(array $ids, null|int|\DateInterval $ttl = null): array + protected function _findMultiCached(array $ids, null|int|\DateInterval $ttl = null): array { $result = $cacheKeys = $foundIds = []; foreach ($ids as $id) { @@ -188,7 +188,7 @@ protected function findMultiCachedInternal(array $ids, null|int|\DateInterval $t } $ids = array_diff($ids, $foundIds); foreach ($ids as $id) { - if ($row = $this->findByPkCachedInternal($id, $ttl)) { + if ($row = $this->_findByPkCached($id, $ttl)) { $result[] = $row; } } diff --git a/src/AbstractTable.php b/src/AbstractTable.php index 63da7fe..4362f42 100644 --- a/src/AbstractTable.php +++ b/src/AbstractTable.php @@ -173,7 +173,7 @@ public function deleteMany(array $entities): void * @param array $whereParams * @throws \Doctrine\DBAL\Exception */ - protected function countAllInternal(string $whereString = '', array $whereParams = []): int + protected function _countAll(string $whereString = '', array $whereParams = []): int { $query = $this->select('COUNT(*)'); if ($whereString) { @@ -190,10 +190,10 @@ protected function countAllInternal(string $whereString = '', array $whereParams * @throws EntityException * @throws \Doctrine\DBAL\Exception */ - protected function findByPkInternal(mixed $pk): ?array + protected function _findByPk(mixed $pk): ?array { $where = $this->getPkCondition($pk); - return $this->findOneInternal($where); + return $this->_findOne($where); } /** @@ -202,7 +202,7 @@ protected function findByPkInternal(mixed $pk): ?array * @return array|null * @throws \Doctrine\DBAL\Exception */ - protected function findOneInternal(array $where, array|string $orderBy = []): ?array + protected function _findOne(array $where, array|string $orderBy = []): ?array { $query = $this->select(); $this->buildWhere($query, $where); @@ -216,7 +216,7 @@ protected function findOneInternal(array $where, array|string $orderBy = []): ?a * @throws DbException * @throws \Doctrine\DBAL\Exception */ - protected function findMultiInternal(array $pkList): array + protected function _findMulti(array $pkList): array { if (!$pkList) { return []; @@ -231,7 +231,7 @@ protected function findMultiInternal(array $pkList): array * @return list> * @throws \Doctrine\DBAL\Exception */ - protected function findAllInternal( + protected function _findAll( string $whereString = '', array $whereParams = [], array|string $orderBy = [], @@ -332,7 +332,7 @@ protected function select(string $select = '*'): QueryBuilder /** * @param array $where */ - private function buildWhere(\Doctrine\DBAL\Query\QueryBuilder $query, array $where): void + private function buildWhere(QueryBuilder $query, array $where): void { foreach ($where as $column => $value) { if ($value === null) { diff --git a/tests/TestStand/Tables/TestAutoincrementCachedTable.php b/tests/TestStand/Tables/TestAutoincrementCachedTable.php index 5776f92..90f4386 100644 --- a/tests/TestStand/Tables/TestAutoincrementCachedTable.php +++ b/tests/TestStand/Tables/TestAutoincrementCachedTable.php @@ -39,12 +39,12 @@ protected function getFlushCacheKeys(TestAutoincrementEntity|AbstractEntity $ent public function findByPk(int $id): ?TestAutoincrementEntity { - return $this->createEntity($this->findByPkCachedInternal($id)); + return $this->createEntity($this->_findByPkCached($id)); } public function findOneByName(string $name): ?TestAutoincrementEntity { - return $this->createEntity($this->findOneCachedInternal(['name' => $name])); + return $this->createEntity($this->_findOneCached(['name' => $name])); } /** @@ -52,7 +52,7 @@ public function findOneByName(string $name): ?TestAutoincrementEntity */ public function findAllByName(string $name): array { - return $this->createEntities($this->findAllCachedInternal( + return $this->createEntities($this->_findAllCached( 'name = :name', ['name' => $name], )); @@ -63,7 +63,7 @@ public function findAllByName(string $name): array */ public function findRecent(int $limit, int $offset): array { - return $this->createEntities($this->findAllInternal( + return $this->createEntities($this->_findAll( orderBy: ['id' => 'DESC'], limit: $limit, offset: $offset, @@ -72,7 +72,7 @@ public function findRecent(int $limit, int $offset): array public function countAllByName(string $name): int { - return $this->countAllCachedInternal( + return $this->_countAllCached( 'name = :name', ['name' => $name], ); @@ -83,7 +83,7 @@ public function countAllByName(string $name): int */ public function findMulti(array $ids): array { - return $this->createEntities($this->findMultiCachedInternal($ids)); + return $this->createEntities($this->_findMultiCached($ids)); } public function truncate(): void diff --git a/tests/TestStand/Tables/TestAutoincrementSdCachedTable.php b/tests/TestStand/Tables/TestAutoincrementSdCachedTable.php index 66645f4..c646e9a 100644 --- a/tests/TestStand/Tables/TestAutoincrementSdCachedTable.php +++ b/tests/TestStand/Tables/TestAutoincrementSdCachedTable.php @@ -39,12 +39,12 @@ protected function getFlushCacheKeys(TestAutoincrementSdEntity|AbstractEntity $e public function findByPk(int $id): ?TestAutoincrementSdEntity { - return $this->createEntity($this->findByPkInternal($id)); + return $this->createEntity($this->_findByPk($id)); } public function findOneByName(string $name): ?TestAutoincrementSdEntity { - return $this->createEntity($this->findOneCachedInternal(['name' => $name])); + return $this->createEntity($this->_findOneCached(['name' => $name])); } /** @@ -52,7 +52,7 @@ public function findOneByName(string $name): ?TestAutoincrementSdEntity */ public function findAllByName(string $name): array { - return $this->createEntities($this->findAllCachedInternal( + return $this->createEntities($this->_findAllCached( 'name = :name', ['name' => $name, 'deleted_at' => null], )); @@ -63,7 +63,7 @@ public function findAllByName(string $name): array */ public function findRecent(int $limit, int $offset): array { - return $this->createEntities($this->findAllInternal( + return $this->createEntities($this->_findAll( orderBy: 'id DESC', limit: $limit, offset: $offset, @@ -72,7 +72,7 @@ public function findRecent(int $limit, int $offset): array public function countAllByName(string $name): int { - return $this->countAllCachedInternal( + return $this->_countAllCached( 'name = :name', ['name' => $name], ); diff --git a/tests/TestStand/Tables/TestAutoincrementSdTable.php b/tests/TestStand/Tables/TestAutoincrementSdTable.php index b9ce555..c45e826 100644 --- a/tests/TestStand/Tables/TestAutoincrementSdTable.php +++ b/tests/TestStand/Tables/TestAutoincrementSdTable.php @@ -20,12 +20,12 @@ protected function getConfig(): TableConfig public function findByPk(int $id): ?TestAutoincrementSdEntity { - return $this->createEntity($this->findByPkInternal($id)); + return $this->createEntity($this->_findByPk($id)); } public function findOneByName(string $name): ?TestAutoincrementSdEntity { - return $this->createEntity($this->findOneInternal(['name' => $name, 'deleted_at' => null])); + return $this->createEntity($this->_findOne(['name' => $name, 'deleted_at' => null])); } /** @@ -33,7 +33,7 @@ public function findOneByName(string $name): ?TestAutoincrementSdEntity */ public function findAllByName(string $name): array { - return $this->createEntities($this->findAllInternal( + return $this->createEntities($this->_findAll( 'name = :name', ['name' => $name] )); @@ -44,7 +44,7 @@ public function findAllByName(string $name): array */ public function findRecent(int $limit, int $offset): array { - return $this->createEntities($this->findAllInternal( + return $this->createEntities($this->_findAll( orderBy: 'id DESC', limit: $limit, offset: $offset, diff --git a/tests/TestStand/Tables/TestAutoincrementTable.php b/tests/TestStand/Tables/TestAutoincrementTable.php index c0768e6..86c9a50 100644 --- a/tests/TestStand/Tables/TestAutoincrementTable.php +++ b/tests/TestStand/Tables/TestAutoincrementTable.php @@ -22,12 +22,12 @@ protected function getConfig(): TableConfig public function findByPk(int $id): ?TestAutoincrementEntity { - return $this->createEntity($this->findByPkInternal($id)); + return $this->createEntity($this->_findByPk($id)); } public function findOneByName(string $name): ?TestAutoincrementEntity { - return $this->createEntity($this->findOneInternal(['name' => $name])); + return $this->createEntity($this->_findOne(['name' => $name])); } /** @@ -35,7 +35,7 @@ public function findOneByName(string $name): ?TestAutoincrementEntity */ public function findAllByName(string $name): array { - return $this->createEntities($this->findAllInternal( + return $this->createEntities($this->_findAll( whereString: 'name = :name', whereParams: ['name' => $name], orderBy: 'id', @@ -47,7 +47,7 @@ public function findAllByName(string $name): array */ public function findRecent(int $limit, int $offset): array { - return $this->createEntities($this->findAllInternal( + return $this->createEntities($this->_findAll( orderBy: ['id' => 'DESC'], limit: $limit, offset: $offset, @@ -56,7 +56,7 @@ public function findRecent(int $limit, int $offset): array public function countAllByName(string $name): int { - return $this->countAllInternal( + return $this->_countAll( 'name = :name', ['name' => $name] ); @@ -69,7 +69,7 @@ public function countAllByName(string $name): int */ public function findMulti(array $ids): array { - return $this->createEntities($this->findMultiInternal($ids), 'id'); + return $this->createEntities($this->_findMulti($ids), 'id'); } public function init(): bool diff --git a/tests/TestStand/Tables/TestCompositeCachedTable.php b/tests/TestStand/Tables/TestCompositeCachedTable.php index 936d0a9..532294f 100644 --- a/tests/TestStand/Tables/TestCompositeCachedTable.php +++ b/tests/TestStand/Tables/TestCompositeCachedTable.php @@ -30,7 +30,7 @@ protected function getFlushCacheKeys(TestCompositeEntity|AbstractEntity $entity) public function findOne(int $user_id, int $post_id): ?TestCompositeEntity { - return $this->createEntity($this->findOneCachedInternal([ + return $this->createEntity($this->_findOneCached([ 'user_id' => $user_id, 'post_id' => $post_id, ])); @@ -43,7 +43,7 @@ public function findAllByUser(int $userId): array { return array_map( fn (array $data) => TestCompositeEntity::fromArray($data), - $this->findAllCachedInternal( + $this->_findAllCached( 'user_id = :user_id', ['user_id' => $userId], ) @@ -52,7 +52,7 @@ public function findAllByUser(int $userId): array public function countAllByUser(int $userId): int { - return $this->countAllCachedInternal( + return $this->_countAllCached( 'user_id = :user_id', ['user_id' => $userId], ); diff --git a/tests/TestStand/Tables/TestCompositeSdCachedTable.php b/tests/TestStand/Tables/TestCompositeSdCachedTable.php index f4938f1..aefee62 100644 --- a/tests/TestStand/Tables/TestCompositeSdCachedTable.php +++ b/tests/TestStand/Tables/TestCompositeSdCachedTable.php @@ -38,7 +38,7 @@ protected function getFlushCacheKeys(TestCompositeSdEntity|AbstractEntity $entit public function findOne(int $user_id, int $post_id): ?TestCompositeSdEntity { - return $this->createEntity($this->findOneCachedInternal([ + return $this->createEntity($this->_findOneCached([ 'user_id' => $user_id, 'post_id' => $post_id, ])); @@ -51,7 +51,7 @@ public function findAllByUser(int $userId): array { return array_map( fn (array $data) => TestCompositeSdEntity::fromArray($data), - $this->findAllCachedInternal( + $this->_findAllCached( 'user_id = :user_id', ['user_id' => $userId], ) @@ -60,7 +60,7 @@ public function findAllByUser(int $userId): array public function countAllByUser(int $userId): int { - return $this->countAllCachedInternal( + return $this->_countAllCached( 'user_id = :user_id', ['user_id' => $userId], ); diff --git a/tests/TestStand/Tables/TestCompositeSdTable.php b/tests/TestStand/Tables/TestCompositeSdTable.php index 28d3123..3217f73 100644 --- a/tests/TestStand/Tables/TestCompositeSdTable.php +++ b/tests/TestStand/Tables/TestCompositeSdTable.php @@ -20,7 +20,7 @@ protected function getConfig(): TableConfig public function findOne(int $user_id, int $post_id): ?TestCompositeSdEntity { - return $this->createEntity($this->findOneInternal([ + return $this->createEntity($this->_findOne([ 'user_id' => $user_id, 'post_id' => $post_id, ])); @@ -31,7 +31,7 @@ public function findOne(int $user_id, int $post_id): ?TestCompositeSdEntity */ public function findAllByUser(int $userId): array { - return $this->createEntities($this->findAllInternal( + return $this->createEntities($this->_findAll( 'user_id = :user_id', ['user_id' => $userId], )); @@ -39,7 +39,7 @@ public function findAllByUser(int $userId): array public function countAllByUser(int $userId): int { - return $this->countAllInternal( + return $this->_countAll( 'user_id = :user_id', ['user_id' => $userId], ); diff --git a/tests/TestStand/Tables/TestCompositeTable.php b/tests/TestStand/Tables/TestCompositeTable.php index 359e450..50639be 100644 --- a/tests/TestStand/Tables/TestCompositeTable.php +++ b/tests/TestStand/Tables/TestCompositeTable.php @@ -32,7 +32,7 @@ public function delete(AbstractEntity|TestCompositeEntity &$entity): void public function findOne(int $user_id, int $post_id): ?TestCompositeEntity { - return $this->createEntity($this->findOneInternal(['user_id' => $user_id, 'post_id' => $post_id])); + return $this->createEntity($this->_findOne(['user_id' => $user_id, 'post_id' => $post_id])); } /** @@ -40,7 +40,7 @@ public function findOne(int $user_id, int $post_id): ?TestCompositeEntity */ public function findAllByUser(int $userId): array { - return $this->createEntities($this->findAllInternal( + return $this->createEntities($this->_findAll( 'user_id = :user_id', ['user_id' => $userId], )); @@ -48,7 +48,7 @@ public function findAllByUser(int $userId): array public function countAllByUser(int $userId): int { - return $this->countAllInternal( + return $this->_countAll( 'user_id = :user_id', ['user_id' => $userId, 'deleted_at' => null], ); @@ -61,7 +61,7 @@ public function countAllByUser(int $userId): int */ public function findMulti(array $ids): array { - return $this->createEntities($this->findMultiInternal($ids), 'post_id'); + return $this->createEntities($this->_findMulti($ids), 'post_id'); } public function init(): bool diff --git a/tests/TestStand/Tables/TestOptimisticLockTable.php b/tests/TestStand/Tables/TestOptimisticLockTable.php index 8303514..375a61a 100644 --- a/tests/TestStand/Tables/TestOptimisticLockTable.php +++ b/tests/TestStand/Tables/TestOptimisticLockTable.php @@ -21,7 +21,7 @@ protected function getConfig(): TableConfig public function findByPk(int $id): ?TestOptimisticLockEntity { - return $this->createEntity($this->findByPkInternal($id)); + return $this->createEntity($this->_findByPk($id)); } public function init(): bool diff --git a/tests/TestStand/Tables/TestUniqueCachedTable.php b/tests/TestStand/Tables/TestUniqueCachedTable.php index 7cb7fa3..cdcaccb 100644 --- a/tests/TestStand/Tables/TestUniqueCachedTable.php +++ b/tests/TestStand/Tables/TestUniqueCachedTable.php @@ -32,7 +32,7 @@ protected function getFlushCacheKeys(TestUniqueEntity|AbstractEntity $entity): a public function findByPk(UuidInterface $id): ?TestUniqueEntity { - return $this->createEntity($this->findByPkInternal($id)); + return $this->createEntity($this->_findByPk($id)); } /** @@ -40,7 +40,7 @@ public function findByPk(UuidInterface $id): ?TestUniqueEntity */ public function findAllByName(string $name): array { - return $this->createEntities($this->findAllCachedInternal( + return $this->createEntities($this->_findAllCached( 'name = :name', ['name' => $name], )); @@ -48,7 +48,7 @@ public function findAllByName(string $name): array public function countAllByName(string $name): int { - return $this->countAllCachedInternal( + return $this->_countAllCached( 'name = :name', ['name' => $name], ); diff --git a/tests/TestStand/Tables/TestUniqueSdCachedTable.php b/tests/TestStand/Tables/TestUniqueSdCachedTable.php index 016133d..f5877ec 100644 --- a/tests/TestStand/Tables/TestUniqueSdCachedTable.php +++ b/tests/TestStand/Tables/TestUniqueSdCachedTable.php @@ -32,7 +32,7 @@ protected function getFlushCacheKeys(TestUniqueSdEntity|AbstractEntity $entity): public function findByPk(UuidInterface $id): ?TestUniqueSdEntity { - return $this->createEntity($this->findByPkInternal($id)); + return $this->createEntity($this->_findByPk($id)); } /** @@ -40,7 +40,7 @@ public function findByPk(UuidInterface $id): ?TestUniqueSdEntity */ public function findAllByName(string $name): array { - return $this->createEntities($this->findAllCachedInternal( + return $this->createEntities($this->_findAllCached( 'name = :name', ['name' => $name], )); @@ -48,7 +48,7 @@ public function findAllByName(string $name): array public function countAllByName(string $name): int { - return $this->countAllCachedInternal( + return $this->_countAllCached( 'name = :name', ['name' => $name], ); diff --git a/tests/TestStand/Tables/TestUniqueSdTable.php b/tests/TestStand/Tables/TestUniqueSdTable.php index df078b9..5f61e0d 100644 --- a/tests/TestStand/Tables/TestUniqueSdTable.php +++ b/tests/TestStand/Tables/TestUniqueSdTable.php @@ -21,7 +21,7 @@ protected function getConfig(): TableConfig public function findByPk(UuidInterface $id): ?TestUniqueSdEntity { - return $this->createEntity($this->findByPkInternal($id)); + return $this->createEntity($this->_findByPk($id)); } /** @@ -29,7 +29,7 @@ public function findByPk(UuidInterface $id): ?TestUniqueSdEntity */ public function findAllByName(string $name): array { - return $this->createEntities($this->findAllInternal( + return $this->createEntities($this->_findAll( 'name = :name', ['name' => $name], )); diff --git a/tests/TestStand/Tables/TestUniqueTable.php b/tests/TestStand/Tables/TestUniqueTable.php index 7e223d9..befb866 100644 --- a/tests/TestStand/Tables/TestUniqueTable.php +++ b/tests/TestStand/Tables/TestUniqueTable.php @@ -32,7 +32,7 @@ protected function getConfig(): TableConfig public function findByPk(UuidInterface $id): ?TestUniqueEntity { - return $this->createEntity($this->findByPkInternal($id)); + return $this->createEntity($this->_findByPk($id)); } /** @@ -40,7 +40,7 @@ public function findByPk(UuidInterface $id): ?TestUniqueEntity */ public function findAllByName(string $name): array { - return $this->createEntities($this->findAllInternal( + return $this->createEntities($this->_findAll( 'name = :name', ['name' => $name], )); @@ -48,7 +48,7 @@ public function findAllByName(string $name): array public function countAllByName(string $name): int { - return $this->countAllInternal( + return $this->_countAll( 'name = :name', ['name' => $name], ); diff --git a/tests/TestStand/Tables/TestUpdateAtTable.php b/tests/TestStand/Tables/TestUpdateAtTable.php index 2fb74c4..48c7c2a 100644 --- a/tests/TestStand/Tables/TestUpdateAtTable.php +++ b/tests/TestStand/Tables/TestUpdateAtTable.php @@ -21,7 +21,7 @@ protected function getConfig(): TableConfig public function findByPk(string $id): ?TestUpdatedAtEntity { - return $this->createEntity($this->findByPkInternal($id)); + return $this->createEntity($this->_findByPk($id)); } public function init(): bool From e68dd36ea67e7c9513da091764597229b3c08aa3 Mon Sep 17 00:00:00 2001 From: Composite PHP Date: Sat, 28 Oct 2023 18:26:20 +0100 Subject: [PATCH 03/12] Rework $where param, add flexibility to use simple assoc array or flexible Where class --- src/AbstractCachedTable.php | 53 +++++------- src/AbstractTable.php | 62 +++++++++----- src/Where.php | 16 ++++ tests/Table/AbstractCachedTableTest.php | 5 +- tests/Table/AbstractTableTest.php | 84 ++++++++++++++----- tests/Table/AutoIncrementTableTest.php | 12 +++ tests/Table/CompositeTableTest.php | 46 +--------- tests/Table/UniqueTableTest.php | 20 +---- .../Entities/Enums/TestBackedEnum.php | 12 +++ .../TestStand/Entities/Enums/TestUnitEnum.php | 12 +++ .../Entities/TestCompositeEntity.php | 1 + .../Entities/TestCompositeSdEntity.php | 12 --- tests/TestStand/Entities/TestUniqueEntity.php | 1 + .../TestStand/Entities/TestUniqueSdEntity.php | 12 --- .../Tables/TestAutoincrementCachedTable.php | 25 +++--- .../Tables/TestAutoincrementSdCachedTable.php | 24 ++++-- .../Tables/TestAutoincrementSdTable.php | 5 +- .../Tables/TestAutoincrementTable.php | 18 ++-- .../Tables/TestCompositeCachedTable.php | 14 +--- .../Tables/TestCompositeSdCachedTable.php | 73 ---------------- .../TestStand/Tables/TestCompositeSdTable.php | 65 -------------- tests/TestStand/Tables/TestCompositeTable.php | 12 +-- .../Tables/TestUniqueCachedTable.php | 13 ++- .../Tables/TestUniqueSdCachedTable.php | 61 -------------- tests/TestStand/Tables/TestUniqueSdTable.php | 54 ------------ tests/TestStand/Tables/TestUniqueTable.php | 13 ++- 26 files changed, 254 insertions(+), 471 deletions(-) create mode 100644 src/Where.php create mode 100644 tests/TestStand/Entities/Enums/TestBackedEnum.php create mode 100644 tests/TestStand/Entities/Enums/TestUnitEnum.php delete mode 100644 tests/TestStand/Entities/TestCompositeSdEntity.php delete mode 100644 tests/TestStand/Entities/TestUniqueSdEntity.php delete mode 100644 tests/TestStand/Tables/TestCompositeSdCachedTable.php delete mode 100644 tests/TestStand/Tables/TestCompositeSdTable.php delete mode 100644 tests/TestStand/Tables/TestUniqueSdCachedTable.php delete mode 100644 tests/TestStand/Tables/TestUniqueSdTable.php diff --git a/src/AbstractCachedTable.php b/src/AbstractCachedTable.php index cca50ad..dd3a2b7 100644 --- a/src/AbstractCachedTable.php +++ b/src/AbstractCachedTable.php @@ -3,6 +3,7 @@ namespace Composite\DB; use Composite\DB\Exceptions\DbException; +use Composite\DB\Tests\TestStand\Entities\TestAutoincrementEntity; use Composite\Entity\AbstractEntity; use Psr\SimpleCache\CacheInterface; use Ramsey\Uuid\UuidInterface; @@ -115,37 +116,35 @@ protected function _findOneCached(array $condition, null|int|\DateInterval $ttl } /** - * @param array $whereParams + * @param array|Where $where * @param array|string $orderBy * @return array[] */ protected function _findAllCached( - string $whereString = '', - array $whereParams = [], + array|Where $where = [], array|string $orderBy = [], ?int $limit = null, null|int|\DateInterval $ttl = null, ): array { return $this->getCached( - $this->getListCacheKey($whereString, $whereParams, $orderBy, $limit), - fn() => $this->_findAll(whereString: $whereString, whereParams: $whereParams, orderBy: $orderBy, limit: $limit), + $this->getListCacheKey($where, $orderBy, $limit), + fn() => $this->_findAll(where: $where, orderBy: $orderBy, limit: $limit), $ttl, ); } /** - * @param array $whereParams + * @param array|Where $where */ - protected function _countAllCached( - string $whereString = '', - array $whereParams = [], + protected function _countByAllCached( + array|Where $where = [], null|int|\DateInterval $ttl = null, ): int { return (int)$this->getCached( - $this->getCountCacheKey($whereString, $whereParams), - fn() => $this->_countAll(whereString: $whereString, whereParams: $whereParams), + $this->getCountCacheKey($where), + fn() => $this->_countAll(where: $where), $ttl, ); } @@ -209,37 +208,35 @@ protected function getOneCacheKey(string|int|array|AbstractEntity|UuidInterface } /** - * @param array $whereParams + * @param array|Where $where * @param array|string $orderBy */ protected function getListCacheKey( - string $whereString = '', - array $whereParams = [], + array|Where $where = [], array|string $orderBy = [], ?int $limit = null ): string { - $wherePart = $this->prepareWhereKey($whereString, $whereParams); + $wherePart = is_array($where) ? $where : $this->prepareWhereKey($where); return $this->buildCacheKey( 'l', - $wherePart ?? 'all', + $wherePart ?: 'all', $orderBy ? ['ob' => $orderBy] : null, $limit ? ['limit' => $limit] : null, ); } /** - * @param array $whereParams + * @param array|Where $where */ protected function getCountCacheKey( - string $whereString = '', - array $whereParams = [], + array|Where $where = [], ): string { - $wherePart = $this->prepareWhereKey($whereString, $whereParams); + $wherePart = is_array($where) ? $where : $this->prepareWhereKey($where); return $this->buildCacheKey( 'c', - $wherePart ?? 'all', + $wherePart ?: 'all', ); } @@ -280,18 +277,12 @@ private function formatStringForCacheKey(string $string): string return trim((string)preg_replace('/_+/', '_', $string), '_'); } - /** - * @param array $whereParams - */ - private function prepareWhereKey(string $whereString, array $whereParams): ?string + private function prepareWhereKey(Where $where): string { - if (!$whereString) { - return null; - } return str_replace( - array_map(fn (string $key): string => ':' . $key, array_keys($whereParams)), - array_values($whereParams), - $whereString, + array_map(fn (string $key): string => ':' . $key, array_keys($where->params)), + array_values($where->params), + $where->string, ); } } diff --git a/src/AbstractTable.php b/src/AbstractTable.php index 4362f42..59e89e3 100644 --- a/src/AbstractTable.php +++ b/src/AbstractTable.php @@ -7,7 +7,6 @@ use Composite\Entity\Helpers\DateTimeHelper; use Composite\Entity\AbstractEntity; use Composite\DB\Exceptions\DbException; -use Composite\Entity\Exceptions\EntityException; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Query\QueryBuilder; @@ -15,6 +14,8 @@ abstract class AbstractTable { + private const COMPARISON_SIGNS = ['=', '!=', '>', '<', '>=', '<=', '<>']; + protected readonly TableConfig $config; private ?QueryBuilder $selectQuery = null; @@ -170,15 +171,17 @@ public function deleteMany(array $entities): void } /** - * @param array $whereParams + * @param array|Where $where * @throws \Doctrine\DBAL\Exception */ - protected function _countAll(string $whereString = '', array $whereParams = []): int + protected function _countAll(array|Where $where = []): int { $query = $this->select('COUNT(*)'); - if ($whereString) { - $query->where($whereString); - foreach ($whereParams as $param => $value) { + if (is_array($where)) { + $this->buildWhere($query, $where); + } else { + $query->where($where->string); + foreach ($where->params as $param => $value) { $query->setParameter($param, $value); } } @@ -187,7 +190,6 @@ protected function _countAll(string $whereString = '', array $whereParams = []): /** * @return array|null - * @throws EntityException * @throws \Doctrine\DBAL\Exception */ protected function _findByPk(mixed $pk): ?array @@ -226,23 +228,24 @@ protected function _findMulti(array $pkList): array } /** - * @param array $whereParams + * @param array|Where $where * @param array|string $orderBy * @return list> * @throws \Doctrine\DBAL\Exception */ protected function _findAll( - string $whereString = '', - array $whereParams = [], + array|Where $where = [], array|string $orderBy = [], ?int $limit = null, ?int $offset = null, ): array { $query = $this->select(); - if ($whereString) { - $query->where($whereString); - foreach ($whereParams as $param => $value) { + if (is_array($where)) { + $this->buildWhere($query, $where); + } else { + $query->where($where->string); + foreach ($where->params as $param => $value) { $query->setParameter($param, $value); } } @@ -301,7 +304,6 @@ final protected function createEntities(mixed $data, ?string $keyColumnName = nu /** * @param int|string|array|AbstractEntity $data * @return array - * @throws EntityException */ protected function getPkCondition(int|string|array|AbstractEntity|UuidInterface $data): array { @@ -335,14 +337,34 @@ protected function select(string $select = '*'): QueryBuilder private function buildWhere(QueryBuilder $query, array $where): void { foreach ($where as $column => $value) { - if ($value === null) { - $query->andWhere("$column IS NULL"); + if ($value instanceof \BackedEnum) { + $value = $value->value; + } elseif ($value instanceof \UnitEnum) { + $value = $value->name; + } + + if (is_null($value)) { + $query->andWhere($column . ' IS NULL'); + } elseif (is_array($value) && count($value) === 2 && \in_array($value[0], self::COMPARISON_SIGNS)) { + $comparisonSign = $value[0]; + $comparisonValue = $value[1]; + + // Handle special case of "!= null" + if ($comparisonSign === '!=' && is_null($comparisonValue)) { + $query->andWhere($column . ' IS NOT NULL'); + } else { + $query->andWhere($column . ' ' . $comparisonSign . ' :' . $column) + ->setParameter($column, $comparisonValue); + } } elseif (is_array($value)) { - $query - ->andWhere($query->expr()->in($column, $value)); + $placeholders = []; + foreach ($value as $index => $val) { + $placeholders[] = ':' . $column . $index; + $query->setParameter($column . $index, $val); + } + $query->andWhere($column . ' IN(' . implode(', ', $placeholders) . ')'); } else { - $query - ->andWhere("$column = :" . $column) + $query->andWhere($column . ' = :' . $column) ->setParameter($column, $value); } } diff --git a/src/Where.php b/src/Where.php new file mode 100644 index 0000000..41b545f --- /dev/null +++ b/src/Where.php @@ -0,0 +1,16 @@ + 0" + * @param array $params params with placeholders, which used in $string, example: ['user_id' => 123], + */ + public function __construct( + public readonly string $string, + public readonly array $params, + ) { + } +} \ No newline at end of file diff --git a/tests/Table/AbstractCachedTableTest.php b/tests/Table/AbstractCachedTableTest.php index 6c65846..8ca3877 100644 --- a/tests/Table/AbstractCachedTableTest.php +++ b/tests/Table/AbstractCachedTableTest.php @@ -7,6 +7,7 @@ use Composite\DB\Exceptions\DbException; use Composite\DB\Tests\TestStand\Entities; use Composite\DB\Tests\TestStand\Tables; +use Composite\DB\Where; use Composite\Entity\AbstractEntity; use Composite\DB\Tests\Helpers; use Ramsey\Uuid\Uuid; @@ -91,7 +92,7 @@ public function test_getCountCacheKey(string $whereString, array $whereParams, s { $table = new Tables\TestAutoincrementCachedTable(Helpers\CacheHelper::getCache()); $reflectionMethod = new \ReflectionMethod($table, 'getCountCacheKey'); - $actual = $reflectionMethod->invoke($table, $whereString, $whereParams); + $actual = $reflectionMethod->invoke($table, new Where($whereString, $whereParams)); $this->assertEquals($expected, $actual); } @@ -150,7 +151,7 @@ public function test_getListCacheKey(string $whereString, array $whereArray, arr { $table = new Tables\TestAutoincrementCachedTable(Helpers\CacheHelper::getCache()); $reflectionMethod = new \ReflectionMethod($table, 'getListCacheKey'); - $actual = $reflectionMethod->invoke($table, $whereString, $whereArray, $orderBy, $limit); + $actual = $reflectionMethod->invoke($table, new Where($whereString, $whereArray), $orderBy, $limit); $this->assertEquals($expected, $actual); } diff --git a/tests/Table/AbstractTableTest.php b/tests/Table/AbstractTableTest.php index 5c45eca..cb48987 100644 --- a/tests/Table/AbstractTableTest.php +++ b/tests/Table/AbstractTableTest.php @@ -51,16 +51,6 @@ public static function getPkCondition_dataProvider(): array Entities\TestAutoincrementSdEntity::fromArray(['id' => 123, 'name' => 'John']), ['id' => 123], ], - [ - new Tables\TestCompositeSdTable(), - new Entities\TestCompositeSdEntity(user_id: 123, post_id: 456, message: 'Text'), - ['user_id' => 123, 'post_id' => 456], - ], - [ - new Tables\TestUniqueSdTable(), - new Entities\TestUniqueSdEntity(id: $uuid, name: 'John'), - ['id' => $uuid->toString()], - ], ]; } @@ -123,29 +113,81 @@ public function test_buildWhere($where, $expectedSQL, $expectedParams) $buildWhereReflection->invokeArgs($table, [$queryBuilder, $where]); $this->assertEquals($expectedSQL, $queryBuilder->getSQL()); + $this->assertEquals($expectedParams, $queryBuilder->getParameters()); } public static function buildWhere_dataProvider(): array { return [ - // Test when value is null + // Scalar value [ - ['column1' => null], - 'SELECT * FROM Strict WHERE column1 IS NULL', + ['column' => 1], + 'SELECT * FROM Strict WHERE column = :column', + ['column' => 1] + ], + + // Null value + [ + ['column' => null], + 'SELECT * FROM Strict WHERE column IS NULL', [] ], - // Test when value is an array + + // Greater than comparison [ - ['column1' => [1, 2, 3]], - 'SELECT * FROM Strict WHERE column1 IN (1, 2, 3)', - [1, 2, 3] + ['column' => ['>', 0]], + 'SELECT * FROM Strict WHERE column > :column', + ['column' => 0] ], - // Test when value is a single value + + // Less than comparison [ - ['column1' => 'value1'], - 'SELECT * FROM Strict WHERE column1 = :column1', - ['value1'] + ['column' => ['<', 5]], + 'SELECT * FROM Strict WHERE column < :column', + ['column' => 5] ], + + // Greater than or equal to comparison + [ + ['column' => ['>=', 3]], + 'SELECT * FROM Strict WHERE column >= :column', + ['column' => 3] + ], + + // Less than or equal to comparison + [ + ['column' => ['<=', 7]], + 'SELECT * FROM Strict WHERE column <= :column', + ['column' => 7] + ], + + // Not equal to comparison with scalar value + [ + ['column' => ['<>', 10]], + 'SELECT * FROM Strict WHERE column <> :column', + ['column' => 10] + ], + + // Not equal to comparison with null + [ + ['column' => ['!=', null]], + 'SELECT * FROM Strict WHERE column IS NOT NULL', + [] + ], + + // IN condition + [ + ['column' => [1, 2, 3]], + 'SELECT * FROM Strict WHERE column IN(:column0, :column1, :column2)', + ['column0' => 1, 'column1' => 2, 'column2' => 3] + ], + + // Multiple conditions + [ + ['column1' => 1, 'column2' => null, 'column3' => ['>', 5]], + 'SELECT * FROM Strict WHERE (column1 = :column1) AND (column2 IS NULL) AND (column3 > :column3)', + ['column1' => 1, 'column3' => 5] + ] ]; } } \ No newline at end of file diff --git a/tests/Table/AutoIncrementTableTest.php b/tests/Table/AutoIncrementTableTest.php index ec85178..98690f2 100644 --- a/tests/Table/AutoIncrementTableTest.php +++ b/tests/Table/AutoIncrementTableTest.php @@ -85,6 +85,18 @@ public function test_crud(AbstractTable&IAutoincrementTable $table, string $clas $preLastEntity = $table->findRecent(1, 1); $this->assertEquals($e1, $preLastEntity[0]); + if ($tableConfig->hasSoftDelete()) { + $e1->name = 'Exception'; + $exceptionThrown = false; + try { + $table->deleteMany([$e1, $e2]); + } catch (\Exception) { + $exceptionThrown = true; + } + $this->assertTrue($exceptionThrown); + $e1->name = Helpers\StringHelper::getUniqueName(); + } + $table->deleteMany([$e1, $e2]); if ($tableConfig->hasSoftDelete()) { diff --git a/tests/Table/CompositeTableTest.php b/tests/Table/CompositeTableTest.php index afc9f97..224e604 100644 --- a/tests/Table/CompositeTableTest.php +++ b/tests/Table/CompositeTableTest.php @@ -19,29 +19,20 @@ public static function crud_dataProvider(): array new Tables\TestCompositeTable(), Entities\TestCompositeEntity::class, ], - [ - new Tables\TestCompositeSdTable(), - Entities\TestCompositeSdEntity::class, - ], [ new Tables\TestCompositeCachedTable(Helpers\CacheHelper::getCache()), Entities\TestCompositeEntity::class, ], - [ - new Tables\TestCompositeSdCachedTable(Helpers\CacheHelper::getCache()), - Entities\TestCompositeSdEntity::class, - ], ]; } /** - * @param class-string $class + * @param class-string $class * @dataProvider crud_dataProvider */ public function test_crud(AbstractTable&ICompositeTable $table, string $class): void { $table->truncate(); - $tableConfig = TableConfig::fromEntitySchema($class::schema()); $entity = new $class( user_id: mt_rand(1, 1000000), @@ -57,14 +48,7 @@ public function test_crud(AbstractTable&ICompositeTable $table, string $class): $this->assertEntityExists($table, $entity); $table->delete($entity); - - if ($tableConfig->hasSoftDelete()) { - /** @var Entities\TestCompositeSdEntity $deletedEntity */ - $deletedEntity = $table->findOne(user_id: $entity->user_id, post_id: $entity->post_id); - $this->assertTrue($deletedEntity->isDeleted()); - } else { - $this->assertEntityNotExists($table, $entity); - } + $this->assertEntityNotExists($table, $entity); $e1 = new $class( user_id: mt_rand(1, 1000000), @@ -84,32 +68,10 @@ public function test_crud(AbstractTable&ICompositeTable $table, string $class): $this->assertEntityExists($table, $e1); $this->assertEntityExists($table, $e2); - if ($tableConfig->hasSoftDelete()) { - $e1->message = 'Exception'; - $exceptionThrown = false; - try { - $table->deleteMany([$e1, $e2]); - } catch (\Exception) { - $exceptionThrown = true; - } - $this->assertTrue($exceptionThrown); - $e1->message = Helpers\StringHelper::getUniqueName(); - } - $table->deleteMany([$e1, $e2]); - if ($tableConfig->hasSoftDelete()) { - /** @var Entities\TestCompositeSdEntity $deletedEntity1 */ - $deletedEntity1 = $table->findOne(user_id: $e1->user_id, post_id: $e1->post_id); - $this->assertTrue($deletedEntity1->isDeleted()); - - /** @var Entities\TestCompositeSdEntity $deletedEntity2 */ - $deletedEntity2 = $table->findOne(user_id: $e2->user_id, post_id: $e2->post_id); - $this->assertTrue($deletedEntity2->isDeleted()); - } else { - $this->assertEntityNotExists($table, $e1); - $this->assertEntityNotExists($table, $e2); - } + $this->assertEntityNotExists($table, $e1); + $this->assertEntityNotExists($table, $e2); } public function test_getMulti(): void diff --git a/tests/Table/UniqueTableTest.php b/tests/Table/UniqueTableTest.php index e9cd434..d4073fd 100644 --- a/tests/Table/UniqueTableTest.php +++ b/tests/Table/UniqueTableTest.php @@ -19,29 +19,20 @@ public static function crud_dataProvider(): array new Tables\TestUniqueTable(), Entities\TestUniqueEntity::class, ], - [ - new Tables\TestUniqueSdTable(), - Entities\TestUniqueSdEntity::class, - ], [ new Tables\TestUniqueCachedTable(Helpers\CacheHelper::getCache()), Entities\TestUniqueEntity::class, ], - [ - new Tables\TestUniqueSdCachedTable(Helpers\CacheHelper::getCache()), - Entities\TestUniqueSdEntity::class, - ], ]; } /** - * @param class-string $class + * @param class-string $class * @dataProvider crud_dataProvider */ public function test_crud(AbstractTable&IUniqueTable $table, string $class): void { $table->truncate(); - $tableConfig = TableConfig::fromEntitySchema($class::schema()); $entity = new $class( id: Uuid::uuid4(), @@ -56,14 +47,7 @@ public function test_crud(AbstractTable&IUniqueTable $table, string $class): voi $this->assertEntityExists($table, $entity); $table->delete($entity); - - if ($tableConfig->hasSoftDelete()) { - /** @var Entities\TestUniqueSdEntity $deletedEntity */ - $deletedEntity = $table->findByPk($entity->id); - $this->assertTrue($deletedEntity->isDeleted()); - } else { - $this->assertEntityNotExists($table, $entity); - } + $this->assertEntityNotExists($table, $entity); } public function test_multiSave(): void diff --git a/tests/TestStand/Entities/Enums/TestBackedEnum.php b/tests/TestStand/Entities/Enums/TestBackedEnum.php new file mode 100644 index 0000000..107d79f --- /dev/null +++ b/tests/TestStand/Entities/Enums/TestBackedEnum.php @@ -0,0 +1,12 @@ +getOneCacheKey(['name' => $entity->name]), - $this->getListCacheKey('name = :name', ['name' => $entity->name]), - $this->getCountCacheKey('name = :name', ['name' => $entity->name]), + $this->getListCacheKey(new Where('name = :name', ['name' => $entity->name])), + $this->getCountCacheKey(new Where('name = :name', ['name' => $entity->name])), ]; $oldName = $entity->getOldValue('name'); if (!$entity->isNew() && $oldName !== $entity->name) { $keys[] = $this->getOneCacheKey(['name' => $oldName]); - $keys[] = $this->getListCacheKey('name = :name', ['name' => $oldName]); - $keys[] = $this->getCountCacheKey('name = :name', ['name' => $oldName]); + $keys[] = $this->getListCacheKey(new Where('name = :name', ['name' => $oldName])); + $keys[] = $this->getCountCacheKey(new Where('name = :name', ['name' => $oldName])); } return $keys; } @@ -47,14 +48,21 @@ public function findOneByName(string $name): ?TestAutoincrementEntity return $this->createEntity($this->_findOneCached(['name' => $name])); } + public function delete(TestAutoincrementEntity|AbstractEntity &$entity): void + { + if ($entity->name === 'Exception') { + throw new \Exception('Test Exception'); + } + parent::delete($entity); + } + /** * @return TestAutoincrementEntity[] */ public function findAllByName(string $name): array { return $this->createEntities($this->_findAllCached( - 'name = :name', - ['name' => $name], + new Where('name = :name', ['name' => $name]) )); } @@ -72,10 +80,7 @@ public function findRecent(int $limit, int $offset): array public function countAllByName(string $name): int { - return $this->_countAllCached( - 'name = :name', - ['name' => $name], - ); + return $this->_countByAllCached(new Where('name = :name', ['name' => $name])); } /** diff --git a/tests/TestStand/Tables/TestAutoincrementSdCachedTable.php b/tests/TestStand/Tables/TestAutoincrementSdCachedTable.php index c646e9a..4f432b7 100644 --- a/tests/TestStand/Tables/TestAutoincrementSdCachedTable.php +++ b/tests/TestStand/Tables/TestAutoincrementSdCachedTable.php @@ -6,6 +6,7 @@ use Composite\DB\TableConfig; use Composite\DB\Tests\TestStand\Entities\TestAutoincrementSdEntity; use Composite\DB\Tests\TestStand\Interfaces\IAutoincrementTable; +use Composite\DB\Where; use Composite\Entity\AbstractEntity; class TestAutoincrementSdCachedTable extends AbstractCachedTable implements IAutoincrementTable @@ -25,14 +26,14 @@ protected function getFlushCacheKeys(TestAutoincrementSdEntity|AbstractEntity $e { $keys = [ $this->getOneCacheKey(['name' => $entity->name]), - $this->getListCacheKey('name = :name', ['name' => $entity->name]), - $this->getCountCacheKey('name = :name', ['name' => $entity->name]), + $this->getListCacheKey(new Where('name = :name', ['name' => $entity->name])), + $this->getCountCacheKey(new Where('name = :name', ['name' => $entity->name])), ]; $oldName = $entity->getOldValue('name'); if ($oldName !== null && $oldName !== $entity->name) { $keys[] = $this->getOneCacheKey(['name' => $oldName]); - $keys[] = $this->getListCacheKey('name = :name', ['name' => $oldName]); - $keys[] = $this->getCountCacheKey('name = :name', ['name' => $oldName]); + $keys[] = $this->getListCacheKey(new Where('name = :name', ['name' => $oldName])); + $keys[] = $this->getCountCacheKey(new Where('name = :name', ['name' => $oldName])); } return $keys; } @@ -47,14 +48,21 @@ public function findOneByName(string $name): ?TestAutoincrementSdEntity return $this->createEntity($this->_findOneCached(['name' => $name])); } + public function delete(TestAutoincrementSdEntity|AbstractEntity &$entity): void + { + if ($entity->name === 'Exception') { + throw new \Exception('Test Exception'); + } + parent::delete($entity); + } + /** * @return TestAutoincrementSdEntity[] */ public function findAllByName(string $name): array { return $this->createEntities($this->_findAllCached( - 'name = :name', - ['name' => $name, 'deleted_at' => null], + new Where('name = :name', ['name' => $name, 'deleted_at' => null]), )); } @@ -72,10 +80,10 @@ public function findRecent(int $limit, int $offset): array public function countAllByName(string $name): int { - return $this->_countAllCached( + return $this->_countByAllCached(new Where( 'name = :name', ['name' => $name], - ); + )); } public function truncate(): void diff --git a/tests/TestStand/Tables/TestAutoincrementSdTable.php b/tests/TestStand/Tables/TestAutoincrementSdTable.php index c45e826..519f063 100644 --- a/tests/TestStand/Tables/TestAutoincrementSdTable.php +++ b/tests/TestStand/Tables/TestAutoincrementSdTable.php @@ -4,6 +4,7 @@ use Composite\DB\TableConfig; use Composite\DB\Tests\TestStand\Entities\TestAutoincrementSdEntity; +use Composite\DB\Where; class TestAutoincrementSdTable extends TestAutoincrementTable { @@ -34,8 +35,7 @@ public function findOneByName(string $name): ?TestAutoincrementSdEntity public function findAllByName(string $name): array { return $this->createEntities($this->_findAll( - 'name = :name', - ['name' => $name] + new Where('name = :name', ['name' => $name]) )); } @@ -45,6 +45,7 @@ public function findAllByName(string $name): array public function findRecent(int $limit, int $offset): array { return $this->createEntities($this->_findAll( + where: ['deleted_at' => null], orderBy: 'id DESC', limit: $limit, offset: $offset, diff --git a/tests/TestStand/Tables/TestAutoincrementTable.php b/tests/TestStand/Tables/TestAutoincrementTable.php index 86c9a50..de0e994 100644 --- a/tests/TestStand/Tables/TestAutoincrementTable.php +++ b/tests/TestStand/Tables/TestAutoincrementTable.php @@ -6,6 +6,8 @@ use Composite\DB\TableConfig; use Composite\DB\Tests\TestStand\Entities\TestAutoincrementEntity; use Composite\DB\Tests\TestStand\Interfaces\IAutoincrementTable; +use Composite\DB\Where; +use Composite\Entity\AbstractEntity; class TestAutoincrementTable extends AbstractTable implements IAutoincrementTable { @@ -30,14 +32,21 @@ public function findOneByName(string $name): ?TestAutoincrementEntity return $this->createEntity($this->_findOne(['name' => $name])); } + public function delete(AbstractEntity|TestAutoincrementEntity &$entity): void + { + if ($entity->name === 'Exception') { + throw new \Exception('Test Exception'); + } + parent::delete($entity); + } + /** * @return TestAutoincrementEntity[] */ public function findAllByName(string $name): array { return $this->createEntities($this->_findAll( - whereString: 'name = :name', - whereParams: ['name' => $name], + where: new Where('name = :name', ['name' => $name]), orderBy: 'id', )); } @@ -56,10 +65,7 @@ public function findRecent(int $limit, int $offset): array public function countAllByName(string $name): int { - return $this->_countAll( - 'name = :name', - ['name' => $name] - ); + return $this->_countAll(new Where('name = :name', ['name' => $name])); } /** diff --git a/tests/TestStand/Tables/TestCompositeCachedTable.php b/tests/TestStand/Tables/TestCompositeCachedTable.php index 532294f..57f3203 100644 --- a/tests/TestStand/Tables/TestCompositeCachedTable.php +++ b/tests/TestStand/Tables/TestCompositeCachedTable.php @@ -23,8 +23,8 @@ protected function getConfig(): TableConfig protected function getFlushCacheKeys(TestCompositeEntity|AbstractEntity $entity): array { return [ - $this->getListCacheKey('user_id = :user_id', ['user_id' => $entity->user_id]), - $this->getCountCacheKey('user_id = :user_id', ['user_id' => $entity->user_id]), + $this->getListCacheKey(['user_id' => $entity->user_id]), + $this->getCountCacheKey(['user_id' => $entity->user_id]), ]; } @@ -43,19 +43,13 @@ public function findAllByUser(int $userId): array { return array_map( fn (array $data) => TestCompositeEntity::fromArray($data), - $this->_findAllCached( - 'user_id = :user_id', - ['user_id' => $userId], - ) + $this->_findAllCached(['user_id' => $userId]) ); } public function countAllByUser(int $userId): int { - return $this->_countAllCached( - 'user_id = :user_id', - ['user_id' => $userId], - ); + return $this->_countByAllCached(['user_id' => $userId]); } public function truncate(): void diff --git a/tests/TestStand/Tables/TestCompositeSdCachedTable.php b/tests/TestStand/Tables/TestCompositeSdCachedTable.php deleted file mode 100644 index aefee62..0000000 --- a/tests/TestStand/Tables/TestCompositeSdCachedTable.php +++ /dev/null @@ -1,73 +0,0 @@ -init(); - } - - public function save(AbstractEntity|TestCompositeSdEntity &$entity): void - { - if ($entity->message === 'Exception') { - throw new \Exception('Test Exception'); - } - parent::save($entity); - } - - protected function getConfig(): TableConfig - { - return TableConfig::fromEntitySchema(TestCompositeSdEntity::schema()); - } - - protected function getFlushCacheKeys(TestCompositeSdEntity|AbstractEntity $entity): array - { - return [ - $this->getListCacheKey('user_id = :user_id', ['user_id' => $entity->user_id]), - $this->getCountCacheKey('user_id = :user_id', ['user_id' => $entity->user_id]), - ]; - } - - public function findOne(int $user_id, int $post_id): ?TestCompositeSdEntity - { - return $this->createEntity($this->_findOneCached([ - 'user_id' => $user_id, - 'post_id' => $post_id, - ])); - } - - /** - * @return TestCompositeSdEntity[] - */ - public function findAllByUser(int $userId): array - { - return array_map( - fn (array $data) => TestCompositeSdEntity::fromArray($data), - $this->_findAllCached( - 'user_id = :user_id', - ['user_id' => $userId], - ) - ); - } - - public function countAllByUser(int $userId): int - { - return $this->_countAllCached( - 'user_id = :user_id', - ['user_id' => $userId], - ); - } - - public function truncate(): void - { - $this->getConnection()->executeStatement("DELETE FROM {$this->getTableName()} WHERE 1"); - } -} \ No newline at end of file diff --git a/tests/TestStand/Tables/TestCompositeSdTable.php b/tests/TestStand/Tables/TestCompositeSdTable.php deleted file mode 100644 index 3217f73..0000000 --- a/tests/TestStand/Tables/TestCompositeSdTable.php +++ /dev/null @@ -1,65 +0,0 @@ -init(); - } - - protected function getConfig(): TableConfig - { - return TableConfig::fromEntitySchema(TestCompositeSdEntity::schema()); - } - - public function findOne(int $user_id, int $post_id): ?TestCompositeSdEntity - { - return $this->createEntity($this->_findOne([ - 'user_id' => $user_id, - 'post_id' => $post_id, - ])); - } - - /** - * @return TestCompositeSdEntity[] - */ - public function findAllByUser(int $userId): array - { - return $this->createEntities($this->_findAll( - 'user_id = :user_id', - ['user_id' => $userId], - )); - } - - public function countAllByUser(int $userId): int - { - return $this->_countAll( - 'user_id = :user_id', - ['user_id' => $userId], - ); - } - - public function init(): bool - { - $this->getConnection()->executeStatement( - " - CREATE TABLE IF NOT EXISTS {$this->getTableName()} - ( - `user_id` integer not null, - `post_id` integer not null, - `message` VARCHAR(255) DEFAULT '' NOT NULL, - `created_at` TIMESTAMP NOT NULL, - `deleted_at` TIMESTAMP NULL DEFAULT NULL, - CONSTRAINT TestCompositeSd PRIMARY KEY (`user_id`, `post_id`, `deleted_at`) - ); - " - ); - return true; - } -} \ No newline at end of file diff --git a/tests/TestStand/Tables/TestCompositeTable.php b/tests/TestStand/Tables/TestCompositeTable.php index 50639be..2fe6910 100644 --- a/tests/TestStand/Tables/TestCompositeTable.php +++ b/tests/TestStand/Tables/TestCompositeTable.php @@ -3,6 +3,7 @@ namespace Composite\DB\Tests\TestStand\Tables; use Composite\DB\TableConfig; +use Composite\DB\Tests\TestStand\Entities\Enums\TestUnitEnum; use Composite\DB\Tests\TestStand\Entities\TestCompositeEntity; use Composite\DB\Tests\TestStand\Interfaces\ICompositeTable; use Composite\Entity\AbstractEntity; @@ -40,18 +41,12 @@ public function findOne(int $user_id, int $post_id): ?TestCompositeEntity */ public function findAllByUser(int $userId): array { - return $this->createEntities($this->_findAll( - 'user_id = :user_id', - ['user_id' => $userId], - )); + return $this->createEntities($this->_findAll(['user_id' => $userId, 'status' => TestUnitEnum::ACTIVE])); } public function countAllByUser(int $userId): int { - return $this->_countAll( - 'user_id = :user_id', - ['user_id' => $userId, 'deleted_at' => null], - ); + return $this->_countAll(['user_id' => $userId]); } /** @@ -73,6 +68,7 @@ public function init(): bool `user_id` integer not null, `post_id` integer not null, `message` VARCHAR(255) DEFAULT '' NOT NULL, + `status` VARCHAR(16) DEFAULT 'ACTIVE' NOT NULL, `created_at` TIMESTAMP NOT NULL, CONSTRAINT TestComposite PRIMARY KEY (`user_id`, `post_id`) ); diff --git a/tests/TestStand/Tables/TestUniqueCachedTable.php b/tests/TestStand/Tables/TestUniqueCachedTable.php index cdcaccb..cd7c901 100644 --- a/tests/TestStand/Tables/TestUniqueCachedTable.php +++ b/tests/TestStand/Tables/TestUniqueCachedTable.php @@ -6,6 +6,7 @@ use Composite\DB\TableConfig; use Composite\DB\Tests\TestStand\Entities\TestUniqueEntity; use Composite\DB\Tests\TestStand\Interfaces\IUniqueTable; +use Composite\DB\Where; use Composite\Entity\AbstractEntity; use Ramsey\Uuid\UuidInterface; @@ -25,8 +26,8 @@ protected function getConfig(): TableConfig protected function getFlushCacheKeys(TestUniqueEntity|AbstractEntity $entity): array { return [ - $this->getListCacheKey('name = :name', ['name' => $entity->name]), - $this->getCountCacheKey('name = :name', ['name' => $entity->name]), + $this->getListCacheKey(new Where('name = :name', ['name' => $entity->name])), + $this->getCountCacheKey(new Where('name = :name', ['name' => $entity->name])), ]; } @@ -41,17 +42,13 @@ public function findByPk(UuidInterface $id): ?TestUniqueEntity public function findAllByName(string $name): array { return $this->createEntities($this->_findAllCached( - 'name = :name', - ['name' => $name], + new Where('name = :name', ['name' => $name]) )); } public function countAllByName(string $name): int { - return $this->_countAllCached( - 'name = :name', - ['name' => $name], - ); + return $this->_countByAllCached(new Where('name = :name', ['name' => $name])); } public function truncate(): void diff --git a/tests/TestStand/Tables/TestUniqueSdCachedTable.php b/tests/TestStand/Tables/TestUniqueSdCachedTable.php deleted file mode 100644 index f5877ec..0000000 --- a/tests/TestStand/Tables/TestUniqueSdCachedTable.php +++ /dev/null @@ -1,61 +0,0 @@ -init(); - } - - protected function getConfig(): TableConfig - { - return TableConfig::fromEntitySchema(TestUniqueSdEntity::schema()); - } - - protected function getFlushCacheKeys(TestUniqueSdEntity|AbstractEntity $entity): array - { - return [ - $this->getListCacheKey('name = :name', ['name' => $entity->name]), - $this->getCountCacheKey('name = :name', ['name' => $entity->name]), - ]; - } - - public function findByPk(UuidInterface $id): ?TestUniqueSdEntity - { - return $this->createEntity($this->_findByPk($id)); - } - - /** - * @return TestUniqueSdEntity[] - */ - public function findAllByName(string $name): array - { - return $this->createEntities($this->_findAllCached( - 'name = :name', - ['name' => $name], - )); - } - - public function countAllByName(string $name): int - { - return $this->_countAllCached( - 'name = :name', - ['name' => $name], - ); - } - - public function truncate(): void - { - $this->getConnection()->executeStatement("DELETE FROM {$this->getTableName()} WHERE 1"); - } -} \ No newline at end of file diff --git a/tests/TestStand/Tables/TestUniqueSdTable.php b/tests/TestStand/Tables/TestUniqueSdTable.php deleted file mode 100644 index 5f61e0d..0000000 --- a/tests/TestStand/Tables/TestUniqueSdTable.php +++ /dev/null @@ -1,54 +0,0 @@ -init(); - } - - protected function getConfig(): TableConfig - { - return TableConfig::fromEntitySchema(TestUniqueSdEntity::schema()); - } - - public function findByPk(UuidInterface $id): ?TestUniqueSdEntity - { - return $this->createEntity($this->_findByPk($id)); - } - - /** - * @return TestUniqueSdEntity[] - */ - public function findAllByName(string $name): array - { - return $this->createEntities($this->_findAll( - 'name = :name', - ['name' => $name], - )); - } - - public function init(): bool - { - $this->getConnection()->executeStatement( - " - CREATE TABLE IF NOT EXISTS {$this->getTableName()} - ( - `id` VARCHAR(32) NOT NULL, - `name` VARCHAR(255) NOT NULL, - `created_at` TIMESTAMP NOT NULL, - `deleted_at` TIMESTAMP NULL DEFAULT NULL, - CONSTRAINT TestUniqueSd PRIMARY KEY (`id`, `deleted_at`) - ); - " - ); - return true; - } -} \ No newline at end of file diff --git a/tests/TestStand/Tables/TestUniqueTable.php b/tests/TestStand/Tables/TestUniqueTable.php index befb866..0ad6264 100644 --- a/tests/TestStand/Tables/TestUniqueTable.php +++ b/tests/TestStand/Tables/TestUniqueTable.php @@ -4,8 +4,10 @@ use Composite\DB\AbstractTable; use Composite\DB\TableConfig; +use Composite\DB\Tests\TestStand\Entities\Enums\TestBackedEnum; use Composite\DB\Tests\TestStand\Entities\TestUniqueEntity; use Composite\DB\Tests\TestStand\Interfaces\IUniqueTable; +use Composite\DB\Where; use Composite\Entity\AbstractEntity; use Ramsey\Uuid\UuidInterface; @@ -40,18 +42,12 @@ public function findByPk(UuidInterface $id): ?TestUniqueEntity */ public function findAllByName(string $name): array { - return $this->createEntities($this->_findAll( - 'name = :name', - ['name' => $name], - )); + return $this->createEntities($this->_findAll(['name' => $name, 'status' => TestBackedEnum::ACTIVE])); } public function countAllByName(string $name): int { - return $this->_countAll( - 'name = :name', - ['name' => $name], - ); + return $this->_countAll(new Where('name = :name', ['name' => $name])); } public function init(): bool @@ -62,6 +58,7 @@ public function init(): bool ( `id` VARCHAR(255) NOT NULL CONSTRAINT TestUnique_pk PRIMARY KEY, `name` VARCHAR(255) NOT NULL, + `status` VARCHAR(16) DEFAULT 'Active' NOT NULL, `created_at` TIMESTAMP NOT NULL ); " From c81df32ae1ce7bfc709ffa9188bc25c3d95353da Mon Sep 17 00:00:00 2001 From: Composite PHP Date: Sat, 28 Oct 2023 18:26:39 +0100 Subject: [PATCH 04/12] Display warnings in unit tests --- phpunit.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/phpunit.xml b/phpunit.xml index f68b1fa..9b42ad0 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -3,6 +3,7 @@ xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd" bootstrap="tests/bootstrap.php" executionOrder="depends,defects" beStrictAboutOutputDuringTests="true" failOnRisky="true" failOnWarning="true" colors="true" cacheDirectory=".phpunit.cache" requireCoverageMetadata="false" + displayDetailsOnTestsThatTriggerWarnings="true" beStrictAboutCoverageMetadata="true"> From 93cc75b9c059d8c1f71e20ed3795a27061258f79 Mon Sep 17 00:00:00 2001 From: Composite PHP Date: Sat, 28 Oct 2023 18:26:59 +0100 Subject: [PATCH 05/12] Update documentation --- doc/cache.md | 1 - doc/example.md | 6 ++---- doc/table.md | 23 ++++++++++++++++++++--- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/doc/cache.md b/doc/cache.md index 7b64d1f..84b123d 100644 --- a/doc/cache.md +++ b/doc/cache.md @@ -55,7 +55,6 @@ class PostsTable extends AbstractCachedTable public function findAllFeatured(): array { return $this->createEntities($this->_findAll( - 'is_featured = :is_featured', ['is_featured' => true], )); } diff --git a/doc/example.md b/doc/example.md index 22a63fc..8709821 100644 --- a/doc/example.md +++ b/doc/example.md @@ -52,16 +52,14 @@ class UsersTable extends \Composite\DB\AbstractTable public function findAllActive(): array { return $this->createEntities($this->_findAll( - 'status = :status', - ['status' => Status::ACTIVE->name], + ['status' => Status::ACTIVE], )); } public function countAllActive(): int { return $this->_countAll( - 'status = :status', - ['status' => Status::ACTIVE->name], + ['status' => Status::ACTIVE], ); } diff --git a/doc/table.md b/doc/table.md index fbd7d1b..e6aec2e 100644 --- a/doc/table.md +++ b/doc/table.md @@ -68,14 +68,31 @@ Example with internal helper: public function findAllActiveAdults(): array { $rows = $this->_findAll( - 'age > :age AND status = :status', - ['age' => 18, 'status' => Status::ACTIVE->name], + new Where( + 'age > :age AND status = :status', + ['age' => 18, 'status' => Status::ACTIVE->name], + ) ); return $this->createEntities($rows); } ``` -Example with pure query builder +Or it might be simplified to: +```php +/** + * @return User[] + */ +public function findAllActiveAdults(): array +{ + $rows = $this->_findAll([ + 'age' => ['>', 18], + 'status' => Status:ACTIVE, + ]); + return $this->createEntities($rows); +} +``` + +Or you can use standard Doctrine QueryBuilder ```php /** * @return User[] From ccc8ca5688040fe0a18a092e928806f4b948911a Mon Sep 17 00:00:00 2001 From: Composite PHP Date: Sat, 28 Oct 2023 19:20:08 +0100 Subject: [PATCH 06/12] Minor improve AbstractCachedTableTest --- tests/Table/AbstractCachedTableTest.php | 55 ++++++++++++++++--------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/tests/Table/AbstractCachedTableTest.php b/tests/Table/AbstractCachedTableTest.php index 8ca3877..f73c4ec 100644 --- a/tests/Table/AbstractCachedTableTest.php +++ b/tests/Table/AbstractCachedTableTest.php @@ -58,41 +58,44 @@ public static function getCountCacheKey_dataProvider(): array { return [ [ - '', [], 'sqlite.TestAutoincrement.v1.c.all', ], [ - 'name = :name', - ['name' => 'John'], + new Where('name = :name', ['name' => 'John']), 'sqlite.TestAutoincrement.v1.c.name_eq_john', ], [ - ' name = :name ', ['name' => 'John'], + 'sqlite.TestAutoincrement.v1.c.name_john', + ], + [ + new Where(' name = :name ', ['name' => 'John']), 'sqlite.TestAutoincrement.v1.c.name_eq_john', ], [ - 'name=:name', - ['name' => 'John'], + new Where('name=:name', ['name' => 'John']), 'sqlite.TestAutoincrement.v1.c.name_eq_john', ], [ - 'name = :name AND id > :id', - ['name' => 'John', 'id' => 10], + new Where('name = :name AND id > :id', ['name' => 'John', 'id' => 10]), 'sqlite.TestAutoincrement.v1.c.name_eq_john_and_id_gt_10', ], + [ + ['name' => 'John', 'id' => ['>', 10]], + 'sqlite.TestAutoincrement.v1.c.name_john_id_gt_10', + ], ]; } /** * @dataProvider getCountCacheKey_dataProvider */ - public function test_getCountCacheKey(string $whereString, array $whereParams, string $expected): void + public function test_getCountCacheKey(array|Where $where, string $expected): void { $table = new Tables\TestAutoincrementCachedTable(Helpers\CacheHelper::getCache()); $reflectionMethod = new \ReflectionMethod($table, 'getCountCacheKey'); - $actual = $reflectionMethod->invoke($table, new Where($whereString, $whereParams)); + $actual = $reflectionMethod->invoke($table, $where); $this->assertEquals($expected, $actual); } @@ -100,43 +103,55 @@ public static function getListCacheKey_dataProvider(): array { return [ [ - '', [], [], null, 'sqlite.TestAutoincrement.v1.l.all', ], [ - '', [], [], 10, 'sqlite.TestAutoincrement.v1.l.all.limit_10', ], [ - '', [], ['id' => 'DESC'], 10, 'sqlite.TestAutoincrement.v1.l.all.ob_id_desc.limit_10', ], [ - 'name = :name', + new Where('name = :name', ['name' => 'John']), + [], + null, + 'sqlite.TestAutoincrement.v1.l.name_eq_john', + ], + [ ['name' => 'John'], [], null, + 'sqlite.TestAutoincrement.v1.l.name_john', + ], + [ + new Where('name = :name', ['name' => 'John']), + [], + null, 'sqlite.TestAutoincrement.v1.l.name_eq_john', ], [ - 'name = :name AND id > :id', - ['name' => 'John', 'id' => 10], + new Where('name = :name AND id > :id', ['name' => 'John', 'id' => 10]), [], null, 'sqlite.TestAutoincrement.v1.l.name_eq_john_and_id_gt_10', ], [ - 'name = :name AND id > :id', - ['name' => 'John', 'id' => 10], + ['name' => 'John', 'id' => ['>', 10]], + [], + null, + 'sqlite.TestAutoincrement.v1.l.name_john_id_gt_10', + ], + [ + new Where('name = :name AND id > :id', ['name' => 'John', 'id' => 10]), ['id' => 'ASC'], 20, 'bbcf331b765b682da02c4d21dbaa3342bf2c3f18', //sha1('sqlite.TestAutoincrement.v1.l.name_eq_john_and_id_gt_10.ob_id_asc.limit_20') @@ -147,11 +162,11 @@ public static function getListCacheKey_dataProvider(): array /** * @dataProvider getListCacheKey_dataProvider */ - public function test_getListCacheKey(string $whereString, array $whereArray, array $orderBy, ?int $limit, string $expected): void + public function test_getListCacheKey(array|Where $where, array $orderBy, ?int $limit, string $expected): void { $table = new Tables\TestAutoincrementCachedTable(Helpers\CacheHelper::getCache()); $reflectionMethod = new \ReflectionMethod($table, 'getListCacheKey'); - $actual = $reflectionMethod->invoke($table, new Where($whereString, $whereArray), $orderBy, $limit); + $actual = $reflectionMethod->invoke($table, $where, $orderBy, $limit); $this->assertEquals($expected, $actual); } From 9a26bbe4dff1d3f10dea1e6d5fa4637232e9142c Mon Sep 17 00:00:00 2001 From: Composite PHP Date: Sat, 28 Oct 2023 19:37:20 +0100 Subject: [PATCH 07/12] Add .scrutinizer.yml to export-ignore --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 1989899..d09a5f7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,7 @@ /.gitattributes export-ignore /.gitignore export-ignore /.github export-ignore +/.scrutinizer.yml export-ignore /doc export-ignore /phpunit.xml export-ignore /tests export-ignore From 1faf615424bcf6f4638a1f69ffd35cf296cacd7a Mon Sep 17 00:00:00 2001 From: Composite PHP Date: Sat, 28 Oct 2023 20:04:13 +0100 Subject: [PATCH 08/12] Add possibility to use Where class in _findOne internal method --- src/AbstractTable.php | 76 +++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/AbstractTable.php b/src/AbstractTable.php index 59e89e3..49f225b 100644 --- a/src/AbstractTable.php +++ b/src/AbstractTable.php @@ -199,12 +199,12 @@ protected function _findByPk(mixed $pk): ?array } /** - * @param array $where + * @param array|Where $where * @param array|string $orderBy * @return array|null * @throws \Doctrine\DBAL\Exception */ - protected function _findOne(array $where, array|string $orderBy = []): ?array + protected function _findOne(array|Where $where, array|string $orderBy = []): ?array { $query = $this->select(); $this->buildWhere($query, $where); @@ -241,14 +241,7 @@ protected function _findAll( ): array { $query = $this->select(); - if (is_array($where)) { - $this->buildWhere($query, $where); - } else { - $query->where($where->string); - foreach ($where->params as $param => $value) { - $query->setParameter($param, $value); - } - } + $this->buildWhere($query, $where); $this->applyOrderBy($query, $orderBy); if ($limit > 0) { $query->setMaxResults($limit); @@ -332,40 +325,47 @@ protected function select(string $select = '*'): QueryBuilder } /** - * @param array $where + * @param array|Where $where */ - private function buildWhere(QueryBuilder $query, array $where): void + private function buildWhere(QueryBuilder $query, array|Where $where): void { - foreach ($where as $column => $value) { - if ($value instanceof \BackedEnum) { - $value = $value->value; - } elseif ($value instanceof \UnitEnum) { - $value = $value->name; - } + if (is_array($where)) { + foreach ($where as $column => $value) { + if ($value instanceof \BackedEnum) { + $value = $value->value; + } elseif ($value instanceof \UnitEnum) { + $value = $value->name; + } - if (is_null($value)) { - $query->andWhere($column . ' IS NULL'); - } elseif (is_array($value) && count($value) === 2 && \in_array($value[0], self::COMPARISON_SIGNS)) { - $comparisonSign = $value[0]; - $comparisonValue = $value[1]; + if (is_null($value)) { + $query->andWhere($column . ' IS NULL'); + } elseif (is_array($value) && count($value) === 2 && \in_array($value[0], self::COMPARISON_SIGNS)) { + $comparisonSign = $value[0]; + $comparisonValue = $value[1]; - // Handle special case of "!= null" - if ($comparisonSign === '!=' && is_null($comparisonValue)) { - $query->andWhere($column . ' IS NOT NULL'); + // Handle special case of "!= null" + if ($comparisonSign === '!=' && is_null($comparisonValue)) { + $query->andWhere($column . ' IS NOT NULL'); + } else { + $query->andWhere($column . ' ' . $comparisonSign . ' :' . $column) + ->setParameter($column, $comparisonValue); + } + } elseif (is_array($value)) { + $placeholders = []; + foreach ($value as $index => $val) { + $placeholders[] = ':' . $column . $index; + $query->setParameter($column . $index, $val); + } + $query->andWhere($column . ' IN(' . implode(', ', $placeholders) . ')'); } else { - $query->andWhere($column . ' ' . $comparisonSign . ' :' . $column) - ->setParameter($column, $comparisonValue); - } - } elseif (is_array($value)) { - $placeholders = []; - foreach ($value as $index => $val) { - $placeholders[] = ':' . $column . $index; - $query->setParameter($column . $index, $val); + $query->andWhere($column . ' = :' . $column) + ->setParameter($column, $value); } - $query->andWhere($column . ' IN(' . implode(', ', $placeholders) . ')'); - } else { - $query->andWhere($column . ' = :' . $column) - ->setParameter($column, $value); + } + } else { + $query->where($where->string); + foreach ($where->params as $param => $value) { + $query->setParameter($param, $value); } } } From 7a6a66ad6c57b0131f88bfeb4db942c105905823 Mon Sep 17 00:00:00 2001 From: Composite PHP Date: Sat, 28 Oct 2023 20:10:02 +0100 Subject: [PATCH 09/12] Rename Where.string to Where.condition --- src/AbstractCachedTable.php | 2 +- src/AbstractTable.php | 4 ++-- src/Where.php | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/AbstractCachedTable.php b/src/AbstractCachedTable.php index dd3a2b7..6a6022c 100644 --- a/src/AbstractCachedTable.php +++ b/src/AbstractCachedTable.php @@ -282,7 +282,7 @@ private function prepareWhereKey(Where $where): string return str_replace( array_map(fn (string $key): string => ':' . $key, array_keys($where->params)), array_values($where->params), - $where->string, + $where->condition, ); } } diff --git a/src/AbstractTable.php b/src/AbstractTable.php index 49f225b..dad1381 100644 --- a/src/AbstractTable.php +++ b/src/AbstractTable.php @@ -180,7 +180,7 @@ protected function _countAll(array|Where $where = []): int if (is_array($where)) { $this->buildWhere($query, $where); } else { - $query->where($where->string); + $query->where($where->condition); foreach ($where->params as $param => $value) { $query->setParameter($param, $value); } @@ -363,7 +363,7 @@ private function buildWhere(QueryBuilder $query, array|Where $where): void } } } else { - $query->where($where->string); + $query->where($where->condition); foreach ($where->params as $param => $value) { $query->setParameter($param, $value); } diff --git a/src/Where.php b/src/Where.php index 41b545f..147b3aa 100644 --- a/src/Where.php +++ b/src/Where.php @@ -5,11 +5,11 @@ class Where { /** - * @param string $string free format where string, example: "user_id = :user_id OR user_id > 0" - * @param array $params params with placeholders, which used in $string, example: ['user_id' => 123], + * @param string $condition free format where string, example: "user_id = :user_id OR user_id > 0" + * @param array $params params with placeholders, which used in $condition, example: ['user_id' => 123], */ public function __construct( - public readonly string $string, + public readonly string $condition, public readonly array $params, ) { } From e0446683211efe7b43abf63ffac3a265b0c68791 Mon Sep 17 00:00:00 2001 From: Composite PHP Date: Sat, 11 Nov 2023 21:52:38 +0000 Subject: [PATCH 10/12] phpstan fixes --- src/MultiQuery/MultiSelect.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/MultiQuery/MultiSelect.php b/src/MultiQuery/MultiSelect.php index 59459eb..c5171a7 100644 --- a/src/MultiQuery/MultiSelect.php +++ b/src/MultiQuery/MultiSelect.php @@ -12,6 +12,10 @@ class MultiSelect { private readonly QueryBuilder $queryBuilder; + /** + * @param array|array $condition + * @throws DbException + */ public function __construct( Connection $connection, TableConfig $tableConfig, @@ -32,7 +36,7 @@ public function __construct( } /** @var \Composite\Entity\Columns\AbstractColumn $pkColumn */ $pkColumn = reset($pkColumns); - $preparedPkValues = array_map(fn ($pk) => $pkColumn->uncast($pk), $condition); + $preparedPkValues = array_map(fn ($pk) => (string)$pkColumn->uncast($pk), $condition); $query->andWhere($query->expr()->in($pkColumn->name, $preparedPkValues)); } else { $expressions = []; From 019af835d163bf5357b2381ba28ddff136805156 Mon Sep 17 00:00:00 2001 From: Ivan Vasilkov Date: Sat, 18 Nov 2023 23:15:29 +0000 Subject: [PATCH 11/12] remove redundant use --- tests/Table/UniqueTableTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Table/UniqueTableTest.php b/tests/Table/UniqueTableTest.php index d4073fd..186ff97 100644 --- a/tests/Table/UniqueTableTest.php +++ b/tests/Table/UniqueTableTest.php @@ -3,7 +3,6 @@ namespace Composite\DB\Tests\Table; use Composite\DB\AbstractTable; -use Composite\DB\TableConfig; use Composite\DB\Tests\Helpers; use Composite\DB\Tests\TestStand\Entities; use Composite\DB\Tests\TestStand\Tables; From b477a3a795dfdc8707499daac22a6bc0b8d43e0d Mon Sep 17 00:00:00 2001 From: Composite PHP Date: Sun, 19 Nov 2023 10:03:06 +0000 Subject: [PATCH 12/12] Move raw selects to separate trait and build entities by default --- doc/cache.md | 6 +- doc/example.md | 6 +- doc/table.md | 10 +- src/AbstractCachedTable.php | 43 +++--- src/AbstractTable.php | 146 +++++------------- src/SelectRawTrait.php | 131 ++++++++++++++++ tests/Table/AbstractCachedTableTest.php | 4 +- .../Tables/TestAutoincrementCachedTable.php | 16 +- .../Tables/TestAutoincrementSdCachedTable.php | 12 +- .../Tables/TestAutoincrementSdTable.php | 12 +- .../Tables/TestAutoincrementTable.php | 14 +- .../Tables/TestCompositeCachedTable.php | 9 +- tests/TestStand/Tables/TestCompositeTable.php | 6 +- .../Tables/TestOptimisticLockTable.php | 2 +- .../Tables/TestUniqueCachedTable.php | 6 +- tests/TestStand/Tables/TestUniqueTable.php | 4 +- tests/TestStand/Tables/TestUpdateAtTable.php | 2 +- 17 files changed, 237 insertions(+), 192 deletions(-) create mode 100644 src/SelectRawTrait.php diff --git a/doc/cache.md b/doc/cache.md index 84b123d..a5d6340 100644 --- a/doc/cache.md +++ b/doc/cache.md @@ -46,7 +46,7 @@ class PostsTable extends AbstractCachedTable public function findByPk(int $id): ?Post { - return $this->createEntity($this->_findByPkCached($id)); + return $this->_findByPkCached($id); } /** @@ -54,9 +54,7 @@ class PostsTable extends AbstractCachedTable */ public function findAllFeatured(): array { - return $this->createEntities($this->_findAll( - ['is_featured' => true], - )); + return $this->_findAll(['is_featured' => true]); } public function countAllFeatured(): int diff --git a/doc/example.md b/doc/example.md index 8709821..4709c67 100644 --- a/doc/example.md +++ b/doc/example.md @@ -43,7 +43,7 @@ class UsersTable extends \Composite\DB\AbstractTable public function findByPk(int $id): ?User { - return $this->createEntity($this->_findByPk($id)); + return $this->_findByPk($id); } /** @@ -51,9 +51,7 @@ class UsersTable extends \Composite\DB\AbstractTable */ public function findAllActive(): array { - return $this->createEntities($this->_findAll( - ['status' => Status::ACTIVE], - )); + return $this->_findAll(['status' => Status::ACTIVE]); } public function countAllActive(): int diff --git a/doc/table.md b/doc/table.md index e6aec2e..852c55b 100644 --- a/doc/table.md +++ b/doc/table.md @@ -38,7 +38,7 @@ class UsersTable extends AbstractTable public function findOne(int $id): ?User { - return $this->createEntity($this->_findOne($id)); + return $this->_findByPk($id); } /** @@ -46,7 +46,7 @@ class UsersTable extends AbstractTable */ public function findAll(): array { - return $this->createEntities($this->_findAll()); + return $this->_findAll(); } public function countAll(): int @@ -67,13 +67,12 @@ Example with internal helper: */ public function findAllActiveAdults(): array { - $rows = $this->_findAll( + return $this->_findAll( new Where( 'age > :age AND status = :status', ['age' => 18, 'status' => Status::ACTIVE->name], ) ); - return $this->createEntities($rows); } ``` @@ -84,11 +83,10 @@ Or it might be simplified to: */ public function findAllActiveAdults(): array { - $rows = $this->_findAll([ + return $this->_findAll([ 'age' => ['>', 18], 'status' => Status:ACTIVE, ]); - return $this->createEntities($rows); } ``` diff --git a/src/AbstractCachedTable.php b/src/AbstractCachedTable.php index 6a6022c..590403e 100644 --- a/src/AbstractCachedTable.php +++ b/src/AbstractCachedTable.php @@ -2,14 +2,14 @@ namespace Composite\DB; -use Composite\DB\Exceptions\DbException; -use Composite\DB\Tests\TestStand\Entities\TestAutoincrementEntity; use Composite\Entity\AbstractEntity; use Psr\SimpleCache\CacheInterface; use Ramsey\Uuid\UuidInterface; abstract class AbstractCachedTable extends AbstractTable { + use SelectRawTrait; + protected const CACHE_VERSION = 1; public function __construct( @@ -94,44 +94,47 @@ private function collectCacheKeysByEntity(AbstractEntity $entity): array } /** - * @return array|null + * @return AbstractEntity|null */ - protected function _findByPkCached(mixed $pk, null|int|\DateInterval $ttl = null): ?array + protected function _findByPkCached(mixed $pk, null|int|\DateInterval $ttl = null): mixed { return $this->_findOneCached($this->getPkCondition($pk), $ttl); } /** - * @param array $condition + * @param array $where * @param int|\DateInterval|null $ttl - * @return array|null + * @return AbstractEntity|null */ - protected function _findOneCached(array $condition, null|int|\DateInterval $ttl = null): ?array + protected function _findOneCached(array $where, null|int|\DateInterval $ttl = null): mixed { - return $this->getCached( - $this->getOneCacheKey($condition), - fn() => $this->_findOne($condition), + $row = $this->getCached( + $this->getOneCacheKey($where), + fn() => $this->_findOneRaw($where), $ttl, - ) ?: null; + ); + return $this->createEntity($row); } /** * @param array|Where $where * @param array|string $orderBy - * @return array[] + * @return array|array */ protected function _findAllCached( array|Where $where = [], array|string $orderBy = [], ?int $limit = null, null|int|\DateInterval $ttl = null, + ?string $keyColumnName = null, ): array { - return $this->getCached( + $rows = $this->getCached( $this->getListCacheKey($where, $orderBy, $limit), - fn() => $this->_findAll(where: $where, orderBy: $orderBy, limit: $limit), + fn() => $this->_findAllRaw(where: $where, orderBy: $orderBy, limit: $limit), $ttl, ); + return $this->createEntities($rows, $keyColumnName); } /** @@ -165,10 +168,14 @@ protected function getCached(string $cacheKey, callable $dataCallback, null|int| /** * @param mixed[] $ids * @param int|\DateInterval|null $ttl - * @return array> + * @return array|array * @throws \Psr\SimpleCache\InvalidArgumentException */ - protected function _findMultiCached(array $ids, null|int|\DateInterval $ttl = null): array + protected function _findMultiCached( + array $ids, + null|int|\DateInterval $ttl = null, + ?string $keyColumnName = null, + ): array { $result = $cacheKeys = $foundIds = []; foreach ($ids as $id) { @@ -191,7 +198,7 @@ protected function _findMultiCached(array $ids, null|int|\DateInterval $ttl = nu $result[] = $row; } } - return $result; + return $this->createEntities($result, $keyColumnName); } /** @@ -271,7 +278,7 @@ protected function buildCacheKey(mixed ...$parts): string private function formatStringForCacheKey(string $string): string { - $string = mb_strtolower($string); + $string = strtolower($string); $string = str_replace(['!=', '<>', '>', '<', '='], ['_not_', '_not_', '_gt_', '_lt_', '_eq_'], $string); $string = (string)preg_replace('/\W/', '_', $string); return trim((string)preg_replace('/_+/', '_', $string), '_'); diff --git a/src/AbstractTable.php b/src/AbstractTable.php index dad1381..df058a1 100644 --- a/src/AbstractTable.php +++ b/src/AbstractTable.php @@ -9,15 +9,13 @@ use Composite\DB\Exceptions\DbException; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; -use Doctrine\DBAL\Query\QueryBuilder; use Ramsey\Uuid\UuidInterface; abstract class AbstractTable { - private const COMPARISON_SIGNS = ['=', '!=', '>', '<', '>=', '<=', '<>']; + use SelectRawTrait; protected readonly TableConfig $config; - private ?QueryBuilder $selectQuery = null; abstract protected function getConfig(): TableConfig; @@ -189,10 +187,10 @@ protected function _countAll(array|Where $where = []): int } /** - * @return array|null * @throws \Doctrine\DBAL\Exception + * @return AbstractEntity|null */ - protected function _findByPk(mixed $pk): ?array + protected function _findByPk(mixed $pk): mixed { $where = $this->getPkCondition($pk); return $this->_findOne($where); @@ -201,55 +199,54 @@ protected function _findByPk(mixed $pk): ?array /** * @param array|Where $where * @param array|string $orderBy - * @return array|null + * @return AbstractEntity|null * @throws \Doctrine\DBAL\Exception */ - protected function _findOne(array|Where $where, array|string $orderBy = []): ?array + protected function _findOne(array|Where $where, array|string $orderBy = []): mixed { - $query = $this->select(); - $this->buildWhere($query, $where); - $this->applyOrderBy($query, $orderBy); - return $query->fetchAssociative() ?: null; + return $this->createEntity($this->_findOneRaw($where, $orderBy)); } /** * @param array> $pkList - * @return array> + * @return array| array * @throws DbException * @throws \Doctrine\DBAL\Exception */ - protected function _findMulti(array $pkList): array + protected function _findMulti(array $pkList, ?string $keyColumnName = null): array { if (!$pkList) { return []; } $multiSelect = new MultiSelect($this->getConnection(), $this->config, $pkList); - return $multiSelect->getQueryBuilder()->executeQuery()->fetchAllAssociative(); + return $this->createEntities( + $multiSelect->getQueryBuilder()->executeQuery()->fetchAllAssociative(), + $keyColumnName, + ); } /** * @param array|Where $where * @param array|string $orderBy - * @return list> - * @throws \Doctrine\DBAL\Exception + * @return array| array */ protected function _findAll( array|Where $where = [], array|string $orderBy = [], ?int $limit = null, ?int $offset = null, + ?string $keyColumnName = null, ): array { - $query = $this->select(); - $this->buildWhere($query, $where); - $this->applyOrderBy($query, $orderBy); - if ($limit > 0) { - $query->setMaxResults($limit); - } - if ($offset > 0) { - $query->setFirstResult($offset); - } - return $query->executeQuery()->fetchAllAssociative(); + return $this->createEntities( + data: $this->_findAllRaw( + where: $where, + orderBy: $orderBy, + limit: $limit, + offset: $offset, + ), + keyColumnName: $keyColumnName, + ); } final protected function createEntity(mixed $data): mixed @@ -279,13 +276,18 @@ final protected function createEntities(mixed $data, ?string $keyColumnName = nu $entityClass = $this->config->entityClass; $result = []; foreach ($data as $datum) { - if (!is_array($datum)) { - continue; - } - if ($keyColumnName && isset($datum[$keyColumnName])) { - $result[$datum[$keyColumnName]] = $entityClass::fromArray($datum); - } else { - $result[] = $entityClass::fromArray($datum); + if (is_array($datum)) { + if ($keyColumnName && isset($datum[$keyColumnName])) { + $result[$datum[$keyColumnName]] = $entityClass::fromArray($datum); + } else { + $result[] = $entityClass::fromArray($datum); + } + } elseif ($datum instanceof $this->config->entityClass) { + if ($keyColumnName && property_exists($datum, $keyColumnName)) { + $result[$datum->{$keyColumnName}] = $datum; + } else { + $result[] = $datum; + } } } } catch (\Throwable) { @@ -316,60 +318,6 @@ protected function getPkCondition(int|string|array|AbstractEntity|UuidInterface return $condition; } - protected function select(string $select = '*'): QueryBuilder - { - if ($this->selectQuery === null) { - $this->selectQuery = $this->getConnection()->createQueryBuilder()->from($this->getTableName()); - } - return (clone $this->selectQuery)->select($select); - } - - /** - * @param array|Where $where - */ - private function buildWhere(QueryBuilder $query, array|Where $where): void - { - if (is_array($where)) { - foreach ($where as $column => $value) { - if ($value instanceof \BackedEnum) { - $value = $value->value; - } elseif ($value instanceof \UnitEnum) { - $value = $value->name; - } - - if (is_null($value)) { - $query->andWhere($column . ' IS NULL'); - } elseif (is_array($value) && count($value) === 2 && \in_array($value[0], self::COMPARISON_SIGNS)) { - $comparisonSign = $value[0]; - $comparisonValue = $value[1]; - - // Handle special case of "!= null" - if ($comparisonSign === '!=' && is_null($comparisonValue)) { - $query->andWhere($column . ' IS NOT NULL'); - } else { - $query->andWhere($column . ' ' . $comparisonSign . ' :' . $column) - ->setParameter($column, $comparisonValue); - } - } elseif (is_array($value)) { - $placeholders = []; - foreach ($value as $index => $val) { - $placeholders[] = ':' . $column . $index; - $query->setParameter($column . $index, $val); - } - $query->andWhere($column . ' IN(' . implode(', ', $placeholders) . ')'); - } else { - $query->andWhere($column . ' = :' . $column) - ->setParameter($column, $value); - } - } - } else { - $query->where($where->condition); - foreach ($where->params as $param => $value) { - $query->setParameter($param, $value); - } - } - } - private function checkUpdatedAt(AbstractEntity $entity): void { if ($this->config->hasUpdatedAt() && property_exists($entity, 'updated_at') && $entity->updated_at === null) { @@ -392,28 +340,4 @@ private function formatData(array $data): array } return $data; } - - /** - * @param array|string $orderBy - */ - private function applyOrderBy(QueryBuilder $query, string|array $orderBy): void - { - if (!$orderBy) { - return; - } - if (is_array($orderBy)) { - foreach ($orderBy as $column => $direction) { - $query->addOrderBy($column, $direction); - } - } else { - foreach (explode(',', $orderBy) as $orderByPart) { - $orderByPart = trim($orderByPart); - if (preg_match('/(.+)\s(asc|desc)$/i', $orderByPart, $orderByPartMatch)) { - $query->addOrderBy($orderByPartMatch[1], $orderByPartMatch[2]); - } else { - $query->addOrderBy($orderByPart); - } - } - } - } } diff --git a/src/SelectRawTrait.php b/src/SelectRawTrait.php new file mode 100644 index 0000000..e1db259 --- /dev/null +++ b/src/SelectRawTrait.php @@ -0,0 +1,131 @@ +', '<', '>=', '<=', '<>']; + + private ?QueryBuilder $selectQuery = null; + + protected function select(string $select = '*'): QueryBuilder + { + if ($this->selectQuery === null) { + $this->selectQuery = $this->getConnection()->createQueryBuilder()->from($this->getTableName()); + } + return (clone $this->selectQuery)->select($select); + } + + /** + * @param array|Where $where + * @param array|string $orderBy + * @return array|null + * @throws \Doctrine\DBAL\Exception + */ + private function _findOneRaw(array|Where $where, array|string $orderBy = []): ?array + { + $query = $this->select(); + $this->buildWhere($query, $where); + $this->applyOrderBy($query, $orderBy); + return $query->fetchAssociative() ?: null; + } + + /** + * @param array|Where $where + * @param array|string $orderBy + * @return list> + * @throws \Doctrine\DBAL\Exception + */ + private function _findAllRaw( + array|Where $where = [], + array|string $orderBy = [], + ?int $limit = null, + ?int $offset = null, + ): array + { + $query = $this->select(); + $this->buildWhere($query, $where); + $this->applyOrderBy($query, $orderBy); + if ($limit > 0) { + $query->setMaxResults($limit); + } + if ($offset > 0) { + $query->setFirstResult($offset); + } + return $query->executeQuery()->fetchAllAssociative(); + } + + + /** + * @param array|Where $where + */ + private function buildWhere(QueryBuilder $query, array|Where $where): void + { + if (is_array($where)) { + foreach ($where as $column => $value) { + if ($value instanceof \BackedEnum) { + $value = $value->value; + } elseif ($value instanceof \UnitEnum) { + $value = $value->name; + } + + if (is_null($value)) { + $query->andWhere($column . ' IS NULL'); + } elseif (is_array($value) && count($value) === 2 && \in_array($value[0], $this->comparisonSigns)) { + $comparisonSign = $value[0]; + $comparisonValue = $value[1]; + + // Handle special case of "!= null" + if ($comparisonSign === '!=' && is_null($comparisonValue)) { + $query->andWhere($column . ' IS NOT NULL'); + } else { + $query->andWhere($column . ' ' . $comparisonSign . ' :' . $column) + ->setParameter($column, $comparisonValue); + } + } elseif (is_array($value)) { + $placeholders = []; + foreach ($value as $index => $val) { + $placeholders[] = ':' . $column . $index; + $query->setParameter($column . $index, $val); + } + $query->andWhere($column . ' IN(' . implode(', ', $placeholders) . ')'); + } else { + $query->andWhere($column . ' = :' . $column) + ->setParameter($column, $value); + } + } + } else { + $query->where($where->condition); + foreach ($where->params as $param => $value) { + $query->setParameter($param, $value); + } + } + } + + /** + * @param array|string $orderBy + */ + private function applyOrderBy(QueryBuilder $query, string|array $orderBy): void + { + if (!$orderBy) { + return; + } + if (is_array($orderBy)) { + foreach ($orderBy as $column => $direction) { + $query->addOrderBy($column, $direction); + } + } else { + foreach (explode(',', $orderBy) as $orderByPart) { + $orderByPart = trim($orderByPart); + if (preg_match('/(.+)\s(asc|desc)$/i', $orderByPart, $orderByPartMatch)) { + $query->addOrderBy($orderByPartMatch[1], $orderByPartMatch[2]); + } else { + $query->addOrderBy($orderByPart); + } + } + } + } +} \ No newline at end of file diff --git a/tests/Table/AbstractCachedTableTest.php b/tests/Table/AbstractCachedTableTest.php index f73c4ec..826ede0 100644 --- a/tests/Table/AbstractCachedTableTest.php +++ b/tests/Table/AbstractCachedTableTest.php @@ -272,8 +272,8 @@ public function test_findMulti(): void $table->save($e1); $table->save($e2); - $multi1 = $table->findMulti([$e1->id]); - $this->assertEquals($e1, $multi1[0]); + $multi1 = $table->findMulti([$e1->id], 'id'); + $this->assertEquals($e1, $multi1[$e1->id]); $multi2 = $table->findMulti([$e1->id, $e2->id]); $this->assertEquals($e1, $multi2[0]); diff --git a/tests/TestStand/Tables/TestAutoincrementCachedTable.php b/tests/TestStand/Tables/TestAutoincrementCachedTable.php index ebb3d96..fb05d76 100644 --- a/tests/TestStand/Tables/TestAutoincrementCachedTable.php +++ b/tests/TestStand/Tables/TestAutoincrementCachedTable.php @@ -40,12 +40,12 @@ protected function getFlushCacheKeys(TestAutoincrementEntity|AbstractEntity $ent public function findByPk(int $id): ?TestAutoincrementEntity { - return $this->createEntity($this->_findByPkCached($id)); + return $this->_findByPkCached($id); } public function findOneByName(string $name): ?TestAutoincrementEntity { - return $this->createEntity($this->_findOneCached(['name' => $name])); + return $this->_findOneCached(['name' => $name]); } public function delete(TestAutoincrementEntity|AbstractEntity &$entity): void @@ -61,9 +61,7 @@ public function delete(TestAutoincrementEntity|AbstractEntity &$entity): void */ public function findAllByName(string $name): array { - return $this->createEntities($this->_findAllCached( - new Where('name = :name', ['name' => $name]) - )); + return $this->_findAllCached(new Where('name = :name', ['name' => $name])); } /** @@ -71,11 +69,11 @@ public function findAllByName(string $name): array */ public function findRecent(int $limit, int $offset): array { - return $this->createEntities($this->_findAll( + return $this->_findAll( orderBy: ['id' => 'DESC'], limit: $limit, offset: $offset, - )); + ); } public function countAllByName(string $name): int @@ -86,9 +84,9 @@ public function countAllByName(string $name): int /** * @return TestAutoincrementEntity[] */ - public function findMulti(array $ids): array + public function findMulti(array $ids, ?string $keyColumnName = null): array { - return $this->createEntities($this->_findMultiCached($ids)); + return $this->_findMultiCached(ids: $ids, keyColumnName: $keyColumnName); } public function truncate(): void diff --git a/tests/TestStand/Tables/TestAutoincrementSdCachedTable.php b/tests/TestStand/Tables/TestAutoincrementSdCachedTable.php index 4f432b7..c1134f0 100644 --- a/tests/TestStand/Tables/TestAutoincrementSdCachedTable.php +++ b/tests/TestStand/Tables/TestAutoincrementSdCachedTable.php @@ -40,12 +40,12 @@ protected function getFlushCacheKeys(TestAutoincrementSdEntity|AbstractEntity $e public function findByPk(int $id): ?TestAutoincrementSdEntity { - return $this->createEntity($this->_findByPk($id)); + return $this->_findByPk($id); } public function findOneByName(string $name): ?TestAutoincrementSdEntity { - return $this->createEntity($this->_findOneCached(['name' => $name])); + return $this->_findOneCached(['name' => $name]); } public function delete(TestAutoincrementSdEntity|AbstractEntity &$entity): void @@ -61,9 +61,7 @@ public function delete(TestAutoincrementSdEntity|AbstractEntity &$entity): void */ public function findAllByName(string $name): array { - return $this->createEntities($this->_findAllCached( - new Where('name = :name', ['name' => $name, 'deleted_at' => null]), - )); + return $this->_findAllCached(new Where('name = :name', ['name' => $name, 'deleted_at' => null])); } /** @@ -71,11 +69,11 @@ public function findAllByName(string $name): array */ public function findRecent(int $limit, int $offset): array { - return $this->createEntities($this->_findAll( + return $this->_findAll( orderBy: 'id DESC', limit: $limit, offset: $offset, - )); + ); } public function countAllByName(string $name): int diff --git a/tests/TestStand/Tables/TestAutoincrementSdTable.php b/tests/TestStand/Tables/TestAutoincrementSdTable.php index 519f063..f878db9 100644 --- a/tests/TestStand/Tables/TestAutoincrementSdTable.php +++ b/tests/TestStand/Tables/TestAutoincrementSdTable.php @@ -21,12 +21,12 @@ protected function getConfig(): TableConfig public function findByPk(int $id): ?TestAutoincrementSdEntity { - return $this->createEntity($this->_findByPk($id)); + return $this->_findByPk($id); } public function findOneByName(string $name): ?TestAutoincrementSdEntity { - return $this->createEntity($this->_findOne(['name' => $name, 'deleted_at' => null])); + return $this->_findOne(['name' => $name, 'deleted_at' => null]); } /** @@ -34,9 +34,7 @@ public function findOneByName(string $name): ?TestAutoincrementSdEntity */ public function findAllByName(string $name): array { - return $this->createEntities($this->_findAll( - new Where('name = :name', ['name' => $name]) - )); + return $this->_findAll(new Where('name = :name', ['name' => $name])); } /** @@ -44,12 +42,12 @@ public function findAllByName(string $name): array */ public function findRecent(int $limit, int $offset): array { - return $this->createEntities($this->_findAll( + return $this->_findAll( where: ['deleted_at' => null], orderBy: 'id DESC', limit: $limit, offset: $offset, - )); + ); } public function init(): bool diff --git a/tests/TestStand/Tables/TestAutoincrementTable.php b/tests/TestStand/Tables/TestAutoincrementTable.php index de0e994..7b6ac40 100644 --- a/tests/TestStand/Tables/TestAutoincrementTable.php +++ b/tests/TestStand/Tables/TestAutoincrementTable.php @@ -24,12 +24,12 @@ protected function getConfig(): TableConfig public function findByPk(int $id): ?TestAutoincrementEntity { - return $this->createEntity($this->_findByPk($id)); + return $this->_findByPk($id); } public function findOneByName(string $name): ?TestAutoincrementEntity { - return $this->createEntity($this->_findOne(['name' => $name])); + return $this->_findOne(['name' => $name]); } public function delete(AbstractEntity|TestAutoincrementEntity &$entity): void @@ -45,10 +45,10 @@ public function delete(AbstractEntity|TestAutoincrementEntity &$entity): void */ public function findAllByName(string $name): array { - return $this->createEntities($this->_findAll( + return $this->_findAll( where: new Where('name = :name', ['name' => $name]), orderBy: 'id', - )); + ); } /** @@ -56,11 +56,11 @@ public function findAllByName(string $name): array */ public function findRecent(int $limit, int $offset): array { - return $this->createEntities($this->_findAll( + return $this->_findAll( orderBy: ['id' => 'DESC'], limit: $limit, offset: $offset, - )); + ); } public function countAllByName(string $name): int @@ -75,7 +75,7 @@ public function countAllByName(string $name): int */ public function findMulti(array $ids): array { - return $this->createEntities($this->_findMulti($ids), 'id'); + return $this->_findMulti($ids, 'id'); } public function init(): bool diff --git a/tests/TestStand/Tables/TestCompositeCachedTable.php b/tests/TestStand/Tables/TestCompositeCachedTable.php index 57f3203..6b5996a 100644 --- a/tests/TestStand/Tables/TestCompositeCachedTable.php +++ b/tests/TestStand/Tables/TestCompositeCachedTable.php @@ -30,10 +30,10 @@ protected function getFlushCacheKeys(TestCompositeEntity|AbstractEntity $entity) public function findOne(int $user_id, int $post_id): ?TestCompositeEntity { - return $this->createEntity($this->_findOneCached([ + return $this->_findOneCached([ 'user_id' => $user_id, 'post_id' => $post_id, - ])); + ]); } /** @@ -41,10 +41,7 @@ public function findOne(int $user_id, int $post_id): ?TestCompositeEntity */ public function findAllByUser(int $userId): array { - return array_map( - fn (array $data) => TestCompositeEntity::fromArray($data), - $this->_findAllCached(['user_id' => $userId]) - ); + return $this->_findAllCached(['user_id' => $userId]); } public function countAllByUser(int $userId): int diff --git a/tests/TestStand/Tables/TestCompositeTable.php b/tests/TestStand/Tables/TestCompositeTable.php index 2fe6910..64e90d4 100644 --- a/tests/TestStand/Tables/TestCompositeTable.php +++ b/tests/TestStand/Tables/TestCompositeTable.php @@ -33,7 +33,7 @@ public function delete(AbstractEntity|TestCompositeEntity &$entity): void public function findOne(int $user_id, int $post_id): ?TestCompositeEntity { - return $this->createEntity($this->_findOne(['user_id' => $user_id, 'post_id' => $post_id])); + return $this->_findOne(['user_id' => $user_id, 'post_id' => $post_id]); } /** @@ -41,7 +41,7 @@ public function findOne(int $user_id, int $post_id): ?TestCompositeEntity */ public function findAllByUser(int $userId): array { - return $this->createEntities($this->_findAll(['user_id' => $userId, 'status' => TestUnitEnum::ACTIVE])); + return $this->_findAll(['user_id' => $userId, 'status' => TestUnitEnum::ACTIVE]); } public function countAllByUser(int $userId): int @@ -56,7 +56,7 @@ public function countAllByUser(int $userId): int */ public function findMulti(array $ids): array { - return $this->createEntities($this->_findMulti($ids), 'post_id'); + return $this->_findMulti($ids, 'post_id'); } public function init(): bool diff --git a/tests/TestStand/Tables/TestOptimisticLockTable.php b/tests/TestStand/Tables/TestOptimisticLockTable.php index 375a61a..1ede3bc 100644 --- a/tests/TestStand/Tables/TestOptimisticLockTable.php +++ b/tests/TestStand/Tables/TestOptimisticLockTable.php @@ -21,7 +21,7 @@ protected function getConfig(): TableConfig public function findByPk(int $id): ?TestOptimisticLockEntity { - return $this->createEntity($this->_findByPk($id)); + return $this->_findByPk($id); } public function init(): bool diff --git a/tests/TestStand/Tables/TestUniqueCachedTable.php b/tests/TestStand/Tables/TestUniqueCachedTable.php index cd7c901..8fe714f 100644 --- a/tests/TestStand/Tables/TestUniqueCachedTable.php +++ b/tests/TestStand/Tables/TestUniqueCachedTable.php @@ -33,7 +33,7 @@ protected function getFlushCacheKeys(TestUniqueEntity|AbstractEntity $entity): a public function findByPk(UuidInterface $id): ?TestUniqueEntity { - return $this->createEntity($this->_findByPk($id)); + return $this->_findByPk($id); } /** @@ -41,9 +41,7 @@ public function findByPk(UuidInterface $id): ?TestUniqueEntity */ public function findAllByName(string $name): array { - return $this->createEntities($this->_findAllCached( - new Where('name = :name', ['name' => $name]) - )); + return $this->_findAllCached(new Where('name = :name', ['name' => $name])); } public function countAllByName(string $name): int diff --git a/tests/TestStand/Tables/TestUniqueTable.php b/tests/TestStand/Tables/TestUniqueTable.php index 0ad6264..8353194 100644 --- a/tests/TestStand/Tables/TestUniqueTable.php +++ b/tests/TestStand/Tables/TestUniqueTable.php @@ -34,7 +34,7 @@ protected function getConfig(): TableConfig public function findByPk(UuidInterface $id): ?TestUniqueEntity { - return $this->createEntity($this->_findByPk($id)); + return $this->_findByPk($id); } /** @@ -42,7 +42,7 @@ public function findByPk(UuidInterface $id): ?TestUniqueEntity */ public function findAllByName(string $name): array { - return $this->createEntities($this->_findAll(['name' => $name, 'status' => TestBackedEnum::ACTIVE])); + return $this->_findAll(['name' => $name, 'status' => TestBackedEnum::ACTIVE]); } public function countAllByName(string $name): int diff --git a/tests/TestStand/Tables/TestUpdateAtTable.php b/tests/TestStand/Tables/TestUpdateAtTable.php index 48c7c2a..ea114bd 100644 --- a/tests/TestStand/Tables/TestUpdateAtTable.php +++ b/tests/TestStand/Tables/TestUpdateAtTable.php @@ -21,7 +21,7 @@ protected function getConfig(): TableConfig public function findByPk(string $id): ?TestUpdatedAtEntity { - return $this->createEntity($this->_findByPk($id)); + return $this->_findByPk($id); } public function init(): bool