149 lines
4.8 KiB
PHP
149 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace Ulmus\Query;
|
|
|
|
use Ulmus\Common\EntityField,
|
|
Ulmus\Common\Sql;
|
|
|
|
use Ulmus\QueryBuilder\QueryBuilderInterface;
|
|
|
|
class Having extends Fragment {
|
|
const SQL_TOKEN = "HAVING";
|
|
|
|
public int $order = 80;
|
|
|
|
public array $conditionList;
|
|
|
|
public QueryBuilderInterface $queryBuilder;
|
|
|
|
public ? Having $parent = null;
|
|
|
|
public string $condition = Where::CONDITION_AND;
|
|
|
|
public function __construct(? QueryBuilderInterface $queryBuilder, $condition = Where::CONDITION_AND)
|
|
{
|
|
$this->queryBuilder = $queryBuilder;
|
|
$this->condition = $condition;
|
|
$this->parent = $queryBuilder->having ?? null;
|
|
}
|
|
|
|
public function add($field, $value, string $operator, string $condition, bool $not = false) : self
|
|
{
|
|
$this->validateFieldType($field);
|
|
# $this->validateValueType($value);
|
|
|
|
$this->conditionList[] = [
|
|
$field,
|
|
$value,
|
|
$operator ?: $this->queryBuilder->conditionOperator,
|
|
$condition,
|
|
$not
|
|
];
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function render(bool $skipToken = false) : string
|
|
{
|
|
$stack = [];
|
|
|
|
foreach ($this->conditionList ?? [] as $key => $item) {
|
|
if ( $item instanceof static ) {
|
|
if ( $item->conditionList ?? false ) {
|
|
$stack[] = ( $key !== 0 ? "{$item->condition} " : "" ) . "(" . $item->render($skipToken) . ")";
|
|
}
|
|
}
|
|
else {
|
|
list($field, $value, $operator, $condition, $not) = $item;
|
|
$stack[] = $latest = $this->havingCondition($field, $value, $operator, $key !== 0 ? $condition : "", $not);
|
|
}
|
|
}
|
|
|
|
return $this->renderSegments([
|
|
! $this->parent && ! $skipToken ? static::SQL_TOKEN : "",
|
|
implode(" ", $stack)
|
|
]);
|
|
}
|
|
|
|
protected function havingCondition($field, $value, string $operator = Where::OPERATOR_EQUAL, string $condition = Where::CONDITION_AND, bool $not = false) {
|
|
return new class($this->queryBuilder, $field, $value, $operator, $condition, $not) {
|
|
|
|
public $value;
|
|
public bool $not = false;
|
|
public string $field;
|
|
public string $operator;
|
|
public string $condition;
|
|
public QueryBuilderInterface $queryBuilder;
|
|
|
|
protected string $content = "";
|
|
|
|
public function __construct(QueryBuilderInterface $queryBuilder, string $field, $value, string $operator, string $condition, bool $not) {
|
|
$this->queryBuilder = $queryBuilder;
|
|
$this->field = $field;
|
|
$this->value = $value;
|
|
$this->condition = $condition;
|
|
$this->operator = $operator;
|
|
$this->not = $not;
|
|
}
|
|
|
|
public function render() : string
|
|
{
|
|
$value = $this->value();
|
|
|
|
return $this->content ?: $this->content = implode(" ", array_filter([
|
|
$this->condition,
|
|
$this->not ? Where::CONDITION_NOT : "",
|
|
$this->field,
|
|
$this->operator(),
|
|
$value,
|
|
]));
|
|
}
|
|
|
|
protected function operator() : string
|
|
{
|
|
if ( is_array($this->value) ) {
|
|
return (in_array($this->operator, [ '!=', '<>' ]) ? Where::CONDITION_NOT . " " : "") . Where::COMPARISON_IN;
|
|
}
|
|
|
|
# whitelisting operators
|
|
return in_array(strtoupper($this->operator), [ '=', '!=', '>', '>=', '<', '<=', '<>', 'LIKE', 'IS', 'IS NOT', 'REGEXP', 'IN' ]) ? $this->operator : "=";
|
|
}
|
|
|
|
protected function value()
|
|
{
|
|
if ( is_array($this->value) ) {
|
|
$stack = [];
|
|
|
|
foreach($this->value as $item) {
|
|
$stack[] = $this->filterValue($item);
|
|
}
|
|
|
|
return "(" . implode(", ", $stack) . ")";
|
|
}
|
|
|
|
return $this->filterValue($this->value);
|
|
}
|
|
|
|
protected function filterValue($value)
|
|
{
|
|
if ( $value === null ) {
|
|
$this->operator = in_array($this->operator, [ '!=', '<>' ]) ? Where::COMPARISON_IS . " " . Where::CONDITION_NOT : Where::COMPARISON_IS;
|
|
|
|
return Where::COMPARISON_NULL;
|
|
}
|
|
elseif ( is_object($value) && ( $value instanceof EntityField ) ) {
|
|
return $value->name();
|
|
}
|
|
else {
|
|
return $this->queryBuilder->addParameter($value);
|
|
}
|
|
}
|
|
|
|
public function __toString() : string
|
|
{
|
|
return $this->render();
|
|
}
|
|
};
|
|
}
|
|
}
|