[12.x] Enhance multi-database support#54274
Conversation
|
Thanks for submitting a PR! Note that draft PR's are not reviewed. If you would like a review, please mark your pull request as ready for review in the GitHub user interface. Pull requests that are abandoned in draft may be closed due to inactivity. |
|
Will this result in something different than setting the connection property/method? |
|
@NickSdot I hope the updated PR description makes things clearer. |
|
@hafezdivandari Thanks! Can you send a PR to laravel/docs master branch to document the breaking changes and any other updates we need to make? |
|
@taylorotwell sure. |
|
@hafezdivandari thanks for this! How will it affect applications using mulitple databases via seperate and/or dynamic/ephemaral connections, such as multitenant database setups which switch databases at runtime? Also, would this support extending with a closure to set |
@inmanturbo Good question. The main difference lies in how you set up your DB connections, the database driver you're using, and what you aim to achieve. This PR focuses on utilizing multi-schema with a single connection! As I mentioned in the PR description, "database" and "schema" are essentially the same in MySQL and SQLite, but they differ in PostgreSQL and SQL Server.
Are you referring to Eloquent models? In that case, you can override the class Post extends Model
{
// ...
public function getTable()
{
$schema = $this->schema ?? 'my_schema'; // Determine the schema dynamically!
return "$schema.posts";
}
} |
|
@hafezdivandari Thank you for the helpful reply! Your example for models is helpful in demostrating the utility afforded by this PR |
This PR improves support for multi-database aka schemas (using a single connection) setups. Laravel's Eloquent, Query Builder, and most Schema Builder functions already support references in the
schema_name.table_nameformat. Since all supported databases allow multi-schema functionality, this PR ensures consistent usage across all databases and adds full support for it in all Schema Builder methods.Improvements
$schemaargument toSchema::getTables(),Schema::getViews(),Schema::getTypes(), andSchema::getTableListing()methods.$schema = null, returning all tables / views / types of all schemas.$schemaasstringorarraywill return the result for the given schemas only.Schema::hasTable(),Schema::hasView(),Schema::getColumns(),Schema::getIndexes(),Schema::getForeignKeys(),Schema::hasColumn(),Schema::getColumnListing(), andSchema::hasIndex()methods utilizing MySQL, MariaDB, and SQLite, fallback to the current schema if not specified explicitly on the table name.schema_qualified_nameproperty to the returned results ofSchema::getTables(),Schema::getViews(),Schema::getTypes()methods.$schemaQualifiedargument toSchema::getTableListing()method:$schemaQualified = true, returning the table names asschema_name.table_nameformat.schemaQualified = falsevalue results in not schema-qualified table names.Schema::getSchemas()method for all DB drivers to get schemas for the connection with these properties:name(string): Name of the schema.path(?string): Schema file path (SQLite only)default(bool): The schema is the default one.Schema::getCurrentSchemaName()method. This method has been used by the framework internally to determine the name of the current schema for the connection:search_pathconfig property (same asselect current_schema()).select schema()).'main'.select schema_name()query.Schema::getCurrentSchemaListing()method that returns names of schemas currently in-use for the connection. This method has been used by the framework internally, e.g. when dropping / truncating all tables are needed:search_pathconfig property (same asselect current_schemas()).['main']null, means all schemas.schemaproperty on returned result ofSchema::getTables()andSchema::getViews()on MySQL, MariaDB and SQLite (was alwaysnullpreviously).foreign_schemaproperty on returned result ofSchema::getForeignKeys()on SQLite (was alwaysnullpreviously).DB::withoutTablePrefix()method to return the result of the given callback.DB::table('schema_name.table_name')->truncate()db:show,db:tablecommands,Schema\BuilderandSchema\Grammarclasses, removing redundant codes (without any BC change).Behavioral Changes
DB::withoutTablePrefix($callback)method now returns the result of the given callback instead ofvoid.Schema::getTables(),Schema::getViews()andSchema::getTableListing()methods:$schemaargument to filter the result as desired.Schema::getTableListing()method now returns schema-qualified table names by default. Acceptingbool $schemaQualifiedargument to change the behavior as desired.db:tableanddb:showcommand now outputs results of all schemas on MySQL, MariaDB and SQLite, just like PostgreSQL and SQL Server. Consistent usage across all DB drivers.Context
Being able to categorize your database tables is useful, and this is already possible with all databases supported by Laravel. This means you can manage multiple databases with a single MySQL connection! For example, let’s assume you have this connection:
This
mysqlconnection usesprimary_dbas the default "schema," which you already know how to use. But what if you want to interact with multiple MySQL databases? Laravel has you covered:The same example works in MariaDB, PostgreSQL, SQL Server, and even SQLite (with a small change when creating the schema). You can use
schema_name.table_namereferences almost everywhere, regardless of the database driver. However, each database driver handles "schemas" differently, so it's important to abstract these differences to ensure consistent usage at the application layer.Let's compare databases:
MariaDB
"def"(Engine has 1 Catalog)
(Catalog has many databases)
CREATE DATABASE sch_nameor
CREATE SCHEMA sch_name(Schema has many tables)
CREATE sch_name.tbl_name(Many database files attached as Schemas)
ATTACH db_file AS sch_name(Schema has many tables)
CREATE sch_name.tbl_name(Engine has many databases)
CREATE DATABASE db_name(Catalog has many Schemas)
CREATE SCHEMA sch_name(Schema has many tables)
CREATE sch_name.tbl_name(Engine has many databases)
CREATE DATABASE db_name(Catalog has many Schemas)
CREATE SCHEMA sch_name(Schema has many tables)
CREATE db_name.sch_name.tbl_nameThis means:
schema_name.table_namereference.SCHEMAas a synonym forDATABASE(ref). MySQL connects with adatabaseconfig property as the default schema / database and can switches between schemas / databases viaUSE schema_name):memory:) as schemas (ref). SQLite connects to a database file as the default schema named"main".USE db_name). It connects to adatabaseconfig property as the default database and first schema name on thesearch_pathconfig property as the default schema.databaseconfig propertyas the default database, and "dbo" as the default schema. Unlike PostgreSQL, it's possible to switch between databases via
USE db_nameand change the default schema (refer to [11.x] Use Default Schema Name on SQL Server #50855 for more). That's why sqlsrv has three-part referencesdb_name.schema_name.table_name, but Laravel doesn't fully / officially supports three-part references for tables.Let's compare "Current Schema" for each DB driver connection:
MariaDB
[ 'driver' => 'mysql', // 'mariadb' 'database' => 'laravel', ][ 'driver' => 'sqlite', 'database' => database_path('database.sqlite'), ][ 'driver' => 'pgsql', 'database' => 'laravel', 'search_path' => 'public,my_schema', ][ 'driver' => 'sqlsrv', 'database' => 'laravel', ]'laravel'SELECT schema()SELECT database()'main''public'search_pathSELECT current_schema()'dbo'ALTER USERSELECT schema_name()