【问题标题】:Laravel Eloquent eager loading on a different connectionLaravel Eloquent 急切加载不同的连接
【发布时间】:2015-02-09 19:18:06
【问题描述】:

我正在尝试使用 Laravel Eloquent ORM 在两个不同的连接上建立简单的一对一关系。

假设我愿意:

MyModel::on('secondary_connection')->get()

效果很好。

当我这样做时:

MyModel::on('secondary_connection')->with('AnotherModel')->get();

我收到一个错误,因为 eloquent 正在对默认连接(而不是“secondary_connection”)执行 AnotherModel SELECT 语句。

我找不到解决此问题的方法。

我的模型定义明确,因为我可以将它们加入我的默认连接。

想法?

【问题讨论】:

  • 你在使用存储库吗???
  • 我对这个概念不是很熟悉,但我会说不。每个模型都扩展了 Eloquent 类。
  • 试试->with(['AnotherModel' => function($q){ $q->on('secondary_connection'); }])
  • @lukasgeiter Builder 是使用连接作为依赖创建的,并且无法在已经实例化的对象上更改它。这就是在急切加载期间发生的情况,因此无法即时实现。
  • @JarekTkaczyk 是的,这很有道理......

标签: php laravel eloquent loading eager


【解决方案1】:

好吧,正如许多用户所建议的那样,似乎没有办法即时执行此操作。我对此的理解是,Eloquent 在管理多连接方面是不完善的。

有两种方法可以解决这个问题。

首先,在模型中指定连接:

class MyModel {
    $protected connection = 'secondary_connection';
}

这显然是一个糟糕的解决方法,因为这个模型只能在一个连接中使用......但仍然有效。

然后,正如 Jarek Tkaczyk 所建议的,可以将默认连接切换为新连接。但不是在配置文件中执行此操作,而是可以交换 PDO 对象。

    $default = DB::getPdo(); // Default conn
    $secondary = DB::connection('secondary_connection')->getPdo();
    DB::setPdo($secondary);
    $result = MyModel::with('AnotherModel')->get();
    DB::setPdo($default);

这是一种可行的解决方法,并且可以是一个干净的解决方案。下一步是将这种切换机制以一种不错的 Laravel 方式。

【讨论】:

    【解决方案2】:

    实际上没有办法即时进行。

    您需要更改默认连接以使 Eloquent 将其用于预先加载的模型。您可以将它包装在一个辅助方法中,如下所示:

    function on($connection, Closure $callback)
    {
        // backup default connection
        $default = Config::get('database.default');
    
        // change for current query
        Config::set('database.default', $connection);
    
        // run the query
        $result = $callback();
    
        // restore the default connection
        Config::set('database.default', $default);
    
        return $result;
    }
    

    然后像下面这样调用:

    $models = on('secondary_connection', function () {
       return MyModel::with('relation')->get();
    });
    

    【讨论】:

    • 那没有成功。保持关系的请求被发送到错误的连接。如果连接已经打开,更改配置文件没有效果。
    • 如果您没有在模型上设置 $connection 属性,它可以工作。那么,你呢?
    • 不,我没有。在交换配置之前,您是否对默认连接进行过查询? (它应该实例化一个 PDO)。我正在使用 Laravel 4.2
    【解决方案3】:

    我最终用更多的自定义函数替换了简单的关系函数。例如,我的->relatedModel 在某种程度上是 Eloquent 模型中原件的副本,但包括设置连接:

    public function relatedModel() {
      $instance = new \relatedModel;
      $instance->setConnection($this->getConnectionName());
      $query = $instance->newQuery();
    
      return new BelongsTo($query, $this, 'myForeignKey', $instance->getKeyName(), null);
    }
    

    我在 Laravel 5 上,所以这可能与 4.2 不同。

    由于我一直在摆弄这个问题,现在这里有一个很好的方法来通过另一个连接而不是默认连接完成验证:http://laravel.io/forum/10-29-2014-validation-rules-and-multiple-db-connections

    如果是模型方法,你甚至可以将其缩短为:

    $v = Validator::make($data, $rules);
    $v->getPresenceVerifier()->setConnection($this->getConnectionName());
    

    【讨论】:

      【解决方案4】:

      我也在处理这个问题。我找到了一个解决方案,但它只有在可以通过 .env 文件配置使用的连接时才有效。如果您想动态更改它,这将不起作用,尽管在这种情况下您可以使用一些逻辑(调用函数)来返回正确的连接名称。

      我的解决方案是覆盖 Model 类的 connectionName 方法。就我而言,我的所有模型都有一个基类来处理这个问题:

      abstract class ModelBase extends Model
      {
          public function getConnectionName()
          {
              return env('ELOQUENT_DB_CONNECTION', parent::getConnectionName());
          }
      }
      

      正如我所提到的,您可以在 getConnectionName 中放入任何您想要的逻辑,这样您就可以使其更具动态性。

      【讨论】:

        【解决方案5】:
        class MyModel {
            $protected connection = 'secondary_connection';
        }
        

        是一个不错的选择,因为雄辩的模型是 DB 外观 + 附加功能。所有 DB 外观功能也适用于 eloquent 模型。因此,我们可以在这里更改它,而不是更改 db 外观中的连接,只是没有区别。

        【讨论】:

          最近更新 更多