2015-03-16 3 views
0

Это свой родом прослеживание вопроса отнесколько аренды в Laravel красноречивого ORM

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

Тем не менее, я обнаружил, что он работал только на методы запросов, а не методы, как модели :: создать ::

$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); 
} 

Кажется, что :: создать метод не разрешает соединение один раз экземпляр был создан. Кто-нибудь знает исправление?

ответ

1

Проблема заключается в том, что setConnection() работает с экземпляром класса, но метод create() является статическим методом для самого класса. В вашем репозитории $this->product является экземпляром класса 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); 
    } 
} 
+0

Еще раз спасибо @patricus за ответы. на ваш взгляд, как вы думаете, исходный исходный код должен попытаться разрешить соединение перед вызовом save() в методе create? – yulun

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

вы можете прикрепить этот же наблюдатель, как много моделей, как это требуется, нужно просто добавить их в метод загрузки, например:

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

Это должно работать со всеми вашими существующими хранилищами, при условии, что ваши репозитории будут использовать Eloquent под капотом, который я предполагаю, что они делают.

Код выше не тестировался, и специфика окружающего класса соединений, впрыскиваемый в наблюдатель может быть выключена, но на основе документации по БД Фасада - не могут ли еще ссылок :(

EDIT

Посмотрев в patricus ответ в немного более подробно, используя событие модели должен решить свою проблему с статическим характером метода создания.

экземпляром модели спасаемым передаются каждая модель события, в моем примере выше, как $model, поэтому проблем не должно быть ниже: вместо этого в вашем модельном наблюдателе:

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

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

Это должно быть гораздо более эффективное решение.

+0

Обновления для улучшения предлагаемого кода – Beevee

Смежные вопросы