使用 Laravel 5.2 中的多个 MySQL 数据库连接查询关系是否存在
Querying Relationship Existence using multiple MySQL database connections in Laravel 5.2
我正在处理以下情况:我有两个模型,一个 Employee
带有 id
和 name
字段,一个 Telephone
带有 id
, 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
模型引用了名为 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 的回答,我终于想出了一个符合我需求的解决方案,步骤如下:
- 创建一个方法,在模型
中returns一个关系对象
- eager load控制器中的这个新关系
- 使用模型
中的查询范围过滤掉标志!= 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 作为参数。
我正在处理以下情况:我有两个模型,一个 Employee
带有 id
和 name
字段,一个 Telephone
带有 id
, 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
模型引用了名为 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 的回答,我终于想出了一个符合我需求的解决方案,步骤如下:
- 创建一个方法,在模型 中returns一个关系对象
- eager load控制器中的这个新关系
- 使用模型 中的查询范围过滤掉标志!= 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 作为参数。