【问题标题】:Querying Relationship Existence using multiple MySQL database connections in Laravel 5.2在 Laravel 5.2 中使用多个 MySQL 数据库连接查询关系存在
【发布时间】:2016-07-29 18:30:50
【问题描述】:

我正在处理以下情况:我有两个模型,一个带有 idname 字段的 Employee 和带有 idemployee_idflag 字段的 Telephone。这两种模型之间也存在一对多的关系,即一个员工可能有很多部电话,而一部电话可能属于一个员工。

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 的数据库架构中的表 employees,而 Telephone 模型与存在于名为 mydb2 的不同数据库架构中的 telephones 表相关。

我想要的是只获取具有特定标志热切加载至少一部电话的员工,使用 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();

我在这里尝试做的是首先仅检索具有 flag=1 的电话的员工,然后仅急切加载这些电话,但由于不同使用的数据库连接:

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

2) 控制器中带有约束的急切负载

$employees = Employee::with([

    'telephones' => function ($query) {

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

])->get();

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

【问题讨论】:

  • 只是一个建议,但也许您可以在合并employeestelephones 的数据库中创建view,然后从代码中的视图中提取。还有see discussion on linking databases in MySQL.
  • 你是否设置了受保护的 $connection = 'mydb2';手机型号?
  • @Froxz 是的,但不幸的是 whereHas() 没有考虑到它

标签: mysql laravel eloquent eager-loading relationships


【解决方案1】:

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

  1. 在模型中创建一个返回关系对象的方法
  2. 渴望加载控制器中的这种新关系
  3. 使用模型中的查询范围过滤掉 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() 子句确实可以在使用 from() 的不同数据库中存在关系模型的情况下工作方法,如下图所示:

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

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

})->get();

@Rob Contreras 对 from() 方法的使用表示赞赏,但看起来该方法需要同时将数据库和表作为参数。

【讨论】:

    【解决方案2】:

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

    $employees = Employee::whereHas('telephones', function($query){
    
        $query->from('mydb2')->where('flag', 1);
    
    })->get();
    

    希望对你有帮助

    【讨论】:

    • 感谢您的回答,但不幸的是,这并没有奏效。您建议的实际上选择了一个名为“db2”的表,该表位于默认数据库连接“db1”中:查看错误:SQLSTATE[42S02]:未找到基表或视图:1146 表“mydb1.mydb2”不存在( SQL: select * from directorates where exists (select * from mydb2 where telephones.directorate_id = directorates.id and flag = 5))。
    • 请看我最新的编辑,涉及到from方法的使用,谢谢
    【解决方案3】:

    肮脏的解决方案:

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

    【讨论】:

    • 非常感谢@Giedrius Kiršys,我最终得到了相同的查询以过滤掉标记的电话(即select * from mydb1.employees where exists (select * from mydb2.telephones where telephones.employee_id = employee.id and flag = 1))。但是,我还需要加载已标记的电话。
    猜你喜欢
    • 1970-01-01
    • 2018-11-06
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-06
    • 1970-01-01
    • 2021-03-21
    相关资源
    最近更新 更多