未验证 提交 c2a88028 编写于 作者: weixin_47267244's avatar weixin_47267244 提交者: GitHub

增强 whereBrackets,支持查询条件收集器 (#580)

* 增强 whereBrackets,支持查询条件收集器

* 更新文档

* 更新文档

* 优化代码,完善测试用例

* 修复测试
上级 3e765a63
......@@ -18,6 +18,12 @@ v2.0 是一个非常成功的 LTS 版本,进行了底层重构,增加了强
## 新功能
### v2.1.53
**发布日期:** `2023-09-01`
* 增强 `whereBrackets`,支持查询条件收集器 ([#580](https://github.com/imiphp/imi/pull/580)) ([文档](https://doc.imiphp.com/v2.1/components/db/index.html#whereBrackets))
### v2.1.52
**发布日期:** `2023-08-18`
......
......@@ -453,26 +453,37 @@ Db::query()->orWhereRaw('id >= :value', [':value' => 1]);
#### whereBrackets
```php
// 查询条件收集器:where (age < 14 or age > 60)
Db::query()->where('id', '=', 1)->whereBrackets(function(\Imi\Db\Query\Interfaces\IQuery $query, \Imi\Db\Query\Interfaces\IWhereCollector $where) {
// 注意:使用第 2 个参数 $where,而不是 $query
$where->where('age', '<', 14)->orWhere('age', '>', 60);
// 不要有返回值
}, 'or');
// where id = 1 or (age < 14)
Db::query()->where('id', '=', 1)->whereBrackets(function(\Imi\Db\Query\Interfaces\IQuery $query) {
// 直接返回字符串
// 返回条件字符串
return 'age < 14';
}, 'or');
// 支持使用 sql 语句: where id = 1 or (age > 10 and age < 14)
Db::query()->where('id', '=', 1)->whereBrackets(function(\Imi\Db\Query\Interfaces\IQuery $query) {
// 直接返回字符串
// 返回 Where 系列数组
return [
\Imi\Db\Query\Where\Where::raw('age > 10'),
new \Imi\Db\Query\Where\Where('age', '<', 14),
];
}, 'or');
// where id = 1 or (age < 14)
Db::query()->where('id', '=', 1)->whereBrackets(function(\Imi\Db\Query\Interfaces\IQuery $query) {
// 直接返回字符串
// 返回 Where 系列对象
return new \Imi\Db\Query\Where\Where('age', '<', 14);
}, 'or');
// OR 条件
Db::query()->where('id', '=', 1)->orWhereBrackets(function(\Imi\Db\Query\Interfaces\IQuery $query) {
// 直接返回字符串
// 返回 Where 系列对象
return new \Imi\Db\Query\Where\Where('age', '<', 14);
});
```
......
......@@ -26,7 +26,7 @@ class QueryCurdTest extends QueryCurdBaseTest
*
* @var string
*/
protected $expectedTestWhereExSql = 'select * from "tb_article" where ("id" = :p1 and ("id" in (:p2) ) )';
protected $expectedTestWhereExSql = 'select * from "tb_article" where ("id" = :p1 and ("id" in (:p2)))';
/**
* 测试 JSON 查询的 SQL.
......
......@@ -26,7 +26,7 @@ class QueryCurdTest extends QueryCurdBaseTest
*
* @var string
*/
protected $expectedTestWhereExSql = 'select * from "tb_article" where ("id" = :p1 and ("id" in (:p2) ) )';
protected $expectedTestWhereExSql = 'select * from "tb_article" where ("id" = :p1 and ("id" in (:p2)))';
/**
* 测试 JSON 查询的 SQL.
......
......@@ -23,7 +23,7 @@ class QueryCurdTest extends QueryCurdBaseTest
*
* @var string
*/
protected $expectedTestWhereExSql = 'select * from `tb_article` where (`id` = :p1 and (`id` in (:p2) ) )';
protected $expectedTestWhereExSql = 'select * from `tb_article` where (`id` = :p1 and (`id` in (:p2)))';
/**
* 测试 JSON 查询的 SQL.
......
<?php
declare(strict_types=1);
namespace Imi\Db\Query\Interfaces;
interface IBaseWhereCollector
{
/**
* 设置 where 条件,一般用于 =、>、<、like等.
*
* @param mixed $value
*
* @return static
*/
public function where(string $fieldName, string $operation, $value, string $logicalOperator = 'and'): self;
/**
* 设置 where 条件,用原生语句.
*
* @return static
*/
public function whereRaw(string $raw, string $logicalOperator = 'and', array $binds = []): self;
/**
* 设置 where 条件,传入回调,回调中的条件加括号.
*
* @return static
*/
public function whereBrackets(callable $callback, string $logicalOperator = 'and'): self;
/**
* 设置 where 条件,使用 IBaseWhere 结构.
*
* @return static
*/
public function whereStruct(IBaseWhere $where, string $logicalOperator = 'and'): self;
/**
* 设置 where 条件,支持语法如下:.
*
* [
* 'id' => 1,
* 'or' => [
* 'id' => 2,
* ],
* 'title' => ['like', '%test%'],
* 'age' => ['>', 18],
* 'age' => ['between', 19, 29]
* ]
*
* SQL: id = 1 or (id = 2) and title like '%test%' and age > 18 and age between 19 and 29
*
* @return static
*/
public function whereEx(array $condition, string $logicalOperator = 'and'): self;
/**
* where between $begin end $end.
*
* @param mixed $begin
* @param mixed $end
*
* @return static
*/
public function whereBetween(string $fieldName, $begin, $end, string $logicalOperator = 'and'): self;
/**
* or where between $begin end $end.
*
* @param mixed $begin
* @param mixed $end
*
* @return static
*/
public function orWhereBetween(string $fieldName, $begin, $end): self;
/**
* where not between $begin end $end.
*
* @param mixed $begin
* @param mixed $end
*
* @return static
*/
public function whereNotBetween(string $fieldName, $begin, $end, string $logicalOperator = 'and'): self;
/**
* or where not between $begin end $end.
*
* @param mixed $begin
* @param mixed $end
*
* @return static
*/
public function orWhereNotBetween(string $fieldName, $begin, $end): self;
/**
* 设置 where or 条件.
*
* @param mixed $value
*
* @return static
*/
public function orWhere(string $fieldName, string $operation, $value): self;
/**
* 设置 where or 条件,用原生语句.
*
* @return static
*/
public function orWhereRaw(string $where, array $binds = []): self;
/**
* 设置 where or 条件,传入回调,回调中的条件加括号.
*
* @return static
*/
public function orWhereBrackets(callable $callback): self;
/**
* 设置 where or 条件,使用 IBaseWhere 结构.
*
* @return static
*/
public function orWhereStruct(IBaseWhere $where): self;
/**
* 设置 where or 条件,支持语法参考 whereEx 方法.
*
* @return static
*/
public function orWhereEx(array $condition): self;
/**
* where field in (list).
*
* @return static
*/
public function whereIn(string $fieldName, array $list, string $logicalOperator = 'and'): self;
/**
* or where field in (list).
*
* @return static
*/
public function orWhereIn(string $fieldName, array $list): self;
/**
* where field not in (list).
*
* @return static
*/
public function whereNotIn(string $fieldName, array $list, string $logicalOperator = 'and'): self;
/**
* or where field not in (list).
*
* @return static
*/
public function orWhereNotIn(string $fieldName, array $list): self;
/**
* where field is null.
*
* @return static
*/
public function whereIsNull(string $fieldName, string $logicalOperator = 'and'): self;
/**
* or where field is null.
*
* @return static
*/
public function orWhereIsNull(string $fieldName): self;
/**
* where field is not null.
*
* @return static
*/
public function whereIsNotNull(string $fieldName, string $logicalOperator = 'and'): self;
/**
* or where field is not null.
*
* @return static
*/
public function orWhereIsNotNull(string $fieldName): self;
}
......@@ -13,7 +13,7 @@ use Imi\Db\Query\Result\CursorResult;
/**
* 查询器接口.
*/
interface IQuery
interface IQuery extends IBaseWhereCollector
{
/**
* 获取所有操作的记录.
......@@ -112,188 +112,6 @@ interface IQuery
*/
public function fieldRaw(string $raw, ?string $alias = null, array $binds = []): self;
/**
* 设置 where 条件,一般用于 =、>、<、like等.
*
* @param mixed $value
*
* @return static
*/
public function where(string $fieldName, string $operation, $value, string $logicalOperator = 'and'): self;
/**
* 设置 where 条件,用原生语句.
*
* @return static
*/
public function whereRaw(string $raw, string $logicalOperator = 'and', array $binds = []): self;
/**
* 设置 where 条件,传入回调,回调中的条件加括号.
*
* @return static
*/
public function whereBrackets(callable $callback, string $logicalOperator = 'and'): self;
/**
* 设置 where 条件,使用 IBaseWhere 结构.
*
* @return static
*/
public function whereStruct(IBaseWhere $where, string $logicalOperator = 'and'): self;
/**
* 设置 where 条件,支持语法如下:.
*
* [
* 'id' => 1,
* 'or' => [
* 'id' => 2,
* ],
* 'title' => ['like', '%test%'],
* 'age' => ['>', 18],
* 'age' => ['between', 19, 29]
* ]
*
* SQL: id = 1 or (id = 2) and title like '%test%' and age > 18 and age between 19 and 29
*
* @return static
*/
public function whereEx(array $condition, string $logicalOperator = 'and'): self;
/**
* where between $begin end $end.
*
* @param mixed $begin
* @param mixed $end
*
* @return static
*/
public function whereBetween(string $fieldName, $begin, $end, string $logicalOperator = 'and'): self;
/**
* or where between $begin end $end.
*
* @param mixed $begin
* @param mixed $end
*
* @return static
*/
public function orWhereBetween(string $fieldName, $begin, $end): self;
/**
* where not between $begin end $end.
*
* @param mixed $begin
* @param mixed $end
*
* @return static
*/
public function whereNotBetween(string $fieldName, $begin, $end, string $logicalOperator = 'and'): self;
/**
* or where not between $begin end $end.
*
* @param mixed $begin
* @param mixed $end
*
* @return static
*/
public function orWhereNotBetween(string $fieldName, $begin, $end): self;
/**
* 设置 where or 条件.
*
* @param mixed $value
*
* @return static
*/
public function orWhere(string $fieldName, string $operation, $value): self;
/**
* 设置 where or 条件,用原生语句.
*
* @return static
*/
public function orWhereRaw(string $where, array $binds = []): self;
/**
* 设置 where or 条件,传入回调,回调中的条件加括号.
*
* @return static
*/
public function orWhereBrackets(callable $callback): self;
/**
* 设置 where or 条件,使用 IBaseWhere 结构.
*
* @return static
*/
public function orWhereStruct(IBaseWhere $where): self;
/**
* 设置 where or 条件,支持语法参考 whereEx 方法.
*
* @return static
*/
public function orWhereEx(array $condition): self;
/**
* where field in (list).
*
* @return static
*/
public function whereIn(string $fieldName, array $list, string $logicalOperator = 'and'): self;
/**
* or where field in (list).
*
* @return static
*/
public function orWhereIn(string $fieldName, array $list): self;
/**
* where field not in (list).
*
* @return static
*/
public function whereNotIn(string $fieldName, array $list, string $logicalOperator = 'and'): self;
/**
* or where field not in (list).
*
* @return static
*/
public function orWhereNotIn(string $fieldName, array $list): self;
/**
* where field is null.
*
* @return static
*/
public function whereIsNull(string $fieldName, string $logicalOperator = 'and'): self;
/**
* or where field is null.
*
* @return static
*/
public function orWhereIsNull(string $fieldName): self;
/**
* where field is not null.
*
* @return static
*/
public function whereIsNotNull(string $fieldName, string $logicalOperator = 'and'): self;
/**
* or where field is not null.
*
* @return static
*/
public function orWhereIsNotNull(string $fieldName): self;
/**
* join.
*
......
<?php
declare(strict_types=1);
namespace Imi\Db\Query\Interfaces;
interface IWhereCollector extends IBaseWhereCollector
{
/**
* @return IBaseWhere[]
*/
public function getWhere(): array;
}
......@@ -19,6 +19,7 @@ use Imi\Db\Query\Interfaces\IResult;
use Imi\Db\Query\Result\ChunkByOffsetResult;
use Imi\Db\Query\Result\ChunkResult;
use Imi\Db\Query\Result\CursorResult;
use Imi\Db\Query\Traits\TWhereCollector;
use Imi\Db\Query\Where\Where;
use Imi\Db\Query\Where\WhereBrackets;
use Imi\Db\Query\Where\WhereFullText;
......@@ -27,6 +28,8 @@ use Imi\Util\Pagination;
abstract class Query implements IQuery
{
use TWhereCollector;
public const SELECT_BUILDER_CLASS = '';
public const INSERT_BUILDER_CLASS = '';
......@@ -369,184 +372,6 @@ abstract class Query implements IQuery
return $this;
}
/**
* {@inheritDoc}
*/
public function whereEx(array $condition, string $logicalOperator = LogicalOperator::AND): self
{
if (!$condition)
{
return $this;
}
return $this->whereBrackets(fn () => $this->parseWhereEx($condition), $logicalOperator);
}
protected function parseWhereEx(array $condition): array
{
$result = [];
foreach ($condition as $key => $value)
{
if (null === LogicalOperator::getText(strtolower($key)))
{
// 条件 k => v
if (\is_array($value))
{
$operator = strtolower($value[0] ?? '');
switch ($operator)
{
case 'between':
if (!isset($value[2]))
{
throw new \RuntimeException('Between must have 3 params');
}
$result[] = new Where($key, 'between', [$value[1], $value[2]]);
break;
case 'not between':
if (!isset($value[2]))
{
throw new \RuntimeException('Not between must have 3 params');
}
$result[] = new Where($key, 'not between', [$value[1], $value[2]]);
break;
case 'in':
if (!isset($value[1]))
{
throw new \RuntimeException('In must have 3 params');
}
$result[] = new Where($key, 'in', $value[1]);
break;
case 'not in':
if (!isset($value[1]))
{
throw new \RuntimeException('Not in must have 3 params');
}
$result[] = new Where($key, 'not in', $value[1]);
break;
default:
$result[] = new Where($key, $operator, $value[1]);
break;
}
}
else
{
$result[] = new Where($key, '=', $value);
}
}
else
{
// 逻辑运算符
$result[] = new WhereBrackets(fn () => $this->parseWhereEx($value), $key);
}
}
return $result;
}
/**
* {@inheritDoc}
*/
public function whereBetween(string $fieldName, $begin, $end, string $logicalOperator = LogicalOperator::AND): self
{
return $this->where($fieldName, 'between', [$begin, $end], $logicalOperator);
}
/**
* {@inheritDoc}
*/
public function orWhereBetween(string $fieldName, $begin, $end): self
{
return $this->where($fieldName, 'between', [$begin, $end], LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function whereNotBetween(string $fieldName, $begin, $end, string $logicalOperator = LogicalOperator::AND): self
{
return $this->where($fieldName, 'not between', [$begin, $end], $logicalOperator);
}
/**
* {@inheritDoc}
*/
public function orWhereNotBetween(string $fieldName, $begin, $end): self
{
return $this->where($fieldName, 'not between', [$begin, $end], LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function orWhere(string $fieldName, string $operation, $value): self
{
return $this->where($fieldName, $operation, $value, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function orWhereRaw(string $where, array $binds = []): self
{
return $this->whereRaw($where, LogicalOperator::OR, $binds);
}
/**
* {@inheritDoc}
*/
public function orWhereBrackets(callable $callback): self
{
return $this->whereBrackets($callback, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function orWhereStruct(IBaseWhere $where): self
{
return $this->whereStruct($where, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function orWhereEx(array $condition): self
{
return $this->whereEx($condition, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function whereIn(string $fieldName, array $list, string $logicalOperator = LogicalOperator::AND): self
{
return $this->where($fieldName, 'in', $list, $logicalOperator);
}
/**
* {@inheritDoc}
*/
public function orWhereIn(string $fieldName, array $list): self
{
return $this->where($fieldName, 'in', $list, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function whereNotIn(string $fieldName, array $list, string $logicalOperator = LogicalOperator::AND): self
{
return $this->where($fieldName, 'not in', $list, $logicalOperator);
}
/**
* {@inheritDoc}
*/
public function orWhereNotIn(string $fieldName, array $list): self
{
return $this->where($fieldName, 'not in', $list, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
......@@ -555,14 +380,6 @@ abstract class Query implements IQuery
return $this->whereRaw($this->fieldQuote($fieldName) . ' is null', $logicalOperator);
}
/**
* {@inheritDoc}
*/
public function orWhereIsNull(string $fieldName): self
{
return $this->whereIsNull($fieldName, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
......@@ -571,14 +388,6 @@ abstract class Query implements IQuery
return $this->whereRaw($this->fieldQuote($fieldName) . ' is not null', $logicalOperator);
}
/**
* {@inheritDoc}
*/
public function orWhereIsNotNull(string $fieldName): self
{
return $this->whereIsNotNull($fieldName, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
......
<?php
declare(strict_types=1);
namespace Imi\Db\Query\Traits;
use Imi\Db\Mysql\Consts\LogicalOperator;
use Imi\Db\Query\Interfaces\IBaseWhere;
use Imi\Db\Query\Where\Where;
use Imi\Db\Query\Where\WhereBrackets;
trait TWhereCollector
{
/**
* {@inheritDoc}
*/
public function whereEx(array $condition, string $logicalOperator = LogicalOperator::AND): self
{
if (!$condition)
{
return $this;
}
return $this->whereBrackets(fn () => $this->parseWhereEx($condition), $logicalOperator);
}
/**
* {@inheritDoc}
*/
public function whereBetween(string $fieldName, $begin, $end, string $logicalOperator = LogicalOperator::AND): self
{
return $this->where($fieldName, 'between', [$begin, $end], $logicalOperator);
}
/**
* {@inheritDoc}
*/
public function orWhereBetween(string $fieldName, $begin, $end): self
{
return $this->where($fieldName, 'between', [$begin, $end], LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function whereNotBetween(string $fieldName, $begin, $end, string $logicalOperator = LogicalOperator::AND): self
{
return $this->where($fieldName, 'not between', [$begin, $end], $logicalOperator);
}
/**
* {@inheritDoc}
*/
public function orWhereNotBetween(string $fieldName, $begin, $end): self
{
return $this->where($fieldName, 'not between', [$begin, $end], LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function orWhere(string $fieldName, string $operation, $value): self
{
return $this->where($fieldName, $operation, $value, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function orWhereRaw(string $where, array $binds = []): self
{
return $this->whereRaw($where, LogicalOperator::OR, $binds);
}
/**
* {@inheritDoc}
*/
public function orWhereBrackets(callable $callback): self
{
return $this->whereBrackets($callback, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function orWhereStruct(IBaseWhere $where): self
{
return $this->whereStruct($where, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function orWhereEx(array $condition): self
{
return $this->whereEx($condition, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function whereIn(string $fieldName, array $list, string $logicalOperator = LogicalOperator::AND): self
{
return $this->where($fieldName, 'in', $list, $logicalOperator);
}
/**
* {@inheritDoc}
*/
public function orWhereIn(string $fieldName, array $list): self
{
return $this->where($fieldName, 'in', $list, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function whereNotIn(string $fieldName, array $list, string $logicalOperator = LogicalOperator::AND): self
{
return $this->where($fieldName, 'not in', $list, $logicalOperator);
}
/**
* {@inheritDoc}
*/
public function orWhereNotIn(string $fieldName, array $list): self
{
return $this->where($fieldName, 'not in', $list, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function orWhereIsNull(string $fieldName): self
{
return $this->whereIsNull($fieldName, LogicalOperator::OR);
}
/**
* {@inheritDoc}
*/
public function orWhereIsNotNull(string $fieldName): self
{
return $this->whereIsNotNull($fieldName, LogicalOperator::OR);
}
protected function parseWhereEx(array $condition): array
{
$result = [];
foreach ($condition as $key => $value)
{
if (null === LogicalOperator::getText(strtolower($key)))
{
// 条件 k => v
if (\is_array($value))
{
$operator = strtolower($value[0] ?? '');
switch ($operator)
{
case 'between':
if (!isset($value[2]))
{
throw new \RuntimeException('Between must have 3 params');
}
$result[] = new Where($key, 'between', [$value[1], $value[2]]);
break;
case 'not between':
if (!isset($value[2]))
{
throw new \RuntimeException('Not between must have 3 params');
}
$result[] = new Where($key, 'not between', [$value[1], $value[2]]);
break;
case 'in':
if (!isset($value[1]))
{
throw new \RuntimeException('In must have 3 params');
}
$result[] = new Where($key, 'in', $value[1]);
break;
case 'not in':
if (!isset($value[1]))
{
throw new \RuntimeException('Not in must have 3 params');
}
$result[] = new Where($key, 'not in', $value[1]);
break;
default:
$result[] = new Where($key, $operator, $value[1]);
break;
}
}
else
{
$result[] = new Where($key, '=', $value);
}
}
else
{
// 逻辑运算符
$result[] = new WhereBrackets(fn () => $this->parseWhereEx($value), $key);
}
}
return $result;
}
}
......@@ -9,6 +9,7 @@ use Imi\Db\Query\Interfaces\IBaseWhere;
use Imi\Db\Query\Interfaces\IQuery;
use Imi\Db\Query\Interfaces\IWhereBrackets;
use Imi\Db\Query\Traits\TRaw;
use Imi\Db\Query\WhereCollector;
class WhereBrackets extends BaseWhere implements IWhereBrackets
{
......@@ -53,7 +54,8 @@ class WhereBrackets extends BaseWhere implements IWhereBrackets
return $this->rawSQL;
}
$binds = &$this->binds;
$callResult = ($this->callback)($query);
$whereCollector = new WhereCollector($query);
$callResult = ($this->callback)($query, $whereCollector);
if (\is_array($callResult))
{
if (empty($callResult))
......@@ -68,17 +70,21 @@ class WhereBrackets extends BaseWhere implements IWhereBrackets
{
if (0 === $i)
{
$result .= $callResultItem->toStringWithoutLogic($query) . ' ';
$result .= $callResultItem->toStringWithoutLogic($query);
}
else
{
$result .= $callResultItem->getLogicalOperator() . ' ' . $callResultItem->toStringWithoutLogic($query) . ' ';
$result .= ' ' . $callResultItem->getLogicalOperator() . ' ' . $callResultItem->toStringWithoutLogic($query);
}
$binds = array_merge($binds, $callResultItem->getBinds());
}
else
{
$result .= $callResultItem . ' ';
if ($i > 0)
{
$result .= ' ';
}
$result .= $callResultItem;
}
}
......@@ -91,6 +97,24 @@ class WhereBrackets extends BaseWhere implements IWhereBrackets
return '(' . $result . ')';
}
elseif (null === $callResult)
{
$result = '(';
foreach ($whereCollector->getWhere() as $i => $where)
{
if (0 === $i)
{
$result .= $where->toStringWithoutLogic($query);
}
else
{
$result .= ' ' . $where->getLogicalOperator() . ' ' . $where->toStringWithoutLogic($query);
}
$binds = array_merge($binds, $where->getBinds());
}
return $result . ')';
}
else
{
return '(' . $callResult . ')';
......
<?php
declare(strict_types=1);
namespace Imi\Db\Query;
use Imi\Db\Mysql\Consts\LogicalOperator;
use Imi\Db\Query\Interfaces\IBaseWhere;
use Imi\Db\Query\Interfaces\IQuery;
use Imi\Db\Query\Interfaces\IWhereCollector;
use Imi\Db\Query\Traits\TWhereCollector;
use Imi\Db\Query\Where\Where;
use Imi\Db\Query\Where\WhereBrackets;
class WhereCollector implements IWhereCollector
{
use TWhereCollector;
/**
* @var IBaseWhere[]
*/
protected array $where = [];
protected IQuery $query;
public function __construct(IQuery $query)
{
$this->query = $query;
}
/**
* @return IBaseWhere[]
*/
public function getWhere(): array
{
return $this->where;
}
/**
* {@inheritDoc}
*/
public function where(string $fieldName, string $operation, $value, string $logicalOperator = LogicalOperator::AND): self
{
$this->where[] = new Where($fieldName, $operation, $value, $logicalOperator);
return $this;
}
/**
* {@inheritDoc}
*/
public function whereRaw(string $raw, string $logicalOperator = LogicalOperator::AND, array $binds = []): self
{
$where = new Where();
$where->useRaw();
$where->setRawSQL($raw, $binds);
$where->setLogicalOperator($logicalOperator);
$this->where[] = $where;
return $this;
}
/**
* {@inheritDoc}
*/
public function whereBrackets(callable $callback, string $logicalOperator = LogicalOperator::AND): self
{
$this->where[] = new WhereBrackets($callback, $logicalOperator);
return $this;
}
/**
* {@inheritDoc}
*/
public function whereStruct(IBaseWhere $where, string $logicalOperator = LogicalOperator::AND): self
{
$this->where[] = $where;
return $this;
}
/**
* {@inheritDoc}
*/
public function whereIsNull(string $fieldName, string $logicalOperator = LogicalOperator::AND): self
{
return $this->whereRaw($this->query->fieldQuote($fieldName) . ' is null', $logicalOperator);
}
/**
* {@inheritDoc}
*/
public function whereIsNotNull(string $fieldName, string $logicalOperator = LogicalOperator::AND): self
{
return $this->whereRaw($this->query->fieldQuote($fieldName) . ' is not null', $logicalOperator);
}
}
......@@ -23,7 +23,7 @@ class QueryCurdTest extends QueryCurdBaseTest
*
* @var string
*/
protected $expectedTestWhereExSql = 'select * from `tb_article` where (`id` = :p1 and (`id` in (:p2) ) )';
protected $expectedTestWhereExSql = 'select * from `tb_article` where (`id` = :p1 and (`id` in (:p2)))';
/**
* 测试 JSON 查询的 SQL.
......
......@@ -23,7 +23,7 @@ class QueryCurdTest extends QueryCurdBaseTest
*
* @var string
*/
protected $expectedTestWhereExSql = 'select * from `tb_article` where (`id` = :p1 and (`id` in (:p2) ) )';
protected $expectedTestWhereExSql = 'select * from `tb_article` where (`id` = :p1 and (`id` in (:p2)))';
/**
* 测试 JSON 查询的 SQL.
......
......@@ -5,11 +5,14 @@ declare(strict_types=1);
namespace Imi\Test\Component\Tests\Db;
use Imi\Db\Db;
use Imi\Db\Mysql\Consts\LogicalOperator;
use Imi\Db\Mysql\Query\FullText\MysqlFullTextOptions;
use Imi\Db\Mysql\Query\FullText\SearchModifier;
use Imi\Db\Mysql\Query\Lock\MysqlLock;
use Imi\Db\Mysql\Query\Pagination\BigTablePagination;
use Imi\Db\Query\Database;
use Imi\Db\Query\Interfaces\IQuery;
use Imi\Db\Query\Interfaces\IWhereCollector;
use Imi\Db\Query\Raw;
use Imi\Db\Query\Where\Where;
use Imi\Test\BaseTest;
......@@ -431,6 +434,32 @@ abstract class QueryCurdBaseTest extends BaseTest
Assert::assertNull($record);
}
public function testWhere(): void
{
$query = Db::query($this->poolName);
$sql = $query->from($this->tableArticle)->where('where', '=', 1)
->orWhere('orWhere', '=', 2)
->whereBetween('whereBetween', 1, 2)
->orWhereBetween('orWhereBetween', 1, 2)
->whereNotBetween('whereNotBetween', 1, 2)
->orWhereNotBetween('orWhereNotBetween', 1, 2)
->whereIn('whereIn', [1, 2])
->orWhereIn('orWhereIn', [1, 2])
->whereNotIn('whereNotIn', [1, 2])
->orWhereNotIn('orWhereNotIn', [1, 2])
->whereIsNull('whereIsNull')
->orWhereIsNull('orWhereIsNull')
->whereIsNotNull('whereIsNotNull')
->orWhereIsNotNull('orWhereIsNotNull')
->whereRaw('whereRaw = 1')
->orWhereRaw('orWhereRaw = 2')
->whereStruct(new Where('whereStruct', '=', 1))
->buildSelectSql();
Assert::assertEquals(<<<SQL
select * from `{$this->fullTableArticle}` where `where` = :p1 or `orWhere` = :p2 and `whereBetween` between :p3 and :p4 or `orWhereBetween` between :p5 and :p6 and `whereNotBetween` not between :p7 and :p8 or `orWhereNotBetween` not between :p9 and :pa and `whereIn` in (:pb,:pc) or `orWhereIn` in (:pd,:pe) and `whereNotIn` not in (:pf,:p10) or `orWhereNotIn` not in (:p11,:p12) and `whereIsNull` is null or `orWhereIsNull` is null and `whereIsNotNull` is not null or `orWhereIsNotNull` is not null and whereRaw = 1 or orWhereRaw = 2 and `whereStruct` = :p13
SQL, $sql);
}
/**
* @depends testInsert
*/
......@@ -459,6 +488,129 @@ abstract class QueryCurdBaseTest extends BaseTest
Assert::assertEquals('select * from `' . $this->fullTableArticle . '`', Db::query($this->poolName)->from($this->tableArticle)->whereEx([])->select()->getSql());
}
/**
* @depends testInsert
*/
public function testWhereBrackets(array $args): void
{
['ids' => $ids] = $args;
$id = $ids[1];
// 字符串
$query = Db::query($this->poolName);
$result = $query->from($this->tableArticle)->whereBrackets(static fn () => 'id=' . $id)->select();
$record = $result->get();
Assert::assertEquals([
'id' => $id,
'title' => 'title-insert',
'content' => 'content-insert',
'time' => '2019-06-21 00:00:00',
'member_id' => 0,
], $record);
Assert::assertEquals('select * from `tb_article` where (id=2)', $result->getSql());
// Where 数组
$query = Db::query($this->poolName);
$result = $query->from($this->tableArticle)->whereBrackets(static fn () => [
new Where('id', '=', $id),
(static function () {
$where = new Where();
$where->setRawSQL('1=2');
$where->setLogicalOperator(LogicalOperator::OR);
$where->useRaw();
return $where;
})(),
])->select();
$record = $result->get();
Assert::assertEquals([
'id' => $id,
'title' => 'title-insert',
'content' => 'content-insert',
'time' => '2019-06-21 00:00:00',
'member_id' => 0,
], $record);
Assert::assertEquals('select * from `tb_article` where (`id` = :p1 or 1=2)', $result->getSql());
$query = Db::query($this->poolName);
$result = $query->from($this->tableArticle)->whereBrackets(static fn () => [
new Where('id', '=', $id),
'or 1=2',
])->select();
$record = $result->get();
Assert::assertEquals([
'id' => $id,
'title' => 'title-insert',
'content' => 'content-insert',
'time' => '2019-06-21 00:00:00',
'member_id' => 0,
], $record);
Assert::assertEquals('select * from `tb_article` where (`id` = :p1 or 1=2)', $result->getSql());
// Where 对象
$query = Db::query($this->poolName);
$result = $query->from($this->tableArticle)->whereBrackets(static fn () => new Where('id', '=', $id))->select();
$record = $result->get();
Assert::assertEquals([
'id' => $id,
'title' => 'title-insert',
'content' => 'content-insert',
'time' => '2019-06-21 00:00:00',
'member_id' => 0,
], $record);
Assert::assertEquals('select * from `tb_article` where (`id` = :p1)', $result->getSql());
// Where 收集器
$query = Db::query($this->poolName);
$result = $query->from($this->tableArticle)->whereBrackets(static function (IQuery $query, IWhereCollector $where) use ($id) {
$where->where('id', '=', $id)->orWhereRaw('1=2');
})->select();
$record = $result->get();
Assert::assertEquals([
'id' => $id,
'title' => 'title-insert',
'content' => 'content-insert',
'time' => '2019-06-21 00:00:00',
'member_id' => 0,
], $record);
Assert::assertEquals('select * from `tb_article` where (`id` = :p1 or 1=2)', $result->getSql());
$query = Db::query($this->poolName);
$sql = $query->from($this->tableArticle)->whereBrackets(static function (IQuery $query, IWhereCollector $where) {
$where->where('where', '=', 1)
->orWhere('orWhere', '=', 2)
->whereBetween('whereBetween', 1, 2)
->orWhereBetween('orWhereBetween', 1, 2)
->whereNotBetween('whereNotBetween', 1, 2)
->orWhereNotBetween('orWhereNotBetween', 1, 2)
->whereIn('whereIn', [1, 2])
->orWhereIn('orWhereIn', [1, 2])
->whereNotIn('whereNotIn', [1, 2])
->orWhereNotIn('orWhereNotIn', [1, 2])
->whereIsNull('whereIsNull')
->orWhereIsNull('orWhereIsNull')
->whereIsNotNull('whereIsNotNull')
->orWhereIsNotNull('orWhereIsNotNull')
->whereRaw('whereRaw = 1')
->orWhereRaw('orWhereRaw = 2')
->whereStruct(new Where('whereStruct', '=', 1))
->whereEx([
'whereEx' => 1,
'and' => [
'whereEx' => ['in', [1]],
],
])
->orWhereEx([
'orWhereEx' => 1,
'or' => [
'orWhereEx' => ['in', [1]],
],
]);
})->buildSelectSql();
Assert::assertEquals(<<<SQL
select * from `{$this->fullTableArticle}` where (`where` = :p1 or `orWhere` = :p2 and `whereBetween` between :p3 and :p4 or `orWhereBetween` between :p5 and :p6 and `whereNotBetween` not between :p7 and :p8 or `orWhereNotBetween` not between :p9 and :pa and `whereIn` in (:pb,:pc) or `orWhereIn` in (:pd,:pe) and `whereNotIn` not in (:pf,:p10) or `orWhereNotIn` not in (:p11,:p12) and `whereIsNull` is null or `orWhereIsNull` is null and `whereIsNotNull` is not null or `orWhereIsNotNull` is not null and whereRaw = 1 or orWhereRaw = 2 and `whereStruct` = :p13 and (`whereEx` = :p14 and (`whereEx` in (:p15))) or (`orWhereEx` = :p16 or (`orWhereEx` in (:p17))))
SQL, $sql);
}
/**
* @depends testInsert
*/
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册