tp5实现跨数据库关联查询

因某些需求,需跨库查询,一开始以为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 连接。当没指定表别名时,默认以模型类名作为表别名。

你可能感兴趣的:(tp5实现跨数据库关联查询)