CakePHP 3.0 -> 查找条件之间

2023-11-27

是否可以执行“BETWEEN?AND?” cakephp 2.5 中的条件类似于 cakephp 2.5? 在 cakephp 2.5 中我写了类似的东西

'conditions' => ['start_date BETWEEN ? AND ?' => ['2014-01-01', '2014-12-32']]

我该如何迁移它?

另外我会写一些类似的东西

'conditions' => [ '? BETWEEN start_date AND end_date'] => '2014-03-31']

表达式

Between 表达式是开箱即用的,但是它们只支持第一种情况,无需额外的修改:

$Query = $Table
    ->find()
    ->where(function($exp) {
        return $exp->between('start_date', '2014-01-01', '2014-12-32', 'date');
    });

如果您想通过 Between 方法处理第二种情况,那么您必须将所有值作为表达式传递,这很容易出错,因为在这种情况下它们不会受到转义/参数绑定的影响,您' d 必须自己执行此操作(这是绝对不推荐的!请参阅中的安全说明的手册PDO::quote()),大致如下:

use Cake\Database\Expression\IdentifierExpression;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\Query;

// ...

$Query = $Table
    ->find()
    ->where(function(QueryExpression $exp, Query $query) {
        return $exp->between(
            $query->newExpr(
                $query->connection()->driver()->quote(
                    '2014-03-31',
                    \PDO::PARAM_STR
                )
            ),
            new IdentifierExpression('start_date'),
            new IdentifierExpression('end_date')
        );
    });

对于 CakePHP 附带的所有 SQL 方言都支持的基本 SQL 表达式来说,这可能会感觉有点不方便,因此您可能有理由使用带有值 Bindig 的原始 SQL 片段。

但应该注意的是,当涉及到跨方言支持时,表达式通常是更好的选择,因为它们可以(或多或少)在编译时轻松转换,请参阅实现SqlDialectTrait::_expressionTranslators()。此外,表达式通常支持自动标识符引用。

值绑定

通过手动值绑定,您几乎可以创建任何您喜欢的东西。但应该注意的是,只要有可能,您就应该使用表达式,因为它们更容易移植,对于很多表达式来说,这种情况已经是开箱即用的。

$Query = $Table
    ->find()
    ->where([
        'start_date BETWEEN :start AND :end'
    ])
    ->bind(':start', '2014-01-01', 'date')
    ->bind(':end',   '2014-12-31', 'date');

这样第二种情况也可以很容易地解决,例如:

$Query = $Table
    ->find()
    ->where([
        ':date BETWEEN start_date AND end_date'
    ])
    ->bind(':date', '2014-03-31', 'date');

两者的混合(最安全且最兼容的方法)

也可以混合两者,即使用一个利用自定义绑定的表达式,大致如下:

use Cake\Database\Expression\IdentifierExpression;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\Query;

// ...

$Query = $Table
    ->find()
    ->where(function(QueryExpression $exp, Query $query) {
        return $exp->between(
            $query->newExpr(':date'),
            new IdentifierExpression('start_date'),
            new IdentifierExpression('end_date')
        );
    })
    ->bind(':date', '2014-03-31', 'date');

这样您就可以使用可能可移植的表达式来处理第二种情况,而不必担心手动引用/转义输入数据和标识符。

使用数组语法进行常规比较

说了这么多,最后BETWEEN与使用两个单独的简单条件相同,如下所示:

$Query = $Table
    ->find()
    ->where([
        'start_date >=' => '2014-01-01',
        'start_date <=' => '2014-12-32',
    ]);
$Query = $Table
    ->find()
    ->where([
        'start_date >=' => '2014-03-31',
        'end_date <=' => '2014-03-31',
    ]);

但不要生气,如果您一直读到这里,至少您了解了查询生成器的来龙去脉。

See also

  • Cookbook > 数据库访问和 ORM > 查询生成器 > 高级条件
  • API > \Cake\Database\Query::bind()

Currently there seems to be only two options. The core now supports this out of the box, the following is just kept for reference.

值绑定(通过数据库查询生成器)

现在 ORM 查询构建器(Cake\ORM\Query),例如调用时正在检索的那个find()在表对象上,不支持值绑定

https://github.com/cakephp/cakephp/issues/4926

因此,为了能够使用绑定,您必须使用底层数据库查询构建器(Cake\Database\Query),例如可以通过以下方式检索Connection::newQuery().

这是一个例子:

$conn = ConnectionManager::get('default');

$Query = $conn->newQuery(); 
$Query
    ->select('*')
    ->from('table_name')
    ->where([
        'start_date BETWEEN :start AND :end'
    ])
    ->bind(':start', new \DateTime('2014-01-01'), 'date')
    ->bind(':end',   new \DateTime('2014-12-31'), 'date');

debug($Query->execute()->fetchAll());

这将导致类似于此的查询

SELECT 
    *
FROM
    table_name
WHERE
    start_date BETWEEN '2014-01-01' AND '2014-12-31'

自定义表达式类

另一种选择是定制表达生成适当的 SQL 片段的类。这是一个例子。

列名应包装到标识符表达式对象中,以便自动引用(如果启用了自动引用),key > value 数组语法用于绑定值,其中数组键是实际值,数组值是数据类型。

请注意,直接传递用户输入的列名并不安全,因为它们不会被转义!使用白名单或类似的内容来确保列名称可以安全使用!

值之间的字段

use App\Database\Expression\BetweenComparison;
use Cake\Database\Expression\IdentifierExpression;

// ...

$between = new BetweenComparison(
    new IdentifierExpression('created'),
    ['2014-01-01' => 'date'],
    ['2014-12-31' => 'date']
);

$TableName = TableRegistry::get('TableName');
$Query = $TableName
    ->find()
    ->where($between);

debug($Query->execute()->fetchAll());

这将生成类似于上面的查询。

字段之间的值

use App\Database\Expression\BetweenComparison;
use Cake\Database\Expression\IdentifierExpression;

// ...

$between = new BetweenComparison(
    ['2014-03-31' => 'date'],
    new IdentifierExpression('start_date'),
    new IdentifierExpression('end_date')
);

$TableName = TableRegistry::get('TableName');
$Query = $TableName
    ->find()
    ->where($between);

debug($Query->execute()->fetchAll()); 

另一方面,这将导致类似于此的查询

SELECT 
    *
FROM
    table_name
WHERE
    '2014-03-31' BETWEEN start_date AND end_date

表达式类

namespace App\Database\Expression;

use Cake\Database\ExpressionInterface;
use Cake\Database\ValueBinder;

class BetweenComparison implements ExpressionInterface {

    protected $_field;
    protected $_valueA;
    protected $_valueB;

    public function __construct($field, $valueA, $valueB) {
        $this->_field = $field;
        $this->_valueA = $valueA;
        $this->_valueB = $valueB;
    }

    public function sql(ValueBinder $generator) {
        $field  = $this->_compilePart($this->_field,  $generator);
        $valueA = $this->_compilePart($this->_valueA, $generator);
        $valueB = $this->_compilePart($this->_valueB, $generator);

        return sprintf('%s BETWEEN %s AND %s', $field, $valueA, $valueB);
    }

    public function traverse(callable $callable) {
        $this->_traversePart($this->_field,  $callable);
        $this->_traversePart($this->_valueA, $callable);
        $this->_traversePart($this->_valueB, $callable);
    }

    protected function _bindValue($value, $generator, $type) {
        $placeholder = $generator->placeholder('c');
        $generator->bind($placeholder, $value, $type);
        return $placeholder;
    }

    protected function _compilePart($value, $generator) {
        if ($value instanceof ExpressionInterface) {
            return $value->sql($generator);
        } else if(is_array($value)) {
            return $this->_bindValue(key($value), $generator, current($value));
        }
        return $value;
    }

    protected function _traversePart($value, callable $callable) {
        if ($value instanceof ExpressionInterface) {
            $callable($value);
            $value->traverse($callable);
        }
    }

}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CakePHP 3.0 -> 查找条件之间 的相关文章