Multi tenancy in Laravel Eloquent ORM

后端 未结 2 414
耶瑟儿~
耶瑟儿~ 2020-12-22 05:51

This is sort of a follow up question from

Forcing Eloquent models to re resolve database connection

With multiple database connections :

re         


        
相关标签:
2条回答
  • 2020-12-22 06:32

    The problem is that setConnection() works on an instance of a class, but the create() method is a static method on the class itself. In your repository, $this->product is an instance of the Product class. Using setConnection() on this instance before doing queries will work fine, but you'll need to do a little more manual work if you want to use separate connections on the static methods (such as create()).

    All the create() method does is instantiate a new instance with the given attributes and then call save(). So, instead of calling create() on the Product model, you'll just need to do this manually:

    class ProductRepository {
        public function create(array $attributes, $connection = null) {
            $product = $this->product->newInstance($attributes);
            $product->setConnection($connection ?: $this->product->getConnectionName());
            $product->save();
            return $product;
        }
    }
    

    You could also override the static create() method on the Product model to accept a connection, as well.

    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);
        }
    }
    
    0 讨论(0)
  • 2020-12-22 06:41

    You should be able to utilize Model Events & Observers to manipulate the connection being used, documentation is available here:

    • http://laravel.com/docs/4.2/eloquent#model-events

    • http://laravel.com/docs/4.2/eloquent#model-observers

    You could create a single DatabaseModelObserver (As below) that you could attached to each of the relevant models that will set the connection before saving and reset it after saving as below:

    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']);
        }
    }
    

    You would then attach the observer to the relevant models via a Service Provider, utilizing the boot method as detailed below:

    class ModelServiceProvider extends ServiceProvider {
    
        public function register()
        {
        }
    
        public function boot()
        {
            ProductModel::observe(
                $this->app->make('DatabaseModelObserver')
            );
        }
    }
    

    You could attach this same Observer to as many models as required, you just need to add them to the boot method, eg:

    public function boot()
    {
        ProductModel::observe(
            $this->app->make('DatabaseModelObserver')
        );
        CategoryModel::observe(
            $this->app->make('DatabaseModelObserver')
        );
        StoreModel::observe(
            $this->app->make('DatabaseModelObserver')
        );
    }
    

    This should work with all your existing repositories, providing your repositories use Eloquent under the hood, which I'm assuming them do.

    The code above is untested, and the specifics surrounding the connection class injected into the observer may be off, but are based on the documentation on the DB Facade - Can't post any more links :(

    EDIT

    Having looked into patricus answer in a little more detail, using a model event should solve his issue with the static nature of the create method.

    The instance of the Model being saved is passed to each model event, in my example above as $model therefore there should be no issues doing the below in your model observer instead:

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

    This should be a much more efficient solution.

    0 讨论(0)
提交回复
热议问题