Blog / Utiliser plusieurs bases de données pour un même projet Laravel

Image used for article Utiliser plusieurs bases de données pour un même projet Laravel

Utiliser plusieurs bases de données pour un même projet Laravel




TL;DR: Comment utiliser plusieurs bases de données au sein d'un même projet Laravel et séparer la gestion des informations.




Vous trouverez le code source via ce Github Repository. Découvrez en plus sur Capsules ou X.




Dans un souci de clarté pour chacun de mes projets, je sépare mes bases de données en fonction du rôle qu’elles ont. Ce blog, par exemple, comporte plusieurs bases de données : une pour le blog proprement dit et une autre pour l’analytique. Cet article explique comment procéder.




Un nouveau projet Laravel contient déjà, dans son fichier .env, des informations relatives à la base de données, dont la connexion par défaut mysql. Nous partirons sur deux bases de données MySQL : one et two. Ainsi qu’une connexion à one devenant la base de données par défaut.



.env


Avant

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=<nom-de-la-base-de-donnees>
DB_USERNAME=
DB_PASSWORD=

Après

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=



Dans le cas de bases de données SQLite stockées dans le dossier database :



DB_CONNECTION=one

DB_ONE_DATABASE=one
DB_TWO_DATABASE=two




Les informations du fichier .env par défaut sont reprises dans le fichier de configuration 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'),
    	]) : [],
    ],

    ...
]



Dupliquons ces informations autant de fois qu’il y a de connections.



'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'),
        ]) : [],
    ],

    ...
]



Dans le cas de bases de données SQLite stockées dans le dossier database :



'connections' => [

    'one' => [
        'driver' => 'sqlite',
        'url' => env('DATABASE_URL'),
        'database' => database_path(env('DB_ONE_DATABASE', 'database').'.sqlite'),
        'prefix' => '',
        'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
    ],

    'two' => [
        'driver' => 'sqlite',
        'url' => env('DATABASE_URL'),
        'database' => database_path(env('DB_TWO_DATABASE', 'database').'.sqlite'),
        'prefix' => '',
        'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
    ],

    ...
]




Il faut alors indiquer aux migrations de migrer vers les différentes bases de données créées :

  • one2023_08_31_000000_create_foos_table.php
  • two2023_08_31_000001_create_bars_table.php

La fonction statique connection( '<connection-name>' ) de la Facade Schema permet cela, que l’on ajoute dans la fonction up() et 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' );
    }
};



Il faut ensuite modifier les models relatifs aux migrations pour leur indiquer leur connection avec la database via l’attribut $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';
}




Nous pouvons dès maintenant lancer la migration php artisan migrate. Par défaut, cette commande utilise la valeur donnée par DB_CONNECTION. Si celle-ci n’est pas définie dans le fichier .env , il faut alors l’indiquer dans la commande php artisan migrate --database=one.



Afin de tester la fonctionnalité, nous pouvons rapidement implémenter une fonction anonyme lors de l’appel à la route principale.



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 );
});



Les valeurs sont alors créées dans les bases de données respectives et visibles dans le browser.




Dans le cas où une actualisation des bases de données serait nécessaire en utilisant la commande php artisan migrate:fresh, il est bon de noter que seulement la base de données par défaut, c'est-à-dire celle indiquée par DB_CONNECTION, sera rafraîchie. Malheureusement, Laravel ne supporte pas [ encore ] le rafraîchissement de multiples bases de données en même temps.



Pour rafraîchir une base de données qui n'est pas celle par défaut, il est nécessaire d'utiliser la commande php artisan db:wipe --database=<database-name>. Cette commande peut être répétée pour chaque base de données additionnelle. Une fois que toutes les bases de données ont été correctement nettoyées avec db:wipe, vous pouvez ensuite procéder sans erreurs avec php artisan migrate:fresh.




Vous pouvez aussi développer votre propre commande qui automatiserait les différentes tâches nécessaires pour nettoyer votre base de données.




Ravi d’avoir pu aider.

v1.3.1

Icône XIcône Github