ZF3 - Zend\Db\Sql\Predicate\Expression - 在 SQL DATE_FORMAT 上转义错误

ZF3 - Zend\Db\Sql\Predicate\Expression - Wrong escaping on SQL DATE_FORMAT

我正在尝试根据提交的会话开始时间过滤 radius 记帐数据库。 "acctstartime" 字段在 MySQL 数据库中定义为 DATETIME。

这是我要构建的工作 SQL 查询:

SELECT * FROM radacct WHERE DATE_FORMAT(acctstarttime, '%Y-%m-%d') = DATE_FORMAT('2019-08-10' , '%Y-%m-%d') ORDER BY "radacctid" DESC LIMIT 100

在我的 ZF3-Controller 中,我正在使用 Zend\Db 抽象层基于提交的参数构建 SQL- 查询。但不幸的是,Db 层转义了 %-Signs 并产生了一个无法正常工作的 SQL-string:

SELECT * FROM radacct WHERE DATE_FORMAT(acctstarttime, '%%Y-%%m-%%d') = DATE_FORMAT('2019-01-01' , '%%Y-%%m-%%d') AND "acctstarttime" = '2019-01-01' ORDER BY "radacctid" DESC LIMIT 100

在文件 zend-db/src/Sql/Expression.php 的第 136 行:

$expression = str_replace('%', '%%', $this->expression);

有人知道如何生成上面的查询吗?或者也许另一种方法可以做同样的事情?我还没有找到使用 DATE_TIME.

的此类表达式的任何解决方法

控制器:

class AccountingController extends AbstractRestfulJsonController {
    protected $service;

    public function __construct(RadiusService $service) {
        $this->service = $service;
    }

    public function get($id) {
        return $this->getList();
    }

    public function getList() {
        $viewModel = new JsonModel();

        try {
            $filter = [];
            $paramsRoute = $this->params()->fromRoute();
            $paramsQuery = $this->params()->fromQuery();
            $params = (OBJECT) array_merge($paramsRoute, $paramsQuery);

            if(isset($params->id)) {
                $filter['radacctid'] = (int) $params->id;
            }

            // ...

            if(isset($params->sessionStartTime) && strlen($params->sessionStartTime) > 0) {
                $filter[] = new Expression("DATE_FORMAT(acctstarttime, '%Y-%m-%d') = DATE_FORMAT('" . $params->sessionStartTime . "' , '%Y-%m-%d')");
            }

            // ...

            $data = $this->service->getAccounting($filter);

            $viewModel->setVariable('state', 'ok');
            $viewModel->setVariable('count', $data['totalItems']);
            $viewModel->setVariable('data', $data['items']);
        } catch (\Exception $e) {
            $viewModel->setVariable('state', 'nok');
            $viewModel->setVariable('message', $e->getMessage());
        }

        return $viewModel;
    }
}

那么"SUBSTRING(acctstarttime, 1, 10) = '"呢? substr($params->sessionStartTime, 0, 10) 。 “'”?

另一种方式,也许 "cleaner" 正在编写您自己的 class 并覆盖 getExpressionData 方法以不对值进行转义。例如:

    use Zend\Db\Sql\Predicate\Expression;

    public class NonEscapedExpression extends Expression
    {
        /**
         * @return array
         * @throws Exception\RuntimeException
        */
        public function getExpressionData()
        {
            $parameters = (is_scalar($this->parameters)) ? [$this->parameters] : $this->parameters;
            $parametersCount = count($parameters);

            if ($parametersCount == 0) {
                return [
                    str_ireplace(self::PLACEHOLDER, '', $expression)
                ];
            }
            // assign locally, escaping % signs
            $expression = str_replace(self::PLACEHOLDER, '%s', $expression, $count);
            // test number of replacements without considering same variable begin used many times first, which is
            // faster, if the test fails then resort to regex which are slow and used rarely
            if ($count !== $parametersCount && $parametersCount === preg_match_all('/\:[a-zA-Z0-9_]*/', $expression)) {
                throw new Exception\RuntimeException(
                    'The number of replacements in the expression does not match the number of parameters'
                );
            }
            foreach ($parameters as $parameter) {
                list($values[], $types[]) = $this->normalizeArgument($parameter, self::TYPE_VALUE);
            }
            return [[
                $expression,
                $values,
                $types
            ]];
        }
    }

但请谨慎使用。