From 16146556aad2b18feca18aa0182a525067937dce Mon Sep 17 00:00:00 2001 From: Simon Benjamin <163124+Benjaminhu@users.noreply.github.com> Date: Thu, 22 May 2025 17:35:29 +0200 Subject: [PATCH 1/4] Fix: `connections` name, PostgreSQL schemas --- src/Coders/Model/Factory.php | 2 +- src/Meta/MySql/Schema.php | 4 ++-- src/Meta/Postgres/Schema.php | 36 ++++++++++++++++++------------------ src/Meta/SchemaManager.php | 16 +++++++++++++--- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/Coders/Model/Factory.php b/src/Coders/Model/Factory.php index 84ea1936..928a2155 100644 --- a/src/Coders/Model/Factory.php +++ b/src/Coders/Model/Factory.php @@ -96,7 +96,7 @@ protected function models() */ public function on($connection = null) { - $this->schemas = new SchemaManager($this->db->connection($connection)); + $this->schemas = new SchemaManager($this->db->connection($connection), $connection); return $this; } diff --git a/src/Meta/MySql/Schema.php b/src/Meta/MySql/Schema.php index d7256dd6..89547737 100644 --- a/src/Meta/MySql/Schema.php +++ b/src/Meta/MySql/Schema.php @@ -39,7 +39,7 @@ class Schema implements \Reliese\Meta\Schema * @param string $schema * @param \Illuminate\Database\MySqlConnection $connection */ - public function __construct($schema, $connection) + public function __construct($schema, $connection, $connectionName = '') { $this->schema = $schema; $this->connection = $connection; @@ -268,7 +268,7 @@ protected function resolveForeignTable($table, Blueprint $blueprint) * * @return array */ - public static function schemas(Connection $connection) + public static function schemas(Connection $connection, $connectionName) { $schemas = $connection->select('SELECT schema_name FROM information_schema.schemata'); $schemas = array_column($schemas, 'schema_name'); diff --git a/src/Meta/Postgres/Schema.php b/src/Meta/Postgres/Schema.php index be986c69..82bc1072 100644 --- a/src/Meta/Postgres/Schema.php +++ b/src/Meta/Postgres/Schema.php @@ -37,7 +37,7 @@ class Schema implements \Reliese\Meta\Schema /** * @var mixed|null */ - protected $schema_database = null; + protected $schemas = null; /** * Mapper constructor. @@ -45,12 +45,16 @@ class Schema implements \Reliese\Meta\Schema * @param string $schema * @param \Illuminate\Database\PostgresConnection $connection */ - public function __construct($schema, $connection) + public function __construct($schema, $connection, $connectionName = 'pgsql') { - $this->schema_database = Config::get("database.connections.pgsql.schema"); - if (!$this->schema_database){ - $this->schema_database = 'public'; + $this->schemas = Config::get(sprintf('database.connections.%s.search_path', $connectionName)); + if (!$this->schemas) { + $this->schemas = Config::get(sprintf('database.connections.%s.schema', $connectionName)); + if (!$this->schemas) { + $this->schemas = ['public']; + } } + $this->schema = $schema; $this->connection = $connection; @@ -92,7 +96,8 @@ protected function load() protected function fetchTables() { $rows = $this->arraify($this->connection->select( - "SELECT * FROM pg_tables where schemaname='$this->schema_database'" + 'SELECT * FROM pg_tables ' . + "WHERE schemaname IN ('" . implode("', '", $this->schemas) . "')" )); $names = array_column($rows, 'tablename'); @@ -105,9 +110,9 @@ protected function fetchTables() protected function fillColumns(Blueprint $blueprint) { $rows = $this->arraify($this->connection->select( - 'SELECT * FROM information_schema.columns '. - "WHERE table_schema='$this->schema_database'". - 'AND table_name='.$this->wrap($blueprint->table()) + 'SELECT * FROM information_schema.columns ' . + "WHERE table_schema IN ('" . implode("', '", $this->schemas) . "')" . + 'AND table_name = '.$this->wrap($blueprint->table()) )); foreach ($rows as $column) { $blueprint->withColumn( @@ -281,16 +286,11 @@ protected function wrap($table) * * @return array */ - public static function schemas(Connection $connection) + public static function schemas(Connection $connection, string $connectionName = 'pgsql') { - $schemas = $connection->select('SELECT datname FROM pg_database'); - $schemas = array_column($schemas, 'datname'); - - return array_diff($schemas, [ - 'postgres', - 'template0', - 'template1', - ]); + return [ + Config::get(sprintf('database.connections.%s.database', $connectionName)) + ]; } /** diff --git a/src/Meta/SchemaManager.php b/src/Meta/SchemaManager.php index 0bfb4fd3..d43a0adf 100644 --- a/src/Meta/SchemaManager.php +++ b/src/Meta/SchemaManager.php @@ -38,6 +38,11 @@ class SchemaManager implements IteratorAggregate */ private $connection; + /** + * @var string + */ + private $connectionName; + /** * @var \Reliese\Meta\Schema[] */ @@ -48,9 +53,10 @@ class SchemaManager implements IteratorAggregate * * @param \Illuminate\Database\ConnectionInterface $connection */ - public function __construct(ConnectionInterface $connection) + public function __construct(ConnectionInterface $connection, string $connectionName = '') { $this->connection = $connection; + $this->connectionName = $connectionName; $this->boot(); } @@ -64,7 +70,11 @@ public function boot() throw new RuntimeException("There is no Schema Mapper registered for [{$this->type()}] connection."); } - $schemas = forward_static_call([$this->getMapper(), 'schemas'], $this->connection); + /** + * @see \Reliese\Meta\Postgres\Schema::schemas() + * @see \Reliese\Meta\MySql\Schema::schemas() + */ + $schemas = forward_static_call([$this->getMapper(), 'schemas'], $this->connection, $this->connectionName); foreach ($schemas as $schema) { $this->make($schema); @@ -94,7 +104,7 @@ protected function makeMapper($schema) { $mapper = $this->getMapper(); - return new $mapper($schema, $this->connection); + return new $mapper($schema, $this->connection, $this->connectionName); } /** From 46ca827d3108e5e38d19157e4d7772b8d6b2426a Mon Sep 17 00:00:00 2001 From: Simon Benjamin <163124+Benjaminhu@users.noreply.github.com> Date: Wed, 4 Jun 2025 22:43:37 +0200 Subject: [PATCH 2/4] [info] fix Sqlite schemas link --- src/Meta/SchemaManager.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Meta/SchemaManager.php b/src/Meta/SchemaManager.php index d43a0adf..8ad1d852 100644 --- a/src/Meta/SchemaManager.php +++ b/src/Meta/SchemaManager.php @@ -73,6 +73,7 @@ public function boot() /** * @see \Reliese\Meta\Postgres\Schema::schemas() * @see \Reliese\Meta\MySql\Schema::schemas() + * @see \Reliese\Meta\Sqlite\Schema::schemas() */ $schemas = forward_static_call([$this->getMapper(), 'schemas'], $this->connection, $this->connectionName); From 447b7736e70d490d3a87bec860354df1382ab18c Mon Sep 17 00:00:00 2001 From: Simon Benjamin <163124+Benjaminhu@users.noreply.github.com> Date: Wed, 4 Jun 2025 22:57:46 +0200 Subject: [PATCH 3/4] [info] fix schemanames and $table definition: `database.table` -> `schema.table` --- src/Meta/Blueprint.php | 28 +++++++++++++++++--- src/Meta/Postgres/Schema.php | 50 ++++++++++++++++++++++++------------ 2 files changed, 57 insertions(+), 21 deletions(-) diff --git a/src/Meta/Blueprint.php b/src/Meta/Blueprint.php index 8e920ff6..e2bb3507 100644 --- a/src/Meta/Blueprint.php +++ b/src/Meta/Blueprint.php @@ -21,6 +21,11 @@ class Blueprint */ protected $schema; + /** + * @var string + */ + protected $schemaname; + /** * @var string */ @@ -63,11 +68,12 @@ class Blueprint * @param string $schema * @param string $table */ - public function __construct($connection, $schema, $table, $isView = false) + public function __construct($connection, $schema, $table, $isView = false, $schemaname = '') { $this->connection = $connection; - $this->schema = $schema; - $this->table = $table; + $this->schema = $schema; // DATABASE + $this->schemaname = $schemaname; // (real) SCHEMA + $this->table = $table; // TABLE $this->isView = $isView; } @@ -79,6 +85,14 @@ public function schema() return $this->schema; } + /** + * @return string + */ + public function schemaname() + { + return $this->schemaname; + } + /** * @return string */ @@ -92,7 +106,13 @@ public function table() */ public function qualifiedTable() { - return $this->schema().'.'.$this->table(); + $schemaname = $this->schemaname(); + $table = $this->table(); + if ('' !== $schemaname) { + return sprintf('%s.%s', $schemaname, $table); + } + + return sprintf('%s.%s', $this->schema(), $table); } /** diff --git a/src/Meta/Postgres/Schema.php b/src/Meta/Postgres/Schema.php index 82bc1072..4b0771e2 100644 --- a/src/Meta/Postgres/Schema.php +++ b/src/Meta/Postgres/Schema.php @@ -2,7 +2,6 @@ namespace Reliese\Meta\Postgres; -use Illuminate\Support\Arr; use Illuminate\Support\Facades\Config; use Reliese\Meta\Blueprint; use Illuminate\Support\Fluent; @@ -35,9 +34,9 @@ class Schema implements \Reliese\Meta\Schema protected $tables = []; /** - * @var mixed|null + * @var array */ - protected $schemas = null; + protected $schemanames = []; /** * Mapper constructor. @@ -47,12 +46,14 @@ class Schema implements \Reliese\Meta\Schema */ public function __construct($schema, $connection, $connectionName = 'pgsql') { - $this->schemas = Config::get(sprintf('database.connections.%s.search_path', $connectionName)); - if (!$this->schemas) { - $this->schemas = Config::get(sprintf('database.connections.%s.schema', $connectionName)); - if (!$this->schemas) { - $this->schemas = ['public']; - } + $this->schemanames = Config::get(sprintf('database.connections.%s.search_path', $connectionName)); + if (!$this->schemanames) { + $this->schemanames = [ + Config::get(sprintf('database.connections.%s.schema', $connectionName)), + ]; + } + if (!$this->schemanames) { + $this->schemanames = ['public']; } $this->schema = $schema; @@ -78,9 +79,10 @@ protected function load() // Note that "schema" refers to the database name, // not a pgsql schema. $this->connection->raw('\c '.$this->wrap($this->schema)); - $tables = $this->fetchTables($this->schema); - foreach ($tables as $table) { - $blueprint = new Blueprint($this->connection->getName(), $this->schema, $table); + $tableDatas = $this->fetchTables(); + foreach ($tableDatas as $tableData) { + $table = $tableData['tablename']; + $blueprint = new Blueprint($this->connection->getName(), $this->schema, $table, false, $tableData['schemaname']); $this->fillColumns($blueprint); $this->fillConstraints($blueprint); $this->tables[$table] = $blueprint; @@ -97,11 +99,17 @@ protected function fetchTables() { $rows = $this->arraify($this->connection->select( 'SELECT * FROM pg_tables ' . - "WHERE schemaname IN ('" . implode("', '", $this->schemas) . "')" + "WHERE schemaname IN ('" . implode("', '", $this->schemanames) . "')" )); - $names = array_column($rows, 'tablename'); + $names = []; + foreach ($rows as $row) { + $names[] = [ + 'tablename' => $row['tablename'], + 'schemaname' => $row['schemaname'], + ]; + } - return Arr::flatten($names); + return $names; } /** @@ -111,7 +119,7 @@ protected function fillColumns(Blueprint $blueprint) { $rows = $this->arraify($this->connection->select( 'SELECT * FROM information_schema.columns ' . - "WHERE table_schema IN ('" . implode("', '", $this->schemas) . "')" . + "WHERE table_schema IN ('" . implode("', '", $this->schemanames) . "')" . 'AND table_name = '.$this->wrap($blueprint->table()) )); foreach ($rows as $column) { @@ -301,6 +309,14 @@ public function schema() return $this->schema; } + /** + * @return array + */ + public function schemanames() + { + return $this->schemanames; + } + /** * @param string $table * @@ -334,7 +350,7 @@ public function table($table) } /** - * @return \Illuminate\Database\MySqlConnection + * @return \Illuminate\Database\PostgresConnection */ public function connection() { From b2277ab5d4680c1af78e0c9a4ba947cc17a685e4 Mon Sep 17 00:00:00 2001 From: Simon Benjamin <163124+Benjaminhu@users.noreply.github.com> Date: Wed, 4 Jun 2025 23:01:42 +0200 Subject: [PATCH 4/4] [info] add `orchestra/testbench` for laravel package testing, fix tests namespaces, change bootstrap file, `BlueprintTests` expanded, add new `Postgres\SchemaTest` --- composer.json | 9 +- phpunit.xml | 13 ++- tests/Coders/Console/Model/ModelTest.php | 24 +++-- tests/Coders/Model/ConfigTest.php | 5 +- .../Coders/Model/Relations/BelongsToTest.php | 5 +- tests/Coders/Model/Relations/HasManyTest.php | 6 +- .../Model/Relations/RelationHelperTest.php | 5 +- tests/Meta/BlueprintTest.php | 53 ++++++++++- tests/Meta/Postgres/SchemaTest.php | 91 +++++++++++++++++++ tests/TestCase.php | 35 ++++++- tests/bootstrap.php | 15 --- 11 files changed, 223 insertions(+), 38 deletions(-) create mode 100644 tests/Meta/Postgres/SchemaTest.php delete mode 100644 tests/bootstrap.php diff --git a/composer.json b/composer.json index 1d536f22..cbfbeb35 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,8 @@ "require-dev": { "fzaninotto/faker": "~1.4", "mockery/mockery": ">=1.4", - "phpunit/phpunit": "^9" + "phpunit/phpunit": "^9", + "orchestra/testbench": "^6.2" }, "autoload": { "psr-4": { @@ -34,9 +35,9 @@ } }, "autoload-dev": { - "classmap": [ - "tests/TestCase.php" - ] + "psr-4": { + "Reliese\\Tests\\": "tests" + } }, "config": { "preferred-install": "dist" diff --git a/phpunit.xml b/phpunit.xml index d154e28b..748c0534 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,5 +1,16 @@ - + ./src diff --git a/tests/Coders/Console/Model/ModelTest.php b/tests/Coders/Console/Model/ModelTest.php index 8ff3bb01..2c67210e 100644 --- a/tests/Coders/Console/Model/ModelTest.php +++ b/tests/Coders/Console/Model/ModelTest.php @@ -1,10 +1,18 @@ assertEquals('connection', $blueprint->connection()); - $this->assertEquals('schema', $blueprint->schema()); - $this->assertEquals('table', $blueprint->table()); + $this->assertSame('connection', $blueprint->connection()); + $this->assertSame('schema', $blueprint->schema()); + $this->assertSame('table', $blueprint->table()); + $this->assertFalse($blueprint->isView()); + $this->assertSame('', $blueprint->schemaname()); + } + + public function test_it_is_view() + { + $blueprint = new Blueprint('connection', 'schema', 'table', true); + + $this->assertSame('connection', $blueprint->connection()); + $this->assertSame('schema', $blueprint->schema()); + $this->assertSame('table', $blueprint->table()); + $this->assertTrue($blueprint->isView()); + $this->assertSame('', $blueprint->schemaname()); + } + + public function test_it_schemaname() + { + $blueprint = new Blueprint('connection', 'schema', 'table', false, 'schemaname'); + + $this->assertSame('connection', $blueprint->connection()); + $this->assertSame('schema', $blueprint->schema()); + $this->assertSame('table', $blueprint->table()); + $this->assertFalse($blueprint->isView()); + $this->assertSame('schemaname', $blueprint->schemaname()); + } + + + public function provideQualifiedTablePermutations() + { + return [ + // schema, table, schemaname, expected + ['schema', 'table', '', 'schema.table'], + ['schema', 'table', 'schemaname', 'schemaname.table'], + ]; + } + + /** + * @dataProvider provideQualifiedTablePermutations + */ + public function testQualifiedTable($schema, $table, $schemaname, $expected) + { + $blueprint = new Blueprint('connection', $schema, $table, false, $schemaname); + + $this->assertSame($expected, $blueprint->qualifiedTable()); } } diff --git a/tests/Meta/Postgres/SchemaTest.php b/tests/Meta/Postgres/SchemaTest.php new file mode 100644 index 00000000..a49dc644 --- /dev/null +++ b/tests/Meta/Postgres/SchemaTest.php @@ -0,0 +1,91 @@ +connection = Mockery::mock(PostgresConnection::class); + + $this->connection->shouldReceive('raw')->andReturns(); + $this->connection->shouldReceive('select')->andReturns( + /** $names in @see \Reliese\Meta\Postgres\Schema::fetchTables() */ + [], + + /** $relations in @see \Reliese\Meta\Postgres\Schema::fillConstraints() */ + [], + + /** $indexes in @see \Reliese\Meta\Postgres\Schema::fillConstraints() */ + [], + ); + $this->connection->shouldReceive('getName')->andReturn('testdatabase'); + } + + public function test_it_can_be_instantiated() + { + $schema = new Schema('schema', $this->connection); + + $this->assertSame('schema', $schema->schema()); + $this->assertSame(['public'], $schema->schemanames()); + $this->assertFalse($schema->has('test')); + $this->assertSame([], $schema->tables()); + $this->assertSame($this->connection, $schema->connection()); + } + + public function test_it_search_path() + { + $schemanames = [ + 'public', + 'another_schema', + ]; + $this->app['config']->set('database.connections.test_connection.search_path', $schemanames); + $this->app['config']->set('database.connections.test_connection.schema', 'schemaname'); + $schema = new Schema('schema', $this->connection, 'test_connection'); + + $this->assertSame($schemanames, $schema->schemanames()); + } + + public function test_it_schema() + { + $schema = 'schemaname'; + $schemanames = [ + $schema, + ]; + $this->app['config']->set('database.connections.test_connection.schema', $schema); + $schema = new Schema('schema', $this->connection, 'test_connection'); + + $this->assertSame($schemanames, $schema->schemanames()); + } + + public function provideSchemasPermutations() + { + return [ + // connectionName, database, expected + ['pgsql', 'testdatabase', ['testdatabase']], + ['another_connection', 'another_database', ['another_database']], + ]; + } + + /** + * @dataProvider provideSchemasPermutations + */ + public function testSchemas($connectionName, $database, $expected) + { + $this->app['config']->set(sprintf('database.connections.%s.database', $connectionName), $database); + + $this->assertSame($expected, Schema::schemas($this->connection, $connectionName)); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index a3f9e4e4..dbaa68cb 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,10 +1,17 @@ set('database.default', 'testbench'); + }); + } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php deleted file mode 100644 index a3f411df..00000000 --- a/tests/bootstrap.php +++ /dev/null @@ -1,15 +0,0 @@ -