Plusieurs préfixes de table MYSQL avec Laravel

Plusieurs préfixes de table MYSQL avec Laravel

Date de publication : 23/09/2024

Note de contexte

En gestion d'une application multi-servicielle, j'ai pour mission de créer un hub client permettant à ses utilisateurs d'accéder à la donnée provenant d'une même base de données, mais avec des préfixes de tables différents.

L'usage des préfixes de tables est très pratique pour segmenter les applications ou les fonctionnalités au sein d'une même base de données. Cela permet de conserver une architecture de données unifiée tout en isolant les accès et les données propres à chaque service ou module.

La configuration

Au sein de votre application Laravel, il existe le fichier config/database.php qui permet de dresser la liste des connexions SQL nécéssaires, par défaut sur mysql, la config est la suivante :

return [
  	...,
	"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'),
	  ]) : [],
	],
  	...,
];

Notez la présence du préfixe à vide dans le tableau. Si un préfixe est défini ici, le builder de query de Laravel l'ajoutera automatiquement dans les requêtes SQL générées. C'est donc dans ce fichier qu'on va définir l'accès à nos tables, en ajoutant autant de connexions que de préfixes.

Factoriser la configuration des connexions

Je ne vais pas répéter la config de base à chaque fois, je sors la config de base du tableau renvoyé par la config :

$baseConnection = [
  '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'),
  ]) : [],
];

Surcharger nos préfixes

et dans ma config, je n'ai plus qu'à sur-charger en fonction du besoin en utilisant l'opérateur de décomposition (...) .

$baseConnection = [/* ma config de base */];

return [
  	'mysql_pref_1' => [
	  	...$baseConnection, 'prefix' => 'pref_1_'
	 ],
  	'mysql_pref_2' => [
	  	...$baseConnection, 'prefix' => 'pref_2_'
	 ]
]

Notez que cette méthode vous permet de surcharger ce que vous voulez au sein d'un tableau PHP.

Comment accèder à la connexion en fonction du Model

Par défaut, Laravel utilise la connexion définit dans votre fichier .env soit mysql.

Laravel nous permet simplement de spécifier la connexion au sein du model via la propriété protégée :

protected $connection = 'mysql_pref_2';

Cela nous oblige à spécifier la connexion dans chaque model, ce n'est pas très intéressant, pour modifier ce comportement, nous allons utiliser l'héritage de classe, PHP ne propose pas d'héritage multiple, (cette manière part du principe 1 pour 1 qu'un model = 1 connexion).

Créons un classe, MonPref2Model qui étendera Model.

namespace App\Models\NamespacePref2;

use Illuminate\Database\Eloquent\Model;

abstract class MonPref2Model extends Model
{
    protected $connection = 'mysql_pref_2';
}

Ainsi dans votre model, vous n'avez plus qu'à étendre ce model :

namespace App\Models\NamespacePref2;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use App\Models\NamespacePref2\OrderLine;

class Order extends MonPref2Model
{
    use HasFactory;
  
  	protected $table = 'orders';
  
 	...
}

Et utiliser votre model comme bon vous semble :

$orders = Order::paginate(12);
...

Voilà c'est tout, j'espère que cet article vous a intéressé, si vous voyez des points d'optimisation et une façon plus élégante de le faire, n'hésitez pas à les partager en commentaire.

Vos commentaires
Aucun commentaire