Использование нескольких баз данных в проекте Laravel
Пример проекта Laravel можно найти на нашем репозитории Github.
Стремясь сохранить ясность в каждом из своих проектов, я разделяю базы данных в зависимости от выполняемой ими роли. Например, этот блог включает в себя несколько баз данных: одна специально для блога, другая — для аналитики. В этой статье объясняется, как это сделать.
Новый проект Laravel уже содержит в своём файле .env
информацию, связанную с базой данных, включая установленное по умолчанию соединение mysql. Мы будем работать с двумя базами данных: one
и two
. Также будет установлено соединение с one
(опционально).
.env
До:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=<database-name>
DB_USERNAME=
DB_PASSWORD=
После:
DB_CONNECTION=one
DB_ONE_HOST=127.0.0.1
DB_ONE_PORT=3306
DB_ONE_DATABASE=one
DB_ONE_USERNAME=
DB_ONE_PASSWORD=
DB_TWO_HOST=127.0.0.1
DB_TWO_PORT=3306
DB_TWO_DATABASE=two
DB_TWO_USERNAME=
DB_TWO_PASSWORD=
Информация из файла .env
по умолчанию отражается в конфигурационном файле database.php
.
config/database.php
'connections' => [
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
...
]
Продублируем информацию о подключении столько раз, сколько будет подключений.
'connections' => [
'one' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_ONE_HOST', '127.0.0.1'),
'port' => env('DB_ONE_PORT', '3306'),
'database' => env('DB_ONE_DATABASE', 'forge'),
'username' => env('DB_ONE_USERNAME', 'forge'),
'password' => env('DB_ONE_PASSWORD', ''),
'unix_socket' => env('DB_ONE_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'two' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_TWO_HOST', '127.0.0.1'),
'port' => env('DB_TWO_PORT', '3306'),
'database' => env('DB_TWO_DATABASE', 'forge'),
'username' => env('DB_TWO_USERNAME', 'forge'),
'password' => env('DB_TWO_PASSWORD', ''),
'unix_socket' => env('DB_TWO_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
Затем необходимо дать указание миграциям мигрировать в разные созданные базы данных:
one
→2023_08_31_000000_create_foos_table.php
two
→2023_08_31_000001_create_bars_table.php
Это позволяет сделать статическая функция connection('<connection-name>')
фасада Schema
, которую мы добавляем в функции up()
и down()
.
2023_08_31_000000_create_foos_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up() : void
{
Schema::connection( 'one' )->create( 'foos', function( Blueprint $table )
{
$table->id();
$table->timestamps();
});
}
public function down() : void
{
Schema::connection( 'one' )->dropIfExists( 'foos' );
}
};
2023_08_31_000001_create_bars_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up() : void
{
Schema::connection( 'two' )->create( 'bars', function( Blueprint $table )
{
$table->id();
$table->timestamps();
});
}
public function down() : void
{
Schema::connection( 'two' )->dropIfExists( 'bars' );
}
};
Далее необходимо модифицировать модели, связанные с миграциями, указав их связь с базой данных через атрибут $connection
.
App\Models\Foo.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Foo extends Model
{
protected $connection = 'one';
}
App\Models\Bar.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Bar extends Model
{
protected $connection = 'two';
}
Теперь мы можем запустить команду миграции php artisan migrate
. По умолчанию эта команда использует значение, заданное параметром DB_CONNECTION
. Если оно не определено в файле .env
, то его необходимо указать в команде php artisan migrate --database=one
.
Для проверки работоспособности мы можем быстро реализовать анонимную функцию, вызываемую основным маршрутом.
routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Models\Foo;
use App\Models\Bar;
Route::get( '/', function()
{
$foo = Foo::create();
$bar = Bar::create();
dd( $foo, $bar );
});
Затем эти значения создаются в соответствующих базах данных и становятся видимыми в браузере.
В случае необходимости обновления базы данных с помощью команды php artisan migrate:fresh
следует отметить, что будет обновлена только база данных по умолчанию, т.е. та, которая указана в DB_CONNECTION
. К сожалению, Laravel пока не поддерживает одновременное обновление нескольких баз данных.
Для обновления базы данных, не являющейся базой по умолчанию, необходимо использовать команду php artisan db:wipe --database=<database-name>
. Эту команду можно повторить для каждой дополнительной базы данных. После того как все базы данных будут корректно очищены с помощью команды db:wipe
, можно без ошибок приступать к выполнению команды php artisan migrate:fresh
.
Можно также разработать собственную команду, автоматизирующую выполнение различных задач по очистке базы данных.