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

Querying Relationship Existence using multiple MySQL database connections in Laravel 5.2

我正在处理以下情况:我有两个模型,一个 Employee 带有 idname 字段,一个 Telephone 带有 id , employee_idflag 字段。这两个模型之间还存在一对多关系,即一个员工可能有多个电话,一个电话可能属于一个员工。

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 模型引用了名为 mydb1 的数据库模式中存在的 table employees,而 Telephone 模型与 telephones table 存在于名为 mydb2.

的不同数据库模式中

我想要的是仅获取具有至少一部电话特定标志的员工eager loaded,使用 Eloquent 并且(如果可能)不使用查询构建器

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

1) 在Controller中使用whereHas方法

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

我在这里尝试做的是首先仅检索拥有标志为 1 的电话的员工,其次仅急切加载这些电话,但我得到以下 查询异常 因为使用了不同的数据库连接:

Base table or view not found: Table mydb1.telephones doesn't exist (这是真的,电话存在于mydb2)

2) 控制器中有约束的预加载

$employees = Employee::with([

    'telephones' => function ($query) {

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

])->get();

这种方法急切地加载了 flag=1 的电话,但它 returns all 员工实例,这不是我真正想要的。我想收集 只有电话 flag = 1 的员工模型,不包括 telephones = []

的模型

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

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

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

})->get();

希望对您有所帮助

肮脏的解决方案:

使用 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();

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

  1. 创建一个方法,在模型
  2. 中returns一个关系对象
  3. eager load控制器中的这个新关系
  4. 使用模型
  5. 中的查询范围过滤掉标志!= 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();

读起来像 "Fetch all the employees with flagged telephones eager loaded, and then take only the employees that have at least one flagged telephone"

编辑:

在使用 Laravel 框架一段时间后(当前版本使用 5.2.39),我想,事实上, whereHas() 子句在关系模型存在的情况下确实有效使用 from() 方法的不同数据库,如下所示:

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

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

})->get();

@Rob Contreras 声明了 from() 方法的使用,但看起来该方法需要将数据库和 table 作为参数。