Php Laravel雄辩ORM中的多租户

Php Laravel雄辩ORM中的多租户,php,database,laravel,eloquent,Php,Database,Laravel,Eloquent,这是我的后续问题 使用多个数据库连接: return [ 'default' => 'mysql-key1', 'connections' => [ 'mysql-key1' => [ 'driver' => 'mysql', 'database' => 'key1_dbname, // etc ], 'mysql-key

这是我的后续问题

使用多个数据库连接:

return [
    'default' => 'mysql-key1',
    'connections' => [
        'mysql-key1' => [
            'driver'    => 'mysql',
            'database'  => 'key1_dbname,
            // etc
        ],
        'mysql-key2' => [
            'driver'    => 'mysql',
            'database'  => 'key2_dbname',
            // etc
        ]
    ]
];
我有一个产品存储库,它使用Model 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()
可以在类的实例上工作,但是
create()
方法是类本身的静态方法。在您的存储库中,
$this->product
是产品类的一个实例。在执行查询之前在此实例上使用
setConnection()
可以很好地工作,但是如果要在静态方法上使用单独的连接(例如
create()
),则需要多做一些手动工作

所有的
create()
方法都是用给定的属性实例化一个新实例,然后调用
save()
。因此,您不需要在产品模型上调用
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;
    }
}
您还可以覆盖产品模型上的静态
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);
    }
}

问题是
setConnection()
在类的实例上工作,但是
create()
方法在类本身上是一个静态方法。在您的存储库中,
$this->product
是产品类的一个实例。在执行查询之前在此实例上使用
setConnection()
可以很好地工作,但是如果要在静态方法上使用单独的连接(例如
create()
),则需要多做一些手动工作

所有的
create()
方法都是用给定的属性实例化一个新实例,然后调用
save()
。因此,您不需要在产品模型上调用
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;
    }
}
您还可以覆盖产品模型上的静态
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);
    }
}

问题是
setConnection()
在类的实例上工作,但是
create()
方法在类本身上是一个静态方法。在您的存储库中,
$this->product
是产品类的一个实例。在执行查询之前在此实例上使用
setConnection()
可以很好地工作,但是如果要在静态方法上使用单独的连接(例如
create()
),则需要多做一些手动工作

所有的
create()
方法都是用给定的属性实例化一个新实例,然后调用
save()
。因此,您不需要在产品模型上调用
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;
    }
}
您还可以覆盖产品模型上的静态
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);
    }
}

问题是
setConnection()
在类的实例上工作,但是
create()
方法在类本身上是一个静态方法。在您的存储库中,
$this->product
是产品类的一个实例。在执行查询之前在此实例上使用
setConnection()
可以很好地工作,但是如果要在静态方法上使用单独的连接(例如
create()
),则需要多做一些手动工作

所有的
create()
方法都是用给定的属性实例化一个新实例,然后调用
save()
。因此,您不需要在产品模型上调用
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;
    }
}
您还可以覆盖产品模型上的静态
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')
        );
    }
}
您可以根据需要将同一观察者附加到任意多个模型,只需将它们添加到引导方法中,例如:

public function boot()
{
    ProductModel::observe(
        $this->app->make('DatabaseModelObserver')
    );
    CategoryModel::observe(
        $this->app->make('DatabaseModelObserver')
    );
    StoreModel::observe(
        $this->app->make('DatabaseModelObserver')
    );
}
这应该适用于所有现有的存储库,前提是您的存储库在引擎盖下使用了雄辩的语言,我假设他们会这样做

上面的代码未经测试,关于注入观察器的连接类的细节可能已关闭,但基于DB Facade上的文档-无法发布更多链接:(

编辑

在更详细地研究了patricus answer之后,使用模型事件应该可以解决create方法的静态性质的问题

正在保存的模型的实例被传递到每个模型事件,在我上面的示例中为
$Model
,因此在您的模型观察器中执行以下操作应该没有问题:

public function saving($model)
{
    $model->setConnection($this->databases['products']);
}

public function saved($model)
{
    $model->setConnection($this->databases['default']);
}

这应该是一个更有效的解决方案。

您应该能够利用模型事件和观察者来操纵正在使用的连接,文档可在此处获得:

您可以创建一个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')
        );
    }
}
您可以根据需要将同一观察者附加到任意多个模型,只需将它们添加到引导方法中,例如:

public function boot()
{
    ProductModel::observe(
        $this->app->make('DatabaseModelObserver')
    );
    CategoryModel::observe(
        $this->app->make('DatabaseModelObserver')
    );
    StoreModel::observe(
        $this->app->make('DatabaseModelObserver')
    );
}
这应该适用于所有现有的repositori