Revert MongoDB support to 1.x
Some checks are pending
CI / test (8.2) (push) Waiting to run
CI / test (8.3) (push) Waiting to run

This commit is contained in:
Funky Waddle 2026-01-07 12:48:58 -06:00
parent 6252dd6107
commit a575479206
9 changed files with 84 additions and 173 deletions

40
.gitignore vendored
View file

@ -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/
.phpunit.result.cache

View file

@ -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}}

View file

@ -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

View file

@ -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
```

View file

@ -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": {

101
composer.lock generated
View file

@ -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"

View file

@ -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<string,mixed> $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;
}

View file

@ -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])) {

View file

@ -22,6 +22,9 @@ interface MongoConnectionInterface
/** @param array<string,mixed> $filter @param array<string,mixed> $update */
public function upsertOne(string $database, string $collection, array $filter, array $update): string;
/** @param array<string,mixed> $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;