diff --git a/src/Schema/Builder.php b/src/Schema/Builder.php index ef450745a..746fda99e 100644 --- a/src/Schema/Builder.php +++ b/src/Schema/Builder.php @@ -21,6 +21,7 @@ use function assert; use function count; use function current; +use function explode; use function implode; use function in_array; use function is_array; @@ -28,10 +29,14 @@ use function iterator_to_array; use function sort; use function sprintf; +use function str_contains; use function str_ends_with; use function substr; +use function trigger_error; use function usort; +use const E_USER_DEPRECATED; + /** @property Connection $connection */ class Builder extends \Illuminate\Database\Schema\Builder { @@ -47,7 +52,7 @@ public function hasColumn($table, $column): bool } /** - * Check if columns exists in the collection schema. + * Check if columns exist in the collection schema. * * @param string $table * @param string[] $columns @@ -134,12 +139,18 @@ public function drop($table) $blueprint->drop(); } - /** @inheritdoc */ + /** + * @inheritdoc + * + * Drops the entire database instead of deleting each collection individually. + * + * In MongoDB, dropping the whole database is much faster than dropping collections + * one by one. The database will be automatically recreated when a new connection + * writes to it. + */ public function dropAllTables() { - foreach ($this->getAllCollections() as $collection) { - $this->drop($collection); - } + $this->connection->getDatabase()->drop(); } /** @param string|null $schema Database name */ @@ -148,7 +159,14 @@ public function getTables($schema = null) $db = $this->connection->getDatabase($schema); $collections = []; - foreach ($db->listCollectionNames() as $collectionName) { + foreach ($db->listCollections() as $collectionInfo) { + $collectionName = $collectionInfo->getName(); + + // Skip views, which don't support aggregate + if ($collectionInfo->getType() === 'view') { + continue; + } + $stats = $db->selectCollection($collectionName)->aggregate([ ['$collStats' => ['storageStats' => ['scale' => 1]]], ['$project' => ['storageStats.totalSize' => 1]], @@ -165,9 +183,37 @@ public function getTables($schema = null) ]; } - usort($collections, function ($a, $b) { - return $a['name'] <=> $b['name']; - }); + usort($collections, fn ($a, $b) => $a['name'] <=> $b['name']); + + return $collections; + } + + /** @param string|null $schema Database name */ + public function getViews($schema = null) + { + $db = $this->connection->getDatabase($schema); + $collections = []; + + foreach ($db->listCollections() as $collectionInfo) { + $collectionName = $collectionInfo->getName(); + + // Skip normal type collection + if ($collectionInfo->getType() !== 'view') { + continue; + } + + $collections[] = [ + 'name' => $collectionName, + 'schema' => $db->getDatabaseName(), + 'schema_qualified_name' => $db->getDatabaseName() . '.' . $collectionName, + 'size' => null, + 'comment' => null, + 'collation' => null, + 'engine' => null, + ]; + } + + usort($collections, fn ($a, $b) => $a['name'] <=> $b['name']); return $collections; } @@ -203,7 +249,12 @@ public function getTableListing($schema = null, $schemaQualified = false) public function getColumns($table) { - $stats = $this->connection->getDatabase()->selectCollection($table)->aggregate([ + $db = null; + if (str_contains($table, '.')) { + [$db, $table] = explode('.', $table, 2); + } + + $stats = $this->connection->getDatabase($db)->selectCollection($table)->aggregate([ // Sample 1,000 documents to get a representative sample of the collection ['$sample' => ['size' => 1_000]], // Convert each document to an array of fields @@ -340,10 +391,14 @@ public function getCollection($name) /** * Get all of the collections names for the database. * + * @deprecated + * * @return array */ protected function getAllCollections() { + trigger_error(sprintf('Since mongodb/laravel-mongodb:5.4, Method "%s()" is deprecated without replacement.', __METHOD__), E_USER_DEPRECATED); + $collections = []; foreach ($this->connection->getDatabase()->listCollections() as $collection) { $collections[] = $collection->getName(); diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php index 8e91a2f66..3257a671e 100644 --- a/tests/SchemaTest.php +++ b/tests/SchemaTest.php @@ -22,10 +22,11 @@ class SchemaTest extends TestCase { public function tearDown(): void { - $database = $this->getConnection('mongodb')->getMongoDB(); + $database = $this->getConnection('mongodb')->getDatabase(); assert($database instanceof Database); $database->dropCollection('newcollection'); $database->dropCollection('newcollection_two'); + $database->dropCollection('test_view'); parent::tearDown(); } @@ -395,6 +396,7 @@ public function testGetTables() { DB::connection('mongodb')->table('newcollection')->insert(['test' => 'value']); DB::connection('mongodb')->table('newcollection_two')->insert(['test' => 'value']); + DB::connection('mongodb')->getDatabase()->createCollection('test_view', ['viewOn' => 'newcollection']); $dbName = DB::connection('mongodb')->getDatabaseName(); $tables = Schema::getTables(); @@ -406,6 +408,7 @@ public function testGetTables() $this->assertArrayHasKey('size', $table); $this->assertArrayHasKey('schema', $table); $this->assertArrayHasKey('schema_qualified_name', $table); + $this->assertNotEquals('test_view', $table['name'], 'Standard views should not be included in the result of getTables.'); if ($table['name'] === 'newcollection') { $this->assertEquals(8192, $table['size']); @@ -420,6 +423,40 @@ public function testGetTables() } } + public function testGetViews() + { + DB::connection('mongodb')->table('newcollection')->insert(['test' => 'value']); + DB::connection('mongodb')->table('newcollection_two')->insert(['test' => 'value']); + $dbName = DB::connection('mongodb')->getDatabaseName(); + + DB::connection('mongodb')->getDatabase()->createCollection('test_view', ['viewOn' => 'newcollection']); + + $tables = Schema::getViews(); + + $this->assertIsArray($tables); + $this->assertGreaterThanOrEqual(1, count($tables)); + $found = false; + foreach ($tables as $table) { + $this->assertArrayHasKey('name', $table); + $this->assertArrayHasKey('size', $table); + $this->assertArrayHasKey('schema', $table); + $this->assertArrayHasKey('schema_qualified_name', $table); + + // Ensure "normal collections" are not in the views list + $this->assertNotEquals('newcollection', $table['name'], 'Normal collections should not be included in the result of getViews.'); + + if ($table['name'] === 'test_view') { + $this->assertEquals($dbName, $table['schema']); + $this->assertEquals($dbName . '.test_view', $table['schema_qualified_name']); + $found = true; + } + } + + if (! $found) { + $this->fail('Collection "test_view" not found'); + } + } + public function testGetTableListing() { DB::connection('mongodb')->table('newcollection')->insert(['test' => 'value']); @@ -489,6 +526,11 @@ public function testGetColumns() // Non-existent collection $columns = Schema::getColumns('missing'); $this->assertSame([], $columns); + + // Qualified table name + $columns = Schema::getColumns(DB::getDatabaseName() . '.newcollection'); + $this->assertIsArray($columns); + $this->assertCount(5, $columns); } /** @see AtlasSearchTest::testGetIndexes() */