Laravel Eloquent ORM 中的多租户
Multi tenancy in Laravel Eloquent ORM
这是
的后续问题
具有多个数据库连接:
return [
'default' => 'mysql-key1',
'connections' => [
'mysql-key1' => [
'driver' => 'mysql',
'database' => 'key1_dbname,
// etc
],
'mysql-key2' => [
'driver' => 'mysql',
'database' => 'key2_dbname',
// etc
]
]
];
我有一个产品存储库,它使用模型 setConnection 来更改连接属性:
public function setConnection($name) {
// assumes $this->product is your Product model
$this->product->setConnection($name);
}
但是,我发现它只适用于查询方法,而不适用于 Model::create ::
这样的方法
$productRepo = new ProductRepository();
foreach ($array as $key => $value) {
$productRepo->setConnection($key . '_' . $database);
// this works
$products = $productRepo->all();
// this doesn't work and will use the old connection
$product = $productRepo->create($data);
}
实例创建后,::create 方法似乎无法解析连接。有人知道解决方法吗?
问题是 setConnection()
在 class 的实例上工作,但 create()
方法是 class 本身的静态方法。在您的存储库中,$this->product
是产品 class 的一个实例。在执行查询之前在此实例上使用 setConnection()
可以正常工作,但如果您想在静态方法上使用单独的连接(例如 create()
),则需要做更多的手动工作。
所有 create()
方法所做的就是用给定的属性实例化一个新实例,然后调用 save()
。因此,无需在 Product 模型上调用 create()
,您只需手动执行此操作:
class ProductRepository {
public function create(array $attributes, $connection = null) {
$product = $this->product->newInstance($attributes);
$product->setConnection($connection ?: $this->product->getConnectionName());
$product->save();
return $product;
}
}
您也可以覆盖 Product 模型上的静态 create()
方法来接受连接。
class Product extends Model {
public static function create(array $attributes, $connection = null) {
$model = new static($attributes);
$model->setConnection($connection ?: $this->connection);
$model->save();
return $model;
}
}
class ProductRepository {
public function create(array $attributes, $connection = null) {
$connection = $connection ?: $this->product->getConnectionName()
return $this->product->create($attributes, $connection);
}
}
您应该能够利用模型事件和观察者来操纵正在使用的连接,文档可在此处获得:
您可以创建一个 DatabaseModelObserver(如下所示),您可以将其附加到每个相关模型,这些模型将在保存前设置连接并在保存后重置它,如下所示:
class DatabaseModelObserver {
protected $databases = [
'default' => 'mysql-key1',
'products' => 'mysql-key2'
];
protected $connection;
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
public function saving($model)
{
$this->connection->reconnect($this->databases['products']);
}
public function saved($model)
{
$this->connection->reconnect($this->databases['default']);
}
}
然后您将通过服务提供商将观察者附加到相关模型,使用如下详述的引导方法:
class ModelServiceProvider extends ServiceProvider {
public function register()
{
}
public function boot()
{
ProductModel::observe(
$this->app->make('DatabaseModelObserver')
);
}
}
您可以根据需要将同一个 Observer 附加到任意多个模型,您只需将它们添加到启动方法中,例如:
public function boot()
{
ProductModel::observe(
$this->app->make('DatabaseModelObserver')
);
CategoryModel::observe(
$this->app->make('DatabaseModelObserver')
);
StoreModel::observe(
$this->app->make('DatabaseModelObserver')
);
}
这应该适用于您所有现有的存储库,前提是您的存储库在幕后使用 Eloquent,我假设他们这样做。
上面的代码未经测试,围绕注入观察者的连接 class 的细节可能不正确,但基于 DB Facade 上的文档 - 不能 post 任何更多链接:(
编辑
更详细地研究了 patricus answer 后,使用模型事件应该可以解决他与创建方法的静态性质有关的问题。
正在保存的模型实例被传递给每个模型事件,在我上面的示例中为 $model
因此在您的模型观察器中执行以下操作应该没有问题:
public function saving($model)
{
$model->setConnection($this->databases['products']);
}
public function saved($model)
{
$model->setConnection($this->databases['default']);
}
这应该是一个更有效的解决方案。
这是
的后续问题具有多个数据库连接:
return [
'default' => 'mysql-key1',
'connections' => [
'mysql-key1' => [
'driver' => 'mysql',
'database' => 'key1_dbname,
// etc
],
'mysql-key2' => [
'driver' => 'mysql',
'database' => 'key2_dbname',
// etc
]
]
];
我有一个产品存储库,它使用模型 setConnection 来更改连接属性:
public function setConnection($name) {
// assumes $this->product is your Product model
$this->product->setConnection($name);
}
但是,我发现它只适用于查询方法,而不适用于 Model::create ::
这样的方法$productRepo = new ProductRepository();
foreach ($array as $key => $value) {
$productRepo->setConnection($key . '_' . $database);
// this works
$products = $productRepo->all();
// this doesn't work and will use the old connection
$product = $productRepo->create($data);
}
实例创建后,::create 方法似乎无法解析连接。有人知道解决方法吗?
问题是 setConnection()
在 class 的实例上工作,但 create()
方法是 class 本身的静态方法。在您的存储库中,$this->product
是产品 class 的一个实例。在执行查询之前在此实例上使用 setConnection()
可以正常工作,但如果您想在静态方法上使用单独的连接(例如 create()
),则需要做更多的手动工作。
所有 create()
方法所做的就是用给定的属性实例化一个新实例,然后调用 save()
。因此,无需在 Product 模型上调用 create()
,您只需手动执行此操作:
class ProductRepository {
public function create(array $attributes, $connection = null) {
$product = $this->product->newInstance($attributes);
$product->setConnection($connection ?: $this->product->getConnectionName());
$product->save();
return $product;
}
}
您也可以覆盖 Product 模型上的静态 create()
方法来接受连接。
class Product extends Model {
public static function create(array $attributes, $connection = null) {
$model = new static($attributes);
$model->setConnection($connection ?: $this->connection);
$model->save();
return $model;
}
}
class ProductRepository {
public function create(array $attributes, $connection = null) {
$connection = $connection ?: $this->product->getConnectionName()
return $this->product->create($attributes, $connection);
}
}
您应该能够利用模型事件和观察者来操纵正在使用的连接,文档可在此处获得:
您可以创建一个 DatabaseModelObserver(如下所示),您可以将其附加到每个相关模型,这些模型将在保存前设置连接并在保存后重置它,如下所示:
class DatabaseModelObserver {
protected $databases = [
'default' => 'mysql-key1',
'products' => 'mysql-key2'
];
protected $connection;
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
public function saving($model)
{
$this->connection->reconnect($this->databases['products']);
}
public function saved($model)
{
$this->connection->reconnect($this->databases['default']);
}
}
然后您将通过服务提供商将观察者附加到相关模型,使用如下详述的引导方法:
class ModelServiceProvider extends ServiceProvider {
public function register()
{
}
public function boot()
{
ProductModel::observe(
$this->app->make('DatabaseModelObserver')
);
}
}
您可以根据需要将同一个 Observer 附加到任意多个模型,您只需将它们添加到启动方法中,例如:
public function boot()
{
ProductModel::observe(
$this->app->make('DatabaseModelObserver')
);
CategoryModel::observe(
$this->app->make('DatabaseModelObserver')
);
StoreModel::observe(
$this->app->make('DatabaseModelObserver')
);
}
这应该适用于您所有现有的存储库,前提是您的存储库在幕后使用 Eloquent,我假设他们这样做。
上面的代码未经测试,围绕注入观察者的连接 class 的细节可能不正确,但基于 DB Facade 上的文档 - 不能 post 任何更多链接:(
编辑
更详细地研究了 patricus answer 后,使用模型事件应该可以解决他与创建方法的静态性质有关的问题。
正在保存的模型实例被传递给每个模型事件,在我上面的示例中为 $model
因此在您的模型观察器中执行以下操作应该没有问题:
public function saving($model)
{
$model->setConnection($this->databases['products']);
}
public function saved($model)
{
$model->setConnection($this->databases['default']);
}
这应该是一个更有效的解决方案。