aogg
11/9/2018 - 9:41 AM

swoft的model相关类库强化

1、model增加whereRange方法
2、增加localUnionAutoField方法,自定义的union
3、记得修改.env和config/properties/app.php文件的配置

<?php
/**
 * Created by PhpStorm.
 * User: code
 * Date: 2018/10/31
 * Time: 下午7:13
 */

namespace App\Helper\Vendor;

use Swoft\Db\DbDataResult;

/**
 * 系统QueryBuilder类的重写
 *
 * Class Qb
 * @package App\Helper\Vendor
 */
class Qb extends \Swoft\Db\Qb
{
    /**
     * 是否是自定义的union
     *
     * @var bool
     */
    public $localUnionBool = false;

    /**
     * 是否是自定义的union,并且自动通过外层的field补充内层的union
     *
     * @var bool
     */
    public $localUnionAutoFieldBool = false;

    /**
     * union的数据
     *
     * @var array
     */
    protected $unions = [];

    /**
     * 是否在自定义select字段中
     * select是私有方法
     *
     * @var bool
     */
    protected $setSelectBool = false;

    /**
     * 区间where,可以边界值为空
     *
     * @param $field
     * @param $min
     * @param $max
     * @param string $eq
     * @return $this
     */
    public function whereRange($field, $min, $max, $eq = '=')
    {
        if (!empty($min) && !empty($max) && $min > $max){ // 所有
            return $this;
        }

        if (!empty($min)) {
            $this->where($field, $min, '>' . $eq);
        }
        if (!empty($max)) {
            $this->where($field, $max, '<' . $eq);
        }

        return $this;
    }

    /**
     * 自定义的union
     * 并自动通过外层的field补充union的field
     *
     * @param callable $func
     * @return $this
     */
    public function localUnionAutoField($func)
    {
        if (!is_callable($func)) {
            return $this;
        }

        $this->localUnionBool = true;
        $this->localUnionAutoFieldBool = true;
        $this->unions[] = [
            'func' => $func,
            'defaultArg' => [new static()],
        ];

        return $this;
    }

    public function localUnionWhereToAll($whereFuncName, ...$args)
    {
        foreach ($this->unions as $union) {
            if (isset($union['defaultArg'][0])) {
                /** @var static $qb */
                $qb = $union['defaultArg'][0];
                $qb->{$whereFuncName}(...$args);
            }
        }
    }

    /**
     * @return array
     */
    public function getUnions(): array
    {
        return $this->unions;
    }

    /**
     * @param array $select
     * @return $this
     */
    public function setSelect(array $select)
    {
        $this->setSelectBool = true;
        $this->get($select);
        $this->setSelectBool = false;

        return $this;
    }

    public function execute()
    {
        if ($this->setSelectBool){ // 设置get中
            return new DbDataResult([]); // 必须返回Swoft\Core\ResultInterface对象
        }

        return parent::execute();
    }

    protected function addGetDecorator()
    {
        if ($this->setSelectBool){
            return false;
        }

        parent::addGetDecorator();
    }


}
<?php
/**
 * Created by PhpStorm.
 * User: code
 * Date: 2018/10/31
 * Time: 下午7:14
 */

namespace App\Helper\Vendor;


use Swoft\Db\QueryBuilder;

class Query extends \Swoft\Db\Query
{
    /**
     * @param string $tableName
     * @param string $alias
     * @inheritdoc
     *
     * @return Qb|QueryBuilder
     */
    public static function table(string $tableName, string $alias = null): QueryBuilder
    {
        $query = new Qb();
        $query = $query->table($tableName, $alias);

        return $query;
    }
}
<?php
/**
 * Created by PhpStorm.
 * User: code
 * Date: 2018/11/9
 * Time: 下午3:30
 */

namespace App\Helper\Vendor;


use Swoft\Db\Driver\Mysql\MysqlConnection;
use Swoft\Db\Bean\Annotation\Connection;

/**
 * Mysql connection
 *
 * @Connection(driver="SysMysql")
 */
class SysMysqlConnection extends MysqlConnection
{

}
<?php
/**
 * Created by PhpStorm.
 * User: code
 * Date: 2018/11/9
 * Time: 下午1:37
 */

namespace App\Helper\Vendor;


use Swoft\Db\Driver\Mysql\MysqlStatement;
use Swoft\Db\Bean\Annotation\Statement;
use Swoft\Db\Exception\MysqlException;
use Swoft\Db\QueryBuilder;

/**
 * SysMysqlStatement
 *
 * @Statement(driver="SysMysql")
 */
class SysMysqlStatement extends MysqlStatement
{
    /**
     * @var Qb
     */
    protected $builder;

    /**
     * @var QueryBuilder;
     */
    protected $parentBuilder;

    public function getStatement(): string
    {
        if ($this->builder instanceof Qb && $this->builder->localUnionBool && $this->builder->getUnions()){
            return $this->getLocalUnionSelectStatement();
        }

        return parent::getStatement();
    }

    /**
     * select语句
     *
     * @inheritdoc
     * @return string
     */
    protected function getLocalUnionSelectStatement(): string
    {
        $statement = '';
        if (!$this->isSelect() && !$this->isAggregate()) {
            return $statement;
        }

        // union语句
        $statement .= $this->getUnionString();

        // where语句
        if ($this->builder->getWhere()) {
            $statement .= ' ' . $this->getWhereString();
        }

        // having语句
        if ($this->builder->getHaving()) {
            $statement .= ' ' . $this->getHavingString();
        }

        // orderBy语句
        if ($this->builder->getOrderBy()) {
            $statement .= ' ' . $this->getOrderByString();
        }

        // limit语句
        if ($this->builder->getLimit()) {
            $statement .= ' ' . $this->getLimitString();
        }

        // 这里看sql
//        var_dump($statement);

        return $statement;
    }

    /**
     * 获取union字符串
     *
     * @inheritdoc
     * @return array|string
     */
    protected function getUnionString()
    {
        $statement = [];
        $unions = $this->builder->getUnions();
        if (empty($unions)) {
            return $statement;
        }


        foreach ($unions as $union) {
            /** @var QueryBuilder $tempQb */
            $tempQb = isset($union['func']) && is_callable($union['func']) ? $union['func'](...($union['defaultArg'] ?? [])) : false;
            if ($tempQb !== false) {
                if (!$tempQb instanceof QueryBuilder){
                    throw new MysqlException('必须返回' . QueryBuilder::class . '对象或者false');
                }

                $statement[] = '(' . (new static($tempQb))->setParentBuilder($this->builder)->getPartQueryString() . ')';
            }
        }


        $parentField = $this->getUnionParentFieldString();
        return "SELECT {$parentField} FROM(" . join(' union ', $statement) . ') as t';
    }

    /**
     * 获取union内部的sql
     *
     * @inheritdoc
     * @return string
     */
    public function getPartQueryString()
    {
        $statement = '';

        // select语句
        $statement .= $this->getUnionSelectString();

        // from语句
        if ($this->builder->getFrom()) {
            $statement .= ' ' . $this->getFromString();
        }

        // where语句
        if ($this->builder->getWhere()) {
            $statement .= ' ' . $this->getWhereString();
        }

        // groupBy语句
        if ($this->builder->getGroupBy()) {
            $statement .= ' ' . $this->getGroupByString();
        }

        // having语句
//        if ($this->builder->getHaving()) {
//            $statement .= ' ' . $this->getHavingString();
//        }
//
//        // orderBy语句
//        if ($this->builder->getOrderBy()) {
//            $statement .= ' ' . $this->getOrderByString();
//        }
//
//        // limit语句
//        if ($this->builder->getLimit()) {
//            $statement .= ' ' . $this->getLimitString();
//        }

        return $statement;
    }

    /**
     * 获取自定义union内部的select部分sql
     *
     * @return string
     * @throws MysqlException
     */
    protected function getUnionSelectString()
    {
        $statement = '';
        if (!isset($this->parentBuilder)){
            throw new MysqlException('必须设置父builder!');
        }

        // select语句,还不支持count和字段获取一起
        if (!empty($this->parentBuilder->getAggregate())){ // count之类的方式
            //(todo 目前只兼容了外部设置field,内部未知)
            $tempField = []; // select是private
            foreach ($this->parentBuilder->getAggregate() as $alias => $item) {
                if (isset($item[0]) && !empty($item[1])) { // column
                    $tempField[] = $item[0] . " as `{$item[1]}`"; // 这里的$item[0]可能自带表别名
                }
            }

            if (!empty($tempField)) {
                $statement .= 'SELECT ' . join(', ', $tempField) . ' ';
            }
        }else{
            // getSelectString方法在字段都为空的情况下会返回空字符串
            if ($temp = $this->getSelectString()){ // 如果当前字段已指定
                $statement .= $temp;
            }else if ($temp = (new static($this->parentBuilder))->getSelectString()) { // 有父级字段
                $statement .= $temp;
            } else {
                $statement .= 'SELECT *';
            }
        }

        if (empty($statement)) {
            throw new MysqlException('sql字段未指定!');
        }

        return $statement;
    }

    /**
     * 获取union语句的父语句的字段
     *
     * @return string
     */
    protected function getUnionParentFieldString()
    {
        foreach ($this->builder->getAggregate() as $alias => $item) { // 目前只返回一个
            if (!empty($item[1])) {
                return $alias . '(`' . $item[1] . "`) as `{$item[1]}`"; // $alias用于执行方法,$item[1]用于别名且一般是一个关键字
            }
        }

        return '*';
    }

    /**
     * 方便上面调用
     *
     * @inheritdoc
     */
    public function getSelectString(): string
    {
        return parent::getSelectString();
    }

    /**
     * 保持union的参数不丢失
     *
     * @inheritdoc
     */
    protected function getQuoteValue($value): string
    {
        $keyTemp = parent::getQuoteValue($value);
        if ($this->parentBuilder){
            $this->parentBuilder->setParameter(ltrim($keyTemp, ':'), $value);
        }

        return $keyTemp;
    }


    /**
     * @param QueryBuilder $parentBuilder
     * @return SysMysqlStatement
     */
    public function setParentBuilder(QueryBuilder $parentBuilder): SysMysqlStatement
    {
        $this->parentBuilder = $parentBuilder;
        return $this;
    }


}
<?php
/**
 * Created by PhpStorm.
 * User: code
 * Date: 2018/11/24
 * Time: 上午11:08
 */

namespace App\Helper\Vendor;


use Swoft\Db\Driver\Mysql\SyncMysqlConnection;
use Swoft\Db\Bean\Annotation\Connection;
use Swoft\Db\Driver\DriverType;

/**
 * Class SysSyncMysqlConnection
 * 测试用例会用到
 *
 * @package App\Helper\Vendor
 * @Connection(type=DriverType::SYNC, driver="SysMysql")
 */
class SysSyncMysqlConnection extends SyncMysqlConnection
{

}


# 增加环境变量
DB_DRIVER=SysMysql
DB_SLAVE_DRIVER=SysMysql
<?php

/*
 * config/properties/app.php的配置文件
 * (c) Swoft <group@swoft.org>
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

// 增加目录时需要指定当前目录会被系统注解解析
return [
    'beanScan'     => [
        'App\Helper',
    ],
];