From a575479206b28a268d3e979aee39a4ba4f645201 Mon Sep 17 00:00:00 2001 From: Funky Waddle Date: Wed, 7 Jan 2026 12:48:58 -0600 Subject: [PATCH] Revert MongoDB support to 1.x --- .gitignore | 42 +------- .phpunit.result.cache | 1 - CHANGELOG.md | 7 ++ README.md | 2 +- composer.json | 4 +- composer.lock | 101 ++----------------- src/NoSql/Mongo/MongoClientConnection.php | 56 ++++------ src/NoSql/Mongo/MongoConnection.php | 41 ++++++++ src/NoSql/Mongo/MongoConnectionInterface.php | 3 + 9 files changed, 84 insertions(+), 173 deletions(-) delete mode 100644 .phpunit.result.cache diff --git a/.gitignore b/.gitignore index 9f1aa78..afafe53 100644 --- a/.gitignore +++ b/.gitignore @@ -1,52 +1,14 @@ -# ---> Composer composer.phar /vendor/ - -# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control -# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file -# composer.lock - -# ---> JetBrains -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - .idea/ - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format *.iws - -# IntelliJ out/ - -# mpeltonen/sbt-idea plugin .idea_modules/ - -# JIRA plugin atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties - -.junie/ \ No newline at end of file +.junie/ +.phpunit.result.cache \ No newline at end of file diff --git a/.phpunit.result.cache b/.phpunit.result.cache deleted file mode 100644 index a9597ff..0000000 --- a/.phpunit.result.cache +++ /dev/null @@ -1 +0,0 @@ -{"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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5df1905..bdaf10b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. +#### [0.1.1] - 2026-01-07 + +##### Changed +- **MongoDB Downgrade**: Stepped back MongoDB support from `mongodb/mongodb` ^2.0 to ^1.20 for broader compatibility with older `ext-mongodb` environments. +- **Refactoring**: Simplified `normalizeFilter` in `MongoClientConnection` for better maintainability. +- **Features**: Added `count()` method to `MongoConnectionInterface` and implementations. + #### [0.1.0] - 2026-01-06 ##### Added diff --git a/README.md b/README.md index 9b5b674..11bf5a9 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ composer install vendor/bin/phpunit ``` -- **Run MongoDB integration tests** (requires `ext-mongodb >= 2.1` and a reachable server): +- **Run MongoDB integration tests** (requires `ext-mongodb >= 1.20` and a reachable server): ```bash MONGO_HOST=127.0.0.1 MONGO_PORT=27017 vendor/bin/phpunit --group mongo-integration ``` diff --git a/composer.json b/composer.json index 80503f4..1d1aa59 100644 --- a/composer.json +++ b/composer.json @@ -39,8 +39,8 @@ }, "require": { "php": "^8.2", - "ext-mongodb": "*", - "mongodb/mongodb": "^2.0", + "ext-mongodb": "^1.20", + "mongodb/mongodb": "^1.20", "psr/simple-cache": "^3.0" }, "autoload": { diff --git a/composer.lock b/composer.lock index c6a6f3a..a5d4692 100644 --- a/composer.lock +++ b/composer.lock @@ -4,28 +4,27 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "64942e8c928a3d237f245d668b7c255b", + "content-hash": "11f93ad2569b5071bfe47d842a4fa8da", "packages": [ { "name": "mongodb/mongodb", - "version": "2.1.2", + "version": "1.21.3", "source": { "type": "git", "url": "https://github.com/mongodb/mongo-php-library.git", - "reference": "0a2472ba9cbb932f7e43a8770aedb2fc30612a67" + "reference": "b8f569ec52542d2f1bfca88286f20d14a7f72536" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/0a2472ba9cbb932f7e43a8770aedb2fc30612a67", - "reference": "0a2472ba9cbb932f7e43a8770aedb2fc30612a67", + "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/b8f569ec52542d2f1bfca88286f20d14a7f72536", + "reference": "b8f569ec52542d2f1bfca88286f20d14a7f72536", "shasum": "" }, "require": { "composer-runtime-api": "^2.0", - "ext-mongodb": "^2.1", + "ext-mongodb": "^1.21.0", "php": "^8.1", - "psr/log": "^1.1.4|^2|^3", - "symfony/polyfill-php85": "^1.32" + "psr/log": "^1.1.4|^2|^3" }, "replace": { "mongodb/builder": "*" @@ -79,9 +78,9 @@ ], "support": { "issues": "https://github.com/mongodb/mongo-php-library/issues", - "source": "https://github.com/mongodb/mongo-php-library/tree/2.1.2" + "source": "https://github.com/mongodb/mongo-php-library/tree/1.21.3" }, - "time": "2025-10-06T12:12:40+00:00" + "time": "2025-09-22T12:34:29+00:00" }, { "name": "psr/log", @@ -183,86 +182,6 @@ "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" }, "time": "2021-10-29T13:26:27+00:00" - }, - { - "name": "symfony/polyfill-php85", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php85\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-06-23T16:12:55+00:00" } ], "packages-dev": [ @@ -1943,7 +1862,7 @@ "prefer-lowest": false, "platform": { "php": "^8.2", - "ext-mongodb": "*" + "ext-mongodb": "^1.20" }, "platform-dev": {}, "plugin-api-version": "2.6.0" diff --git a/src/NoSql/Mongo/MongoClientConnection.php b/src/NoSql/Mongo/MongoClientConnection.php index 044522e..ead4f69 100644 --- a/src/NoSql/Mongo/MongoClientConnection.php +++ b/src/NoSql/Mongo/MongoClientConnection.php @@ -87,6 +87,12 @@ class MongoClientConnection implements MongoConnectionInterface return ''; } + public function count(string $database, string $collection, array $filter = []): int + { + $coll = $this->client->selectCollection($database, $collection); + return $coll->countDocuments($this->normalizeFilter($filter)); + } + public function withSession(callable $callback): mixed { /** @var Session $session */ @@ -118,52 +124,26 @@ class MongoClientConnection implements MongoConnectionInterface /** @param array $filter */ private function normalizeFilter(array $filter): array { - // Recursively walk the filter and convert any _id string(s) that look like 24-hex to ObjectId - $walker = function (&$node, $key = null) use (&$walker) { - if (is_array($node)) { - foreach ($node as $k => &$v) { - $walker($v, $k); - } - return; - } - if ($key === '_id' && is_string($node) && preg_match('/^[a-f\d]{24}$/i', $node)) { - try { $node = new ObjectId($node); } catch (\Throwable) {} - } - }; - - $convertIdContainer = function (&$value) use (&$convertIdContainer) { - // Handle structures like ['_id' => ['$in' => ['...','...']]] - if (is_string($value) && preg_match('/^[a-f\d]{24}$/i', $value)) { - try { $value = new ObjectId($value); } catch (\Throwable) {} - return; - } + $normalize = function (&$value, $key) use (&$normalize) { if (is_array($value)) { foreach ($value as $k => &$v) { - $convertIdContainer($v); + $normalize($v, $k); + } + return; + } + + if ($key === '_id' && is_string($value) && preg_match('/^[a-f\d]{24}$/i', $value)) { + try { + $value = new ObjectId($value); + } catch (\Throwable) { + // Ignore invalid ObjectIds } } }; - // Top-level traversal foreach ($filter as $k => &$v) { - if ($k === '_id') { - $convertIdContainer($v); - } elseif (is_array($v)) { - // Recurse into nested boolean operators ($and/$or) etc. - foreach ($v as $kk => &$vv) { - if ($kk === '_id') { - $convertIdContainer($vv); - } elseif (is_array($vv)) { - foreach ($vv as $kkk => &$vvv) { - if ($kkk === '_id') { - $convertIdContainer($vvv); - } - } - } - } - } + $normalize($v, $k); } - unset($v); return $filter; } diff --git a/src/NoSql/Mongo/MongoConnection.php b/src/NoSql/Mongo/MongoConnection.php index ca79000..053ead8 100644 --- a/src/NoSql/Mongo/MongoConnection.php +++ b/src/NoSql/Mongo/MongoConnection.php @@ -79,6 +79,47 @@ class MongoConnection implements MongoConnectionInterface return $this->getCollection($database, $collection); } + public function upsertOne(string $database, string $collection, array $filter, array $update): string + { + $id = $this->generateId(); + if ($this->updateOne($database, $collection, $filter, $update) === 0) { + $doc = $filter; + if (isset($update['$set'])) { + foreach ($update['$set'] as $k => $v) { $doc[$k] = $v; } + } else { + foreach ($update as $k => $v) { $doc[$k] = $v; } + } + $doc['_id'] = $doc['_id'] ?? $id; + $this->store[$database][$collection][] = $doc; + return (string)$doc['_id']; + } + $found = $this->find($database, $collection, $filter); + $first = reset($found); + return (string)($first['_id'] ?? ''); + } + + public function count(string $database, string $collection, array $filter = []): int + { + $docs = $this->getCollection($database, $collection); + $count = 0; + foreach ($docs as $doc) { + if ($this->matches($doc, $filter)) { + $count++; + } + } + return $count; + } + + public function withSession(callable $callback): mixed + { + return $callback($this, null); + } + + public function withTransaction(callable $callback): mixed + { + return $callback($this, null); + } + private function &getCollection(string $database, string $collection): array { if (!isset($this->store[$database][$collection])) { diff --git a/src/NoSql/Mongo/MongoConnectionInterface.php b/src/NoSql/Mongo/MongoConnectionInterface.php index 13b11cc..15d5f88 100644 --- a/src/NoSql/Mongo/MongoConnectionInterface.php +++ b/src/NoSql/Mongo/MongoConnectionInterface.php @@ -22,6 +22,9 @@ interface MongoConnectionInterface /** @param array $filter @param array $update */ public function upsertOne(string $database, string $collection, array $filter, array $update): string; + /** @param array $filter */ + public function count(string $database, string $collection, array $filter = []): int; + /** Execute a callback with a client session; callback receives the connection instance and session as args. */ public function withSession(callable $callback): mixed;