簡體   English   中英

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
        ]
    ]
];

我有一個產品存儲庫,它使用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是Product類的實例。 在執行查詢之前在此實例上使用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,那么這應該可以與您所有現有的存儲庫一起使用,我假設它們是這樣做的。

上面的代碼未經測試,有關注入到觀察器中的連接類的詳細信息可能已關閉,但基於DB Facade上的文檔-無法再發布任何鏈接:(

編輯

在更詳細地研究了愛國者的答案之后,使用模型事件應該以create方法的靜態性質解決他的問題。

保存的Model實例將傳遞給每個模型事件,在上面的示例中為$model因此在模型觀察器中執行以下操作應該沒有問題:

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

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

這應該是一個更有效的解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM