多年后再写sql查询条件拼接

先快速能用再说…

<?php

namespace App\Service;

class QueryToolService
{

    /**
     * sql空格分割
     */
    const SQL_SPACE = ' ';

    /**
     * 括号-左
     */
    const SQL_BRACKETS_LEFT = '(';

    /**
     * 括号-右
     */
    const SQL_BRACKETS_RIGHT = ')';

    /**
     * 双引号
     */
    const SQL_BRACKETS_QUOTATIONS = '"';

    /**
     * getInstance
     * @return QueryToolService
     */
    public static function getInstance()
    {
        return new self();
    }

    /**
     * sql where 查询条件拼装
     * 暂时支持:'=', '<', '<=', '>', '>=', '<>', 'like', 'not like', 'in', 'not in', 'between', 'not between'
     * where为空查询全部, 结构为二维数组
     * @param $where =>  [
     * ['id', '=', 2],
     * ['name', '=', '张三'],
     * ['remark', 'like', '%张三%'],
     * ['id', 'in', [1,2,3]],
     * ['age', 'between', [1,'3']],
     * ];
     * @param string $logic
     * @param bool $asOneCondition | 是否当成一个条件拼接,or拼and的时候
     * @return string|void
     */
    public function genConditionSql($where, string $logic = 'AND', $asOneCondition = false)
    {
        if (empty($where)) {
            return $this->getAllTrueSql();
        }

        $arr = [];
        foreach ($where as $key => $condition) {
            if (empty($condition)) {
                continue;
            }
            list($field, $operator, $value) = $condition;
            $operatorStr = self::SQL_SPACE . $operator . self::SQL_SPACE;
            if (in_array($operator, ['=', '<', '<=', '>', '>=', '<>', 'like', 'not like'], true)) {
                $value = $this->tranVal($value);
                $arr[] = self::SQL_BRACKETS_LEFT . $field . $operatorStr . $value . self::SQL_BRACKETS_RIGHT;
            } elseif (in_array($operator, ['in', 'not in'], true)) {
                if (empty($value)) {
                    if ($operator == 'in') {
                        $arr[] = self::SQL_BRACKETS_LEFT . $field . $operatorStr . '(NULL)' . self::SQL_BRACKETS_RIGHT;
                    } else {
                        $arr[] = $this->getAllTrueSql();
                    }
                } else {
                    $arr[] = self::SQL_BRACKETS_LEFT . $field . $operatorStr . self::SQL_BRACKETS_LEFT . $this->implodeSql(',', $value) . self::SQL_BRACKETS_RIGHT . self::SQL_BRACKETS_RIGHT;
                }
            } elseif (in_array($operator, ['between', 'not between'], true)) {
                list($start, $end) = $value;
                $start = $this->tranVal($start);
                $end = $this->tranVal($end);
                $arr[] = self::SQL_BRACKETS_LEFT . $field . $operatorStr . $start . self::SQL_SPACE . 'AND' . self::SQL_SPACE . $end . self::SQL_BRACKETS_RIGHT;
            }
        }
        $conditionSql = implode(self::SQL_SPACE . $logic . self::SQL_SPACE, $arr);
        return $asOneCondition ? self::SQL_BRACKETS_LEFT . $conditionSql . self::SQL_BRACKETS_RIGHT : $conditionSql;
    }

    /**
     * sql的implode
     * @param $separator
     * @param $arr
     * @return string
     */
    public function implodeSql($separator, $arr): string
    {
        $newArr = [];
        foreach ($arr as $k => $v) {
            $newArr[] = $this->tranVal($v);
        }
        return $newArr ? implode($separator, $newArr) : 'NULL';
    }

    /**
     * 数据类型转化
     * @param $v
     * @return string
     */
    public function tranVal($v)
    {
        return is_string($v) ? (self::SQL_BRACKETS_QUOTATIONS . $v . self::SQL_BRACKETS_QUOTATIONS) : $v;
    }

    /**
     * 快速生成 id condition
     * 空id则表示一条数据都没有
     * @param $ids
     * @return string
     */
    public function genIdSql($ids)
    {
        if (is_array($ids)) {
            if (empty($ids)) {
                return $this->getAllFalseSql();
            }
            return self::SQL_BRACKETS_LEFT . 'id in' . self::SQL_SPACE . self::SQL_BRACKETS_LEFT . $this->implodeSql(',', $ids) . self::SQL_BRACKETS_RIGHT . self::SQL_BRACKETS_RIGHT;
        } else {
            return self::SQL_BRACKETS_LEFT . 'id =' . self::SQL_SPACE . $this->tranVal($ids) . self::SQL_BRACKETS_RIGHT;
        }
    }

    /**
     * sql:1=1
     * @return string
     */
    public function getAllTrueSql(): string
    {
        return self::SQL_BRACKETS_LEFT . '1=1' . self::SQL_BRACKETS_RIGHT;
    }

    /**
     * sql:1=2
     * @return string
     */
    public function getAllFalseSql(): string
    {
        return self::SQL_BRACKETS_LEFT . '1=2' . self::SQL_BRACKETS_RIGHT;
    }

}

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 9

SQL查询抽象化的结果就是可读性下降了,另外就是有时候查询场景比较复杂,抽象化设计不满足需要,就需要在上面改造,一旦侵入业务,则抽象化设计几乎不伦不类,只能说各有优劣吧 :sweat_smile:

1个月前 评论

orm用多了看到原生就头疼 :joy:

1个月前 评论

这种不实用,很多场景还是要语义理解的,orm一目了然,这个还的想一想。多了就是灾难

1个月前 评论
陈先生

好家伙,我都好久没写原生的 SQL 了,大佬厉害!

当调用 getAllFalseSql 函数的时候应该没必要去查库了。

genConditionSql 可以再拆拆,这个函数有点臃肿了。 这个级别的 if 嵌套让人有点觉得晦涩。

genIdSql 可以朝着 early return 的方向改改。

大佬,这是我的一点拙见。。。。

1个月前 评论
oliver-l 1个月前
陈先生 (作者) 1个月前
oliver-l 1个月前
陈先生 (作者) 1个月前
fatrbaby

SQL注入攻击者默默地点了个赞

1个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!