因某些需求,需跨库查询,一开始以为tp5关联模型支持跨库查询,结果是不支持。
官方的解决方案是使用 join 连接,但实现效果不是很好,经过一些尝试后,终于可以通过 关联 的方式解决跨库查询问题。
代码示例
控制器
application/index/controller/Demo.php
namespace app\index\controller;
use app\common\model\res\Res;
use app\common\model\user\UserDownlist;
class Demo
{
/**
* 跨库关联查询
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function relation()
{
$model = new UserDownlist();
$model
->useRelationQuery(new Res(), null, 'UserDownlist.ll_id=Res.ll_id')
->field('UserDownlist.*,Res.ll_id')
->where('Res.renderer_version', 1)
->where('UserDownlist.user_id', 20582258)
->select();
die($model->getLastSql());
}
}
模型
application/common/model/user/UserDownlist.php
namespace app\common\model\user;
use app\common\model\res\Res;
use app\common\traits\model\RelationHelpers;
class UserDownlist extends BaseModel
{
use RelationHelpers;
}
模型辅助类
application/common/traits/model/RelationHelpers.php
namespace app\common\traits\model;
use think\db\Query;
use think\Model;
trait RelationHelpers
{
/**
* 使用关联查询(别名,Model对象调用)
* @param mixed $relation 子模型对象
* @param null $alias 子模型别名(单个关联时有效)
* @param null $condition 关联条件
* @param string $joinType JOIN类型
* @return Query
*/
public function useRelationQuery(
$relation,
$alias = null,
$condition = null,
$joinType = 'INNER'
) {
return $this->handldRelationQuery($this, $relation, $alias, $condition, $joinType);
}
/**
* 使用关联查询(别名,Query对象调用)
* @param Query $parent 父Query对象
* @param null $relation 子模型对象
* @param null $alias 子模型别名(单个关联时有效)
* @param null $condition 关联条件
* @param string $joinType JOIN类型
* @return Query
*/
public function scopeUseRelationQuery(
Query $parent,
$relation,
$alias = null,
$condition = null,
$joinType = 'INNER'
) {
return $this->handldRelationQuery($parent->getModel(), $relation, $alias, $condition, $joinType);
}
/**
* 使用关联查询(支持跨库查询)
* @param Model $parent 父模型对象
* @param mixed $relation 子模型对象
* @param null $relationAlias 子模型别名(单个关联时有效)
* @param null $condition 关联条件
* @param string $joinType JOIN类型
* @return Query
*
* 参数说明
* parent
* 父模型对象
* relation
* 关联的子模型对象。多个模型关联时,可传入一个数组
* 数组格式:[Model relation, string|null relationAlias, mixed condition, string joinType]
* relationAlias
* 子模型别名。单个关联时有效,如果参数值为 null 时,默认以模型类名作为别名
* condition
* 关联条件。可以为字符串或数组, 为数组时每一个元素都是一个关联条件
* joinType
* 关联类型。可以为:INNER、LEFT、RIGHT、FULL,不区分大小写,默认为INNER
*
* Example
* 单个关联:(new TestA())->useRelationQuery(new TestB(), null, 'TestA.id=Test.id');
* 多个关联:(new TestA())->useRelationQuery([[new TestB(), null, 'TestA.id=Test.id']]);
*/
public function handldRelationQuery(
Model $parent,
$relation,
$relationAlias = null,
$condition = null,
$joinType = 'INNER'
) {
list($parentTable, $parentTableAlias) = $this->getJoinTable($parent);
$aliass = $parent->getOptions('alias');
$parentTableAlias = empty($alias = $aliass[$parent->getTable()]) ? $parentTableAlias : $alias;
$this->setTable($parentTable)->alias($parentTableAlias);
if (empty($condition)) {
// 如果为组数,则循环调用join
foreach ($relation as $_join) {
list($model, $alias, $condition, $joinType) = array_pad($_join, 4, null);
list($childTable, $tableAlias) = $this->getJoinTable($model);
$childTableAlias = $alias ?: $tableAlias;
$this->join(
[$childTable => $childTableAlias],
$condition,
$joinType ?: 'INNER'
);
}
} else {
list($childTable, $tableAlias) = $this->getJoinTable($relation);
$childTableAlias = $relationAlias ?: $tableAlias;
$this->join(
[$childTable => $childTableAlias],
$condition,
$joinType
);
}
return $this;
}
/**
* 获取JOIN表名及别名
* @param Model $query
* @return array
*/
protected function getJoinTable(Model $query)
{
$table = $query->getConfig('database') . '.' . $query->getTable();
$tableAlias = basename(str_replace('\\', '/', get_class($query)));
return [$table, $tableAlias];
}
}
生成的SQL语句
SELECT `UserDownlist`.*,`Res`.`ll_id` FROM `za_user`.`za_user_downlist` `UserDownlist` INNER JOIN `za_res`.`za_res` `Res` ON `UserDownlist`.`ll_id`=`Res`.`ll_id` WHERE `Res`.`renderer_version` = 1 AND `UserDownlist`.`user_id` = 20582258;
实现原理很简单,通过传入对应的模型对象,自动加上库名,构造 join 连接。当没指定表别名时,默认以模型类名作为表别名。