From 45b9987764e704483903a9668447829b7fe9079d Mon Sep 17 00:00:00 2001 From: Funky Waddle Date: Mon, 15 Dec 2025 13:29:43 -0600 Subject: [PATCH] code standards tightening --- .github/workflows/ci.yml | 2 +- .gitignore | 2 + .phpunit.result.cache | 2 +- README.md | 2 +- composer.json | 2 +- src/Model/AbstractDao.php | 153 ++++++++++++++++++++++++++++++++++---- 6 files changed, 144 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 333ccdd..84ef491 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: [ '8.1', '8.2', '8.3' ] + php: [ '8.2', '8.3' ] services: mysql: image: mysql:8 diff --git a/.gitignore b/.gitignore index 1a3ca50..2d7a642 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties + +.junie.json \ No newline at end of file diff --git a/.phpunit.result.cache b/.phpunit.result.cache index a9597ff..f345938 100644 --- a/.phpunit.result.cache +++ b/.phpunit.result.cache @@ -1 +1 @@ -{"version":2,"defects":{"Pairity\\Tests\\BelongsToManyMysqlTest::testBelongsToManyEagerAndHelpers":1,"Pairity\\Tests\\BelongsToManySqliteTest::testBelongsToManyEagerAndPivotHelpers":7,"Pairity\\Tests\\CastersAndAccessorsSqliteTest::testCustomCasterAndDtoAccessorsMutators":7,"Pairity\\Tests\\JoinEagerMysqlTest::testJoinEagerHasManyAndBelongsTo":1,"Pairity\\Tests\\JoinEagerSqliteTest::testHasManyJoinEagerWithProjectionAndSoftDeleteScope":7,"Pairity\\Tests\\JoinEagerSqliteTest::testBelongsToJoinEagerSingleLevel":8,"Pairity\\Tests\\MongoAdapterTest::testCrudCycle":1,"Pairity\\Tests\\MongoDaoTest::testCrudViaDao":8,"Pairity\\Tests\\MongoOptimisticLockTest::testVersionIncrementOnUpdate":8,"Pairity\\Tests\\MongoPaginationTest::testPaginateAndSimplePaginateWithScopes":8,"Pairity\\Tests\\MongoRelationsTest::testEagerAndLazyRelations":8,"Pairity\\Tests\\MysqlSmokeTest::testCreateAndDropTable":1,"Pairity\\Tests\\PaginationSqliteTest::testPaginateAndSimplePaginateWithScopesAndRelations":7,"Pairity\\Tests\\RelationsNestedConstraintsSqliteTest::testNestedEagerAndPerRelationFieldsConstraint":7,"Pairity\\Tests\\SchemaBuilderSqliteTest::testCreateAlterDropCycle":8,"Pairity\\Tests\\UnitOfWorkCascadeMongoTest::testDeleteByIdCascadesToChildren":8,"Pairity\\Tests\\UnitOfWorkCascadeSqliteTest::testDeleteByIdCascadesToChildren":7,"Pairity\\Tests\\UnitOfWorkMongoTest::testDeferredUpdateAndDeleteCommit":8,"Pairity\\Tests\\UnitOfWorkMongoTest::testRollbackOnExceptionClearsOps":8,"Pairity\\Tests\\MongoEventSystemTest::testDaoEventsFireOnCrud":8,"Pairity\\Tests\\PostgresSmokeTest::testCreateAlterDropCycle":1},"times":{"Pairity\\Tests\\BelongsToManyMysqlTest::testBelongsToManyEagerAndHelpers":0.001,"Pairity\\Tests\\BelongsToManySqliteTest::testBelongsToManyEagerAndPivotHelpers":0.004,"Pairity\\Tests\\CastersAndAccessorsSqliteTest::testCustomCasterAndDtoAccessorsMutators":0,"Pairity\\Tests\\JoinEagerMysqlTest::testJoinEagerHasManyAndBelongsTo":0,"Pairity\\Tests\\JoinEagerSqliteTest::testHasManyJoinEagerWithProjectionAndSoftDeleteScope":0,"Pairity\\Tests\\JoinEagerSqliteTest::testBelongsToJoinEagerSingleLevel":0,"Pairity\\Tests\\MongoAdapterTest::testCrudCycle":0.002,"Pairity\\Tests\\MongoDaoTest::testCrudViaDao":0.001,"Pairity\\Tests\\MongoOptimisticLockTest::testVersionIncrementOnUpdate":0,"Pairity\\Tests\\MongoPaginationTest::testPaginateAndSimplePaginateWithScopes":0,"Pairity\\Tests\\MongoRelationsTest::testEagerAndLazyRelations":0,"Pairity\\Tests\\MysqlSmokeTest::testCreateAndDropTable":0,"Pairity\\Tests\\OptimisticLockSqliteTest::testVersionLockingIncrementsAndBlocksBulkUpdate":0,"Pairity\\Tests\\PaginationSqliteTest::testPaginateAndSimplePaginateWithScopesAndRelations":0.001,"Pairity\\Tests\\RelationsNestedConstraintsSqliteTest::testNestedEagerAndPerRelationFieldsConstraint":0,"Pairity\\Tests\\SchemaBuilderSqliteTest::testCreateAlterDropCycle":0.001,"Pairity\\Tests\\SoftDeletesTimestampsSqliteTest::testTimestampsAndSoftDeletesFlow":0,"Pairity\\Tests\\UnitOfWorkCascadeMongoTest::testDeleteByIdCascadesToChildren":0,"Pairity\\Tests\\UnitOfWorkCascadeSqliteTest::testDeleteByIdCascadesToChildren":0,"Pairity\\Tests\\UnitOfWorkMongoTest::testDeferredUpdateAndDeleteCommit":0,"Pairity\\Tests\\UnitOfWorkMongoTest::testRollbackOnExceptionClearsOps":0,"Pairity\\Tests\\UnitOfWorkSqliteTest::testDeferredUpdateAndDeleteCommit":0,"Pairity\\Tests\\UnitOfWorkSqliteTest::testRollbackOnExceptionClearsOps":0,"Pairity\\Tests\\EventSystemSqliteTest::testDaoEventsForInsertUpdateDeleteAndFind":0,"Pairity\\Tests\\EventSystemSqliteTest::testUowBeforeAfterCommitEvents":0,"Pairity\\Tests\\MongoEventSystemTest::testDaoEventsFireOnCrud":0,"Pairity\\Tests\\PostgresSmokeTest::testCreateAlterDropCycle":0}} \ No newline at end of file +{"version":2,"defects":{"Pairity\\Tests\\BelongsToManyMysqlTest::testBelongsToManyEagerAndHelpers":1,"Pairity\\Tests\\BelongsToManySqliteTest::testBelongsToManyEagerAndPivotHelpers":7,"Pairity\\Tests\\CastersAndAccessorsSqliteTest::testCustomCasterAndDtoAccessorsMutators":7,"Pairity\\Tests\\JoinEagerMysqlTest::testJoinEagerHasManyAndBelongsTo":1,"Pairity\\Tests\\JoinEagerSqliteTest::testHasManyJoinEagerWithProjectionAndSoftDeleteScope":7,"Pairity\\Tests\\JoinEagerSqliteTest::testBelongsToJoinEagerSingleLevel":8,"Pairity\\Tests\\MongoAdapterTest::testCrudCycle":1,"Pairity\\Tests\\MongoDaoTest::testCrudViaDao":8,"Pairity\\Tests\\MongoOptimisticLockTest::testVersionIncrementOnUpdate":8,"Pairity\\Tests\\MongoPaginationTest::testPaginateAndSimplePaginateWithScopes":8,"Pairity\\Tests\\MongoRelationsTest::testEagerAndLazyRelations":8,"Pairity\\Tests\\MysqlSmokeTest::testCreateAndDropTable":1,"Pairity\\Tests\\PaginationSqliteTest::testPaginateAndSimplePaginateWithScopesAndRelations":7,"Pairity\\Tests\\RelationsNestedConstraintsSqliteTest::testNestedEagerAndPerRelationFieldsConstraint":7,"Pairity\\Tests\\SchemaBuilderSqliteTest::testCreateAlterDropCycle":8,"Pairity\\Tests\\UnitOfWorkCascadeMongoTest::testDeleteByIdCascadesToChildren":8,"Pairity\\Tests\\UnitOfWorkCascadeSqliteTest::testDeleteByIdCascadesToChildren":7,"Pairity\\Tests\\UnitOfWorkMongoTest::testDeferredUpdateAndDeleteCommit":8,"Pairity\\Tests\\UnitOfWorkMongoTest::testRollbackOnExceptionClearsOps":8,"Pairity\\Tests\\MongoEventSystemTest::testDaoEventsFireOnCrud":8,"Pairity\\Tests\\PostgresSmokeTest::testCreateAlterDropCycle":1},"times":{"Pairity\\Tests\\BelongsToManyMysqlTest::testBelongsToManyEagerAndHelpers":0.001,"Pairity\\Tests\\BelongsToManySqliteTest::testBelongsToManyEagerAndPivotHelpers":0.008,"Pairity\\Tests\\CastersAndAccessorsSqliteTest::testCustomCasterAndDtoAccessorsMutators":0.003,"Pairity\\Tests\\JoinEagerMysqlTest::testJoinEagerHasManyAndBelongsTo":0,"Pairity\\Tests\\JoinEagerSqliteTest::testHasManyJoinEagerWithProjectionAndSoftDeleteScope":0,"Pairity\\Tests\\JoinEagerSqliteTest::testBelongsToJoinEagerSingleLevel":0,"Pairity\\Tests\\MongoAdapterTest::testCrudCycle":0.002,"Pairity\\Tests\\MongoDaoTest::testCrudViaDao":0.001,"Pairity\\Tests\\MongoOptimisticLockTest::testVersionIncrementOnUpdate":0,"Pairity\\Tests\\MongoPaginationTest::testPaginateAndSimplePaginateWithScopes":0,"Pairity\\Tests\\MongoRelationsTest::testEagerAndLazyRelations":0,"Pairity\\Tests\\MysqlSmokeTest::testCreateAndDropTable":0,"Pairity\\Tests\\OptimisticLockSqliteTest::testVersionLockingIncrementsAndBlocksBulkUpdate":0.001,"Pairity\\Tests\\PaginationSqliteTest::testPaginateAndSimplePaginateWithScopesAndRelations":0.001,"Pairity\\Tests\\RelationsNestedConstraintsSqliteTest::testNestedEagerAndPerRelationFieldsConstraint":0,"Pairity\\Tests\\SchemaBuilderSqliteTest::testCreateAlterDropCycle":0.004,"Pairity\\Tests\\SoftDeletesTimestampsSqliteTest::testTimestampsAndSoftDeletesFlow":0,"Pairity\\Tests\\UnitOfWorkCascadeMongoTest::testDeleteByIdCascadesToChildren":0,"Pairity\\Tests\\UnitOfWorkCascadeSqliteTest::testDeleteByIdCascadesToChildren":0.001,"Pairity\\Tests\\UnitOfWorkMongoTest::testDeferredUpdateAndDeleteCommit":0,"Pairity\\Tests\\UnitOfWorkMongoTest::testRollbackOnExceptionClearsOps":0,"Pairity\\Tests\\UnitOfWorkSqliteTest::testDeferredUpdateAndDeleteCommit":0,"Pairity\\Tests\\UnitOfWorkSqliteTest::testRollbackOnExceptionClearsOps":0,"Pairity\\Tests\\EventSystemSqliteTest::testDaoEventsForInsertUpdateDeleteAndFind":0,"Pairity\\Tests\\EventSystemSqliteTest::testUowBeforeAfterCommitEvents":0,"Pairity\\Tests\\MongoEventSystemTest::testDaoEventsFireOnCrud":0,"Pairity\\Tests\\PostgresSmokeTest::testCreateAlterDropCycle":0}} \ No newline at end of file diff --git a/README.md b/README.md index 919825a..7436ea4 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ MIT ## Installation -- Requirements: PHP >= 8.1, PDO extension for your database(s) +- Requirements: PHP >= 8.2, PDO extension for your database(s) - Install via Composer: ``` diff --git a/composer.json b/composer.json index b005ceb..116b88b 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "source": "https://github.com/getphred/pairity" }, "require": { - "php": ">=8.1", + "php": "^8.2", "ext-mongodb": "*", "mongodb/mongodb": "^2.0" }, diff --git a/src/Model/AbstractDao.php b/src/Model/AbstractDao.php index 0368a54..b594618 100644 --- a/src/Model/AbstractDao.php +++ b/src/Model/AbstractDao.php @@ -143,7 +143,15 @@ abstract class AbstractDao implements DaoInterface public function findOneBy(array $criteria): ?AbstractDto { // Events: dao.beforeFind (criteria may be mutated) - try { $ev = ['dao' => $this, 'table' => $this->getTable(), 'criteria' => &$criteria]; Events::dispatcher()->dispatch('dao.beforeFind', $ev); } catch (\Throwable) {} + try { + $ev = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'criteria' => &$criteria, + ]; + Events::dispatcher()->dispatch('dao.beforeFind', $ev); + } catch (\Throwable) { + } $criteria = $this->applyDefaultScopes($criteria); $this->applyRuntimeScopesToCriteria($criteria); [$where, $bindings] = $this->buildWhere($criteria); @@ -167,7 +175,15 @@ abstract class AbstractDao implements DaoInterface $this->eagerStrategy = null; // reset $this->withStrategies = []; // Events: dao.afterFind - try { $payload = ['dao' => $this, 'table' => $this->getTable(), 'dto' => $dto]; Events::dispatcher()->dispatch('dao.afterFind', $payload); } catch (\Throwable) {} + try { + $payload = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'dto' => $dto, + ]; + Events::dispatcher()->dispatch('dao.afterFind', $payload); + } catch (\Throwable) { + } return $dto; } @@ -190,7 +206,15 @@ abstract class AbstractDao implements DaoInterface public function findAllBy(array $criteria = []): array { // Events: dao.beforeFind (criteria may be mutated) - try { $ev = ['dao' => $this, 'table' => $this->getTable(), 'criteria' => &$criteria]; Events::dispatcher()->dispatch('dao.beforeFind', $ev); } catch (\Throwable) {} + try { + $ev = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'criteria' => &$criteria, + ]; + Events::dispatcher()->dispatch('dao.beforeFind', $ev); + } catch (\Throwable) { + } $criteria = $this->applyDefaultScopes($criteria); $this->applyRuntimeScopesToCriteria($criteria); [$where, $bindings] = $this->buildWhere($criteria); @@ -212,7 +236,15 @@ abstract class AbstractDao implements DaoInterface $this->eagerStrategy = null; // reset $this->withStrategies = []; // Events: dao.afterFind (list) - try { $payload = ['dao' => $this, 'table' => $this->getTable(), 'dtos' => $dtos]; Events::dispatcher()->dispatch('dao.afterFind', $payload); } catch (\Throwable) {} + try { + $payload = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'dtos' => $dtos, + ]; + Events::dispatcher()->dispatch('dao.afterFind', $payload); + } catch (\Throwable) { + } return $dtos; } @@ -300,7 +332,15 @@ abstract class AbstractDao implements DaoInterface throw new \InvalidArgumentException('insert() requires non-empty data'); } // Events: dao.beforeInsert (allow mutation of $data) - try { $ev = ['dao' => $this, 'table' => $this->getTable(), 'data' => &$data]; Events::dispatcher()->dispatch('dao.beforeInsert', $ev); } catch (\Throwable) {} + try { + $ev = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'data' => &$data, + ]; + Events::dispatcher()->dispatch('dao.beforeInsert', $ev); + } catch (\Throwable) { + } $data = $this->prepareForInsert($data); $cols = array_keys($data); $placeholders = array_map(fn($c) => ':' . $c, $cols); @@ -310,12 +350,28 @@ abstract class AbstractDao implements DaoInterface $pk = $this->getPrimaryKey(); if ($id !== null) { $dto = $this->findById($id) ?? $this->hydrate(array_merge($data, [$pk => $id])); - try { $payload = ['dao' => $this, 'table' => $this->getTable(), 'dto' => $dto]; Events::dispatcher()->dispatch('dao.afterInsert', $payload); } catch (\Throwable) {} + try { + $payload = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'dto' => $dto, + ]; + Events::dispatcher()->dispatch('dao.afterInsert', $payload); + } catch (\Throwable) { + } return $dto; } // Fallback when lastInsertId is unavailable: return hydrated DTO from provided data $dto = $this->hydrate($this->castRowFromStorage($data)); - try { $payload = ['dao' => $this, 'table' => $this->getTable(), 'dto' => $dto]; Events::dispatcher()->dispatch('dao.afterInsert', $payload); } catch (\Throwable) {} + try { + $payload = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'dto' => $dto, + ]; + Events::dispatcher()->dispatch('dao.afterInsert', $payload); + } catch (\Throwable) { + } return $dto; } @@ -330,7 +386,16 @@ abstract class AbstractDao implements DaoInterface throw new \InvalidArgumentException('No data provided to update and record not found'); } // Events: dao.beforeUpdate (mutate $data) - try { $ev = ['dao' => $this, 'table' => $this->getTable(), 'id' => $id, 'data' => &$data]; Events::dispatcher()->dispatch('dao.beforeUpdate', $ev); } catch (\Throwable) {} + try { + $ev = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'id' => $id, + 'data' => &$data, + ]; + Events::dispatcher()->dispatch('dao.beforeUpdate', $ev); + } catch (\Throwable) { + } $toStore = $this->prepareForUpdate($data); $self = $this; $conn = $this->connection; @@ -349,7 +414,15 @@ abstract class AbstractDao implements DaoInterface $pk = $this->getPrimaryKey(); $result = array_merge($base, $data, [$pk => $id]); $dto = $this->hydrate($this->castRowFromStorage($result)); - try { $payload = ['dao' => $this, 'table' => $this->getTable(), 'dto' => $dto]; Events::dispatcher()->dispatch('dao.afterUpdate', $payload); } catch (\Throwable) {} + try { + $payload = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'dto' => $dto, + ]; + Events::dispatcher()->dispatch('dao.afterUpdate', $payload); + } catch (\Throwable) { + } return $dto; } @@ -359,17 +432,42 @@ abstract class AbstractDao implements DaoInterface throw new \InvalidArgumentException('No data provided to update and record not found'); } // Events: dao.beforeUpdate - try { $ev = ['dao' => $this, 'table' => $this->getTable(), 'id' => $id, 'data' => &$data]; Events::dispatcher()->dispatch('dao.beforeUpdate', $ev); } catch (\Throwable) {} + try { + $ev = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'id' => $id, + 'data' => &$data, + ]; + Events::dispatcher()->dispatch('dao.beforeUpdate', $ev); + } catch (\Throwable) { + } $data = $this->prepareForUpdate($data); $this->doImmediateUpdateWithLock($id, $data); $updated = $this->findById($id); if ($updated === null) { $pk = $this->getPrimaryKey(); $dto = $this->hydrate($this->castRowFromStorage(array_merge($data, [$pk => $id]))); - try { $payload = ['dao' => $this, 'table' => $this->getTable(), 'dto' => $dto]; Events::dispatcher()->dispatch('dao.afterUpdate', $payload); } catch (\Throwable) {} + try { + $payload = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'dto' => $dto, + ]; + Events::dispatcher()->dispatch('dao.afterUpdate', $payload); + } catch (\Throwable) { + } return $dto; } - try { $payload = ['dao' => $this, 'table' => $this->getTable(), 'dto' => $updated]; Events::dispatcher()->dispatch('dao.afterUpdate', $payload); } catch (\Throwable) {} + try { + $payload = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'dto' => $updated, + ]; + Events::dispatcher()->dispatch('dao.afterUpdate', $payload); + } catch (\Throwable) { + } return $updated; } @@ -378,7 +476,15 @@ abstract class AbstractDao implements DaoInterface $uow = UnitOfWork::current(); if ($uow && !UnitOfWork::isSuspended()) { $self = $this; $conn = $this->connection; $theId = $id; - try { $ev = ['dao' => $this, 'table' => $this->getTable(), 'id' => $id]; Events::dispatcher()->dispatch('dao.beforeDelete', $ev); } catch (\Throwable) {} + try { + $ev = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'id' => $id, + ]; + Events::dispatcher()->dispatch('dao.beforeDelete', $ev); + } catch (\Throwable) { + } $uow->enqueueWithMeta($conn, [ 'type' => 'delete', 'mode' => 'byId', @@ -390,7 +496,15 @@ abstract class AbstractDao implements DaoInterface // deferred; immediate affected count unknown return 0; } - try { $ev = ['dao' => $this, 'table' => $this->getTable(), 'id' => $id]; Events::dispatcher()->dispatch('dao.beforeDelete', $ev); } catch (\Throwable) {} + try { + $ev = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'id' => $id, + ]; + Events::dispatcher()->dispatch('dao.beforeDelete', $ev); + } catch (\Throwable) { + } if ($this->hasSoftDeletes()) { $columns = $this->softDeleteConfig(); $deletedAt = $columns['deletedAt'] ?? 'deleted_at'; @@ -400,7 +514,16 @@ abstract class AbstractDao implements DaoInterface } $sql = 'DELETE FROM ' . $this->getTable() . ' WHERE ' . $this->getPrimaryKey() . ' = :pk'; $affected = $this->connection->execute($sql, ['pk' => $id]); - try { $payload = ['dao' => $this, 'table' => $this->getTable(), 'id' => $id, 'affected' => $affected]; Events::dispatcher()->dispatch('dao.afterDelete', $payload); } catch (\Throwable) {} + try { + $payload = [ + 'dao' => $this, + 'table' => $this->getTable(), + 'id' => $id, + 'affected' => $affected, + ]; + Events::dispatcher()->dispatch('dao.afterDelete', $payload); + } catch (\Throwable) { + } return $affected; }