在Laravel 5.2中使用多个MySQL数据库连接查询关系存在

时间:2016-04-08 13:12:15

标签: mysql laravel eloquent eager-loading relationships

我正在处理以下情况:我有两个模型,Employee idname字段以及Telephone id,{ {1}}和employee_id字段。这两个模型之间还存在一对多关系,即员工可能拥有多部电话,而电话可能属于一名员工。

flag

class Employee extends Model { public function telephones() { return $this->hasMany(Telephone::class); } } class Telephone extends Model { public function employee() { return $this->belongsTo(Employee::class); } } 模型引用名为Employee的数据库模式中存在的表employees,而mydb1模型与存在的Telephone表相关在名为telephones的其他数据库模式中。

我想要的是使用Eloquent 仅使用至少一个特定标志渴望加载的电话员工和(如果可能)不是查询构建器

到目前为止我尝试的没有成功是:

1)使用Controller中的whereHas方法

mydb2

我在这里尝试的方法是首先只检索拥有flag = 1的电话的员工,然后只搜索那些只打算加载这些电话的员工,但我得到以下查询异常因为不同使用的数据库连接:

找不到基表或视图:表mydb1.telephones不存在(这是真的,mydb2中存在电话)

2)在控制器

中使用约束进行预加载
$employees = Employee::whereHas('telephones', function ($query) {

    $query->where('flag', 1); //Fetch only the employees with telephones of flag=1

})->with([

    'telephones' => function ($query) { //Eager load only the telephones of flag=1

        $query->where('flag', 1);
    }

])->get();

这种方法急切地加载了flag = 1的电话,但它返回所有员工实例,这不是我真正想要的。我想收集只有具有$employees = Employee::with([ 'telephones' => function ($query) { $query->where('flag', 1); }, ])->get(); = 1的电话的员工模型,不包括flag的模型

3 个答案:

答案 0 :(得分:2)

考虑到this postthis post和@GiedriusKiršys的回答,我终于想出了一个符合我需求的解决方案,使用以下步骤:

  1. 创建一个在模型
  2. 中返回关系对象的方法
  3. 急切加载 Controller中的这种新关系
  4. 使用模型
  5. 中的查询范围过滤掉flag!= 1的电话

    员工模型

    /**
     * This is the new relationship
     *
     */
    public function flaggedTelephones()
    {
        return $this->telephones()
            ->where('flag', 1); //this will return a relation object
    }
    
    
    
    /**
     *  This is the query scope that filters the flagged telephones
     *
     *    This is the raw query performed:
     *    select * from mydb1.employees where exists (
     *    select * from mydb2.telephones
     *    where telephones.employee_id = employee.id
     *    and flag = 1);
     *
     */    
    public function scopeHasFlaggedTelephones($query, $id)
    {
        return $query->whereExists(function ($query) use ($id) {
            $query->select(DB::raw('*'))
                ->from('mydb2.telephones')
                ->where('telephones.flag', $flag)
                ->whereRaw('telephones.employee_id = employees.id');
        });
    }
    

    在控制器

    现在我可以使用这种优雅的语法a'la Eloquent

    $employees = Employee::with('flaggedTelephones')->hasFlaggedTelephones()->get();
    

    读起来像“抓住所有带有标记电话的员工急切加载,然后只接受至少有一个标记电话的员工”

    修改

    在处理Laravel框架一段时间后(当前版本使用5.2.39),我想,事实上,whereHas()子句在关系模型存在于使用{的不同数据库的情况下工作{1}}方法,如下所示:

    from()

    @Rob Contreras声明使用$employees = Employee::whereHas('telephones', function($query){ $query->from('mydb2.telephones')->where('flag', 1); })->get(); 方法,但看起来该方法需要将数据库和表作为参数。

答案 1 :(得分:1)

不确定这是否有效,但您可以使用from方法在闭包中指定数据库连接:

$employees = Employee::whereHas('telephones', function($query){

    $query->from('mydb2')->where('flag', 1);

})->get();

希望这有帮助

答案 2 :(得分:1)

肮脏的解决方案:

使用whereExists和范围以提高可读性。

在您的Employee模型中:

public function scopeFlags($query, $flag)
{
    $query->whereExists(function ($q) use ($flag) {
        $q->select(\DB::raw(1))
            ->from('mydb2.telephones')
            ->where('telephones.flag', $flag)
            ->whereRaw('telephones.employee_id = employees.id');
    });
}

然后像这样修改您的查询:

$employees = Employee::flags(1)->get();