【问题标题】:Multi tenancy in Laravel Eloquent ORMLaravel Eloquent ORM 中的多租户
【发布时间】:2015-05-18 02:40:23
【问题描述】:

这是一个来自

的后续问题

Forcing Eloquent models to re resolve database connection

有多个数据库连接:

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 方法在创建实例后无法解析连接。有人知道解决办法吗?

【问题讨论】:

    标签: php database laravel eloquent


    【解决方案1】:

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

    您可以创建一个 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 上的文档 - 无法发布更多链接:(

    编辑

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

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

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

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

    【讨论】:

      【解决方案2】:

      问题是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);
          }
      }
      

      【讨论】:

      • 再次感谢@patricus 的回答。不过,在您看来,您认为原始源代码是否应该在 create 方法中调用 save() 之前尝试解析连接?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-03-16
      • 1970-01-01
      • 2020-11-27
      • 2013-02-16
      • 2015-04-16
      • 1970-01-01
      • 2014-08-19
      相关资源
      最近更新 更多