From 4068303385a5ba22622e432a5d9890b2519eaccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Wed, 19 Feb 2025 12:14:05 +0100 Subject: [PATCH 01/10] PHPORM-289 Support Laravel 12 --- .github/workflows/build-ci-atlas.yml | 1 + .github/workflows/build-ci.yml | 7 +++---- composer.json | 11 ++++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-ci-atlas.yml b/.github/workflows/build-ci-atlas.yml index 7a4ebd03f..30b4b06b1 100644 --- a/.github/workflows/build-ci-atlas.yml +++ b/.github/workflows/build-ci-atlas.yml @@ -20,6 +20,7 @@ jobs: - "8.4" laravel: - "11.*" + - "12.*" steps: - uses: "actions/checkout@v4" diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index d16a5885f..659c316d3 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -28,19 +28,18 @@ jobs: laravel: - "10.*" - "11.*" + - "12.*" include: - php: "8.1" laravel: "10.*" mongodb: "5.0" mode: "low-deps" os: "ubuntu-latest" - - php: "8.4" - laravel: "11.*" - mongodb: "7.0" - os: "ubuntu-latest" exclude: - php: "8.1" laravel: "11.*" + - php: "8.1" + laravel: "12.*" steps: - uses: "actions/checkout@v4" diff --git a/composer.json b/composer.json index 2855a9546..cea7fc718 100644 --- a/composer.json +++ b/composer.json @@ -25,11 +25,11 @@ "php": "^8.1", "ext-mongodb": "^1.15", "composer-runtime-api": "^2.0.0", - "illuminate/cache": "^10.36|^11", - "illuminate/container": "^10.0|^11", - "illuminate/database": "^10.30|^11", - "illuminate/events": "^10.0|^11", - "illuminate/support": "^10.0|^11", + "illuminate/cache": "^10.36|^11|^12", + "illuminate/container": "^10.0|^11|^12", + "illuminate/database": "^10.30|^11|^12", + "illuminate/events": "^10.0|^11|^12", + "illuminate/support": "^10.0|^11|^12", "mongodb/mongodb": "^1.21", "symfony/http-foundation": "^6.4|^7" }, @@ -39,6 +39,7 @@ "league/flysystem-read-only": "^3.0", "phpunit/phpunit": "^10.3|^11.5.3", "orchestra/testbench": "^8.0|^9.0", + "orchestra/testbench": "^8.0|^9.0|^10.0", "mockery/mockery": "^1.4.4@stable", "doctrine/coding-standard": "12.0.x-dev", "spatie/laravel-query-builder": "^5.6|^6", From 0cbc826615c2542b73020327312c8c553617db6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 24 Feb 2025 11:59:59 +0100 Subject: [PATCH 02/10] Enable support for laravel-query-builder v6 --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index cea7fc718..0c23cddd2 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,6 @@ "league/flysystem-gridfs": "^3.28", "league/flysystem-read-only": "^3.0", "phpunit/phpunit": "^10.3|^11.5.3", - "orchestra/testbench": "^8.0|^9.0", "orchestra/testbench": "^8.0|^9.0|^10.0", "mockery/mockery": "^1.4.4@stable", "doctrine/coding-standard": "12.0.x-dev", From 6381b8e3649833ba048bfe7a8af0518a0713863f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 24 Feb 2025 12:01:59 +0100 Subject: [PATCH 03/10] Inject the connection into Grammar classes https://github.com/laravel/framework/pull/54487 --- src/Connection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Connection.php b/src/Connection.php index 980750093..b5a71f692 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -355,13 +355,13 @@ protected function getDefaultPostProcessor() /** @inheritdoc */ protected function getDefaultQueryGrammar() { - return new Query\Grammar(); + return new Query\Grammar($this); } /** @inheritdoc */ protected function getDefaultSchemaGrammar() { - return new Schema\Grammar(); + return new Schema\Grammar($this); } /** From a8c8a548200113cdbe2b7ebb114e00e58771b818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 24 Feb 2025 18:21:25 +0100 Subject: [PATCH 04/10] Use stable releases --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0c23cddd2..64006a47b 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "league/flysystem-read-only": "^3.0", "phpunit/phpunit": "^10.3|^11.5.3", "orchestra/testbench": "^8.0|^9.0|^10.0", - "mockery/mockery": "^1.4.4@stable", + "mockery/mockery": "^1.4.4", "doctrine/coding-standard": "12.0.x-dev", "spatie/laravel-query-builder": "^5.6|^6", "phpstan/phpstan": "^1.10", From 18cae95803d4405121833c5828af6c8d1ee05926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 24 Feb 2025 19:25:43 +0100 Subject: [PATCH 05/10] Fix signatures --- src/Schema/Blueprint.php | 12 +++++------- src/Schema/Builder.php | 28 ++++++++++++++++++++++++---- tests/Query/BuilderTest.php | 2 +- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/Schema/Blueprint.php b/src/Schema/Blueprint.php index a525a9cee..333160ef2 100644 --- a/src/Schema/Blueprint.php +++ b/src/Schema/Blueprint.php @@ -19,18 +19,16 @@ class Blueprint extends SchemaBlueprint { /** - * The MongoConnection object for this blueprint. - * - * @var Connection + * The MongoDB connection object for this blueprint. */ - protected $connection; + protected Connection $connection; /** - * The Collection object for this blueprint. + * The MongoDB collection object for this blueprint. * * @var Collection */ - protected $collection; + protected Collection $collection; /** * Fluent columns. @@ -44,7 +42,7 @@ class Blueprint extends SchemaBlueprint */ public function __construct(Connection $connection, string $collection) { - parent::__construct($collection); + parent::__construct($connection, $collection); $this->connection = $connection; diff --git a/src/Schema/Builder.php b/src/Schema/Builder.php index 4af15f1f9..0f9950b72 100644 --- a/src/Schema/Builder.php +++ b/src/Schema/Builder.php @@ -7,6 +7,7 @@ use Closure; use MongoDB\Collection; use MongoDB\Driver\Exception\ServerException; +use MongoDB\Laravel\Connection; use MongoDB\Model\CollectionInfo; use MongoDB\Model\IndexInfo; @@ -16,11 +17,14 @@ use function array_keys; use function array_map; use function array_merge; +use function array_values; use function assert; use function count; use function current; use function implode; use function in_array; +use function is_array; +use function is_string; use function iterator_to_array; use function sort; use function sprintf; @@ -28,6 +32,7 @@ use function substr; use function usort; +/** @property Connection $connection */ class Builder extends \Illuminate\Database\Schema\Builder { /** @@ -137,9 +142,10 @@ public function dropAllTables() } } - public function getTables() + /** @param string|null $schema Database name */ + public function getTables($schema = null) { - $db = $this->connection->getDatabase(); + $db = $this->connection->getDatabase($schema); $collections = []; foreach ($db->listCollectionNames() as $collectionName) { @@ -165,9 +171,23 @@ public function getTables() return $collections; } - public function getTableListing() + public function getTableListing($schema = null, $schemaQualified = true) { - $collections = iterator_to_array($this->connection->getDatabase()->listCollectionNames()); + $collections = []; + + if ($schema === null || is_string($schema)) { + $collections[$schema ?? ''] = iterator_to_array($this->connection->getDatabase($schema)->listCollectionNames()); + } elseif (is_array($schema)) { + foreach ($schema as $db) { + $collections[$db] = iterator_to_array($this->connection->getDatabase($db)->listCollectionNames()); + } + } + + if ($schema && $schemaQualified) { + $collections = array_map(fn ($db, $collections) => array_map(static fn ($collection) => $db . '.' . $collection, $collections), array_keys($collections), $collections); + } + + $collections = array_merge(...array_values($collections)); sort($collections); diff --git a/tests/Query/BuilderTest.php b/tests/Query/BuilderTest.php index 2cc0c5764..20b5a12fb 100644 --- a/tests/Query/BuilderTest.php +++ b/tests/Query/BuilderTest.php @@ -1605,7 +1605,7 @@ private static function getBuilder(): Builder $connection = m::mock(Connection::class); $processor = m::mock(Processor::class); $connection->shouldReceive('getSession')->andReturn(null); - $connection->shouldReceive('getQueryGrammar')->andReturn(new Grammar()); + $connection->shouldReceive('getQueryGrammar')->andReturn(new Grammar($connection)); return new Builder($connection, null, $processor); } From d650305b03480c28d9e70032126c32b1694fa4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 24 Feb 2025 21:09:07 +0100 Subject: [PATCH 06/10] Fix Blueprint constructor for cross-compatibility --- src/Schema/Blueprint.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Schema/Blueprint.php b/src/Schema/Blueprint.php index 333160ef2..07bcd254f 100644 --- a/src/Schema/Blueprint.php +++ b/src/Schema/Blueprint.php @@ -7,6 +7,7 @@ use Illuminate\Database\Connection; use Illuminate\Database\Schema\Blueprint as SchemaBlueprint; use MongoDB\Collection; +use ReflectionMethod; use function array_flip; use function implode; @@ -18,6 +19,8 @@ class Blueprint extends SchemaBlueprint { + private static bool $hasConnectionInConstructor; + /** * The MongoDB connection object for this blueprint. */ @@ -40,9 +43,19 @@ class Blueprint extends SchemaBlueprint /** * Create a new schema blueprint. */ - public function __construct(Connection $connection, string $collection) + public function __construct(Connection $connection, string $collection, ?Closure $callback = null) { - parent::__construct($connection, $collection); + // Parent constructor signature was changed in Laravel 12 + // https://github.com/laravel/framework/commit/f29df4740d724f1c36385c9989569e3feb9422df#diff-68f714a9f1b751481b993414d3f1300ad55bcef12084ec0eb8f47f350033c24bR107 + self::$hasConnectionInConstructor ??= (new ReflectionMethod(parent::class, '__construct'))->getParameters()[0]->getName() === 'connection'; + + if (self::$hasConnectionInConstructor) { + // Laravel 12 and after + parent::__construct($connection, $collection, $callback); + } else { + // Laravel 11 and before + parent::__construct($collection, $callback); + } $this->connection = $connection; From 96b24cb5c4d1098d46fc8d4f8dd48ddf56676f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 24 Feb 2025 21:35:22 +0100 Subject: [PATCH 07/10] Fix tests --- phpstan-baseline.neon | 10 ++++++++++ src/Connection.php | 2 ++ src/Schema/Blueprint.php | 1 + tests/RelationsTest.php | 1 + 4 files changed, 14 insertions(+) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 67fdd4154..ba1f3b7aa 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,5 +1,15 @@ parameters: ignoreErrors: + - + message: "#^Class MongoDB\\\\Laravel\\\\Query\\\\Grammar does not have a constructor and must be instantiated without any parameters\\.$#" + count: 1 + path: src/Connection.php + + - + message: "#^Class MongoDB\\\\Laravel\\\\Schema\\\\Grammar does not have a constructor and must be instantiated without any parameters\\.$#" + count: 1 + path: src/Connection.php + - message: "#^Access to an undefined property Illuminate\\\\Container\\\\Container\\:\\:\\$config\\.$#" count: 3 diff --git a/src/Connection.php b/src/Connection.php index b5a71f692..4dd04120d 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -355,12 +355,14 @@ protected function getDefaultPostProcessor() /** @inheritdoc */ protected function getDefaultQueryGrammar() { + // Argument added in Laravel 12 return new Query\Grammar($this); } /** @inheritdoc */ protected function getDefaultSchemaGrammar() { + // Argument added in Laravel 12 return new Schema\Grammar($this); } diff --git a/src/Schema/Blueprint.php b/src/Schema/Blueprint.php index 07bcd254f..cfeba5c4f 100644 --- a/src/Schema/Blueprint.php +++ b/src/Schema/Blueprint.php @@ -4,6 +4,7 @@ namespace MongoDB\Laravel\Schema; +use Closure; use Illuminate\Database\Connection; use Illuminate\Database\Schema\Blueprint as SchemaBlueprint; use MongoDB\Collection; diff --git a/tests/RelationsTest.php b/tests/RelationsTest.php index a55c8c0e0..643e00e6a 100644 --- a/tests/RelationsTest.php +++ b/tests/RelationsTest.php @@ -35,6 +35,7 @@ public function tearDown(): void Photo::truncate(); Label::truncate(); Skill::truncate(); + Soft::truncate(); parent::tearDown(); } From 87d89e38d761ae5a67be472689cb52a2c3b0ce4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Wed, 26 Feb 2025 17:54:35 +0100 Subject: [PATCH 08/10] Expose schema_qualified_name in Schema::getTables() --- src/Schema/Blueprint.php | 1 + src/Schema/Builder.php | 13 ++++++++++--- tests/SchemaTest.php | 26 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/Schema/Blueprint.php b/src/Schema/Blueprint.php index cfeba5c4f..fff90b173 100644 --- a/src/Schema/Blueprint.php +++ b/src/Schema/Blueprint.php @@ -29,6 +29,7 @@ class Blueprint extends SchemaBlueprint /** * The MongoDB collection object for this blueprint. + * Type added in Laravel 12. * * @var Collection */ diff --git a/src/Schema/Builder.php b/src/Schema/Builder.php index 0f9950b72..ef450745a 100644 --- a/src/Schema/Builder.php +++ b/src/Schema/Builder.php @@ -156,7 +156,8 @@ public function getTables($schema = null) $collections[] = [ 'name' => $collectionName, - 'schema' => null, + 'schema' => $db->getDatabaseName(), + 'schema_qualified_name' => $db->getDatabaseName() . '.' . $collectionName, 'size' => $stats[0]?->storageStats?->totalSize ?? null, 'comment' => null, 'collation' => null, @@ -171,12 +172,18 @@ public function getTables($schema = null) return $collections; } - public function getTableListing($schema = null, $schemaQualified = true) + /** + * @param string|null $schema + * @param bool $schemaQualified If a schema is provided, prefix the collection names with the schema name + * + * @return array + */ + public function getTableListing($schema = null, $schemaQualified = false) { $collections = []; if ($schema === null || is_string($schema)) { - $collections[$schema ?? ''] = iterator_to_array($this->connection->getDatabase($schema)->listCollectionNames()); + $collections[$schema ?? 0] = iterator_to_array($this->connection->getDatabase($schema)->listCollectionNames()); } elseif (is_array($schema)) { foreach ($schema as $db) { $collections[$db] = iterator_to_array($this->connection->getDatabase($db)->listCollectionNames()); diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php index e2f4f7b7e..8e91a2f66 100644 --- a/tests/SchemaTest.php +++ b/tests/SchemaTest.php @@ -395,6 +395,7 @@ public function testGetTables() { DB::connection('mongodb')->table('newcollection')->insert(['test' => 'value']); DB::connection('mongodb')->table('newcollection_two')->insert(['test' => 'value']); + $dbName = DB::connection('mongodb')->getDatabaseName(); $tables = Schema::getTables(); $this->assertIsArray($tables); @@ -403,9 +404,13 @@ public function testGetTables() foreach ($tables as $table) { $this->assertArrayHasKey('name', $table); $this->assertArrayHasKey('size', $table); + $this->assertArrayHasKey('schema', $table); + $this->assertArrayHasKey('schema_qualified_name', $table); if ($table['name'] === 'newcollection') { $this->assertEquals(8192, $table['size']); + $this->assertEquals($dbName, $table['schema']); + $this->assertEquals($dbName . '.newcollection', $table['schema_qualified_name']); $found = true; } } @@ -428,6 +433,27 @@ public function testGetTableListing() $this->assertContains('newcollection_two', $tables); } + public function testGetTableListingBySchema() + { + DB::connection('mongodb')->table('newcollection')->insert(['test' => 'value']); + DB::connection('mongodb')->table('newcollection_two')->insert(['test' => 'value']); + $dbName = DB::connection('mongodb')->getDatabaseName(); + + $tables = Schema::getTableListing([$dbName, 'database__that_does_not_exists'], schemaQualified: true); + + $this->assertIsArray($tables); + $this->assertGreaterThanOrEqual(2, count($tables)); + $this->assertContains($dbName . '.newcollection', $tables); + $this->assertContains($dbName . '.newcollection_two', $tables); + + $tables = Schema::getTableListing([$dbName, 'database__that_does_not_exists'], schemaQualified: false); + + $this->assertIsArray($tables); + $this->assertGreaterThanOrEqual(2, count($tables)); + $this->assertContains('newcollection', $tables); + $this->assertContains('newcollection_two', $tables); + } + public function testGetColumns() { $collection = DB::connection('mongodb')->table('newcollection'); From 00ffc9450609e1d5c535fa08deea0b10f8f07633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Thu, 27 Feb 2025 15:43:53 +0100 Subject: [PATCH 09/10] Use 2 versions of a trait to ensure Schema::$connection property is defined ccompatible with each Laravel version --- src/Schema/Blueprint.php | 42 +++-------------- src/Schema/BlueprintLaravelCompatibility.php | 48 ++++++++++++++++++++ 2 files changed, 55 insertions(+), 35 deletions(-) create mode 100644 src/Schema/BlueprintLaravelCompatibility.php diff --git a/src/Schema/Blueprint.php b/src/Schema/Blueprint.php index fff90b173..1197bfde1 100644 --- a/src/Schema/Blueprint.php +++ b/src/Schema/Blueprint.php @@ -4,11 +4,9 @@ namespace MongoDB\Laravel\Schema; -use Closure; -use Illuminate\Database\Connection; -use Illuminate\Database\Schema\Blueprint as SchemaBlueprint; +use Illuminate\Database\Schema\Blueprint as BaseBlueprint; use MongoDB\Collection; -use ReflectionMethod; +use MongoDB\Laravel\Connection; use function array_flip; use function implode; @@ -18,22 +16,18 @@ use function is_string; use function key; -class Blueprint extends SchemaBlueprint +/** @property Connection $connection */ +class Blueprint extends BaseBlueprint { - private static bool $hasConnectionInConstructor; - - /** - * The MongoDB connection object for this blueprint. - */ - protected Connection $connection; + // Import $connection property and constructor for Laravel 12 compatibility + use BlueprintLaravelCompatibility; /** * The MongoDB collection object for this blueprint. - * Type added in Laravel 12. * * @var Collection */ - protected Collection $collection; + protected $collection; /** * Fluent columns. @@ -42,28 +36,6 @@ class Blueprint extends SchemaBlueprint */ protected $columns = []; - /** - * Create a new schema blueprint. - */ - public function __construct(Connection $connection, string $collection, ?Closure $callback = null) - { - // Parent constructor signature was changed in Laravel 12 - // https://github.com/laravel/framework/commit/f29df4740d724f1c36385c9989569e3feb9422df#diff-68f714a9f1b751481b993414d3f1300ad55bcef12084ec0eb8f47f350033c24bR107 - self::$hasConnectionInConstructor ??= (new ReflectionMethod(parent::class, '__construct'))->getParameters()[0]->getName() === 'connection'; - - if (self::$hasConnectionInConstructor) { - // Laravel 12 and after - parent::__construct($connection, $collection, $callback); - } else { - // Laravel 11 and before - parent::__construct($collection, $callback); - } - - $this->connection = $connection; - - $this->collection = $this->connection->getCollection($collection); - } - /** @inheritdoc */ public function index($columns = null, $name = null, $algorithm = null, $options = []) { diff --git a/src/Schema/BlueprintLaravelCompatibility.php b/src/Schema/BlueprintLaravelCompatibility.php new file mode 100644 index 000000000..1fa5792d4 --- /dev/null +++ b/src/Schema/BlueprintLaravelCompatibility.php @@ -0,0 +1,48 @@ +connection = $connection; + $this->collection = $connection->getCollection($collection); + } + } +} else { + /** @internal For compatibility with Laravel 12+ */ + trait BlueprintLaravelCompatibility + { + public function __construct(Connection $connection, string $collection, ?Closure $callback = null) + { + parent::__construct($connection, $collection, $callback); + + $this->collection = $connection->getCollection($collection); + } + } +} From f5a5468d3768d07184cd8a6e1338b51541b1fd50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Sat, 1 Mar 2025 23:27:45 +0100 Subject: [PATCH 10/10] Ignore CS rule --- src/Schema/BlueprintLaravelCompatibility.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Schema/BlueprintLaravelCompatibility.php b/src/Schema/BlueprintLaravelCompatibility.php index 1fa5792d4..bf288eae8 100644 --- a/src/Schema/BlueprintLaravelCompatibility.php +++ b/src/Schema/BlueprintLaravelCompatibility.php @@ -14,6 +14,8 @@ * and not break projects that would extend the MongoDB Blueprint class. * * @see https://github.com/laravel/framework/commit/f29df4740d724f1c36385c9989569e3feb9422df#diff-68f714a9f1b751481b993414d3f1300ad55bcef12084ec0eb8f47f350033c24bR107 + * + * phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses */ if (! property_exists(BaseBlueprint::class, 'connection')) { /** @internal For compatibility with Laravel 10 and 11 */