"; const CONDITION_AND = "AND"; const CONDITION_OR = "OR"; const CONDITION_NOT = "NOT"; const COMPARISON_IN = "IN"; const COMPARISON_IS = "IS"; const COMPARISON_NULL = "NULL"; const SQL_TOKEN = "WHERE"; public int $order = 50; public array $conditionList; public QueryBuilder $queryBuilder; public ? Where $parent = null; public string $condition = self::CONDITION_AND; public function __construct(? QueryBuilder $queryBuilder, $condition = self::CONDITION_AND) { $this->queryBuilder = $queryBuilder; $this->condition = $condition; $this->parent = $queryBuilder->where ?? null; } public function add($field, $value, string $operator, string $condition, bool $not = false) : self { $this->conditionList[] = [ $field, $value, $operator ?: $this->queryBuilder->conditionOperator, $condition, $not ]; return $this; } public function render() : string { $stack = []; foreach ($this->conditionList ?? [] as $key => $item) { if ( $item instanceof Where ) { if ( $item->conditionList ?? false ) { $stack[] = ( $key !== 0 ? "{$item->condition} " : "" ) . "(" . $item->render() . ")"; } } else { list($field, $value, $operator, $condition, $not) = $item; $stack[] = $latest = $this->whereCondition($field, $value, $operator, $key !== 0 ? $condition : "", $not); } } return $this->renderSegments([ ! $this->parent ? static::SQL_TOKEN : "", implode(" ", $stack) ]); } protected function whereCondition($field, $value, string $operator = self::OPERATOR_EQUAL, string $condition = self::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 QueryBuilder $queryBuilder; protected string $content = ""; public function __construct(QueryBuilder $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; } return $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(); } }; } }