Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for NOT operator #185

Merged
merged 7 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# CHANGELOG

v2.10.0 (01.04.2024)
v2.10.0 (04.04.2024)
--------------------
- Add support for the `NOT` operator in SQL queries. New methods `whereNot`, `andWhereNot`, and `orWhereNot`
have been added to the query builder by @msmakouz (#185)
- Add support **JSON** columns in **orderBy** statement by @msmakouz (#184)
- Add `mediumText` column type by @msmakouz (#178)
- Fix caching of SQL insert query with Fragment values by @msmakouz (#177)
Expand Down
6 changes: 6 additions & 0 deletions src/Driver/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,12 @@ protected function where(QueryParameters $params, Quoter $q, array $tokens): str

// first condition in group/query, no any AND, OR required
if ($activeGroup) {
// first condition can have a `NOT` keyword (WHERE NOT ...)
if (\str_contains(\strtoupper($boolean), 'NOT')) {
$statement .= 'NOT';
$statement .= ' ';
}

// next conditions require AND or OR
$activeGroup = false;
} else {
Expand Down
2 changes: 2 additions & 0 deletions src/Driver/CompilerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ interface CompilerInterface

public const TOKEN_AND = '@AND';
public const TOKEN_OR = '@OR';
public const TOKEN_AND_NOT = '@AND NOT';
public const TOKEN_OR_NOT = '@OR NOT';

/**
* @param string $identifier
Expand Down
38 changes: 29 additions & 9 deletions src/Query/Traits/TokenTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,7 @@ protected function registerToken(string $boolean, array $params, array &$tokens,
}

if (\count($complex) === 1) {
$this->flattenWhere(
$boolean === 'AND' ? CompilerInterface::TOKEN_AND : CompilerInterface::TOKEN_OR,
$complex,
$tokens,
$wrapper
);
$this->flattenWhere($this->booleanToToken($boolean), $complex, $tokens, $wrapper);
return;
}

Expand Down Expand Up @@ -161,7 +156,7 @@ protected function registerToken(string $boolean, array $params, array &$tokens,
*/
private function flattenWhere(string $grouper, array $where, array &$tokens, callable $wrapper): void
{
$boolean = ($grouper === CompilerInterface::TOKEN_AND ? 'AND' : 'OR');
$boolean = $this->tokenToBoolean($grouper);

foreach ($where as $key => $value) {
// Support for closures
Expand All @@ -175,7 +170,12 @@ private function flattenWhere(string $grouper, array $where, array &$tokens, cal
$token = strtoupper($key);

// Grouping identifier (@OR, @AND), MongoDB like style
if ($token === CompilerInterface::TOKEN_AND || $token === CompilerInterface::TOKEN_OR) {
if (
$token === CompilerInterface::TOKEN_AND ||
$token === CompilerInterface::TOKEN_OR ||
$token === CompilerInterface::TOKEN_AND_NOT ||
$token === CompilerInterface::TOKEN_OR_NOT
) {
$tokens[] = [$boolean, '('];

foreach ($value as $nested) {
Expand All @@ -184,7 +184,7 @@ private function flattenWhere(string $grouper, array $where, array &$tokens, cal
continue;
}

$tokens[] = [$token === CompilerInterface::TOKEN_AND ? 'AND' : 'OR', '('];
$tokens[] = [$this->tokenToBoolean($token), '('];
$this->flattenWhere(CompilerInterface::TOKEN_AND, $nested, $tokens, $wrapper);
$tokens[] = ['', ')'];
}
Expand Down Expand Up @@ -267,4 +267,24 @@ private function pushCondition(string $innerJoiner, string $key, array $where, &

return $tokens;
}

private function tokenToBoolean(string $token): string
{
return match ($token) {
CompilerInterface::TOKEN_AND => 'AND',
CompilerInterface::TOKEN_AND_NOT => 'AND NOT',
CompilerInterface::TOKEN_OR_NOT => 'OR NOT',
default => 'OR',
};
}

private function booleanToToken(string $boolean): string
{
return match ($boolean) {
'AND' => CompilerInterface::TOKEN_AND,
'AND NOT' => CompilerInterface::TOKEN_AND_NOT,
'OR NOT' => CompilerInterface::TOKEN_OR_NOT,
default => 'OR',
};
}
}
63 changes: 63 additions & 0 deletions src/Query/Traits/WhereTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,69 @@ public function orWhere(mixed ...$args): self
return $this;
}

/**
* Simple WHERE NOT condition with various set of arguments.
*
* @param mixed ...$args [(column, value), (column, operator, value)]
*
* @throws BuilderException
*
* @return $this|self
*/
public function whereNot(mixed ...$args): self
{
$this->registerToken(
'AND NOT',
$args,
$this->whereTokens,
$this->whereWrapper()
);

return $this;
}

/**
* Simple AND WHERE NOT condition with various set of arguments.
*
* @param mixed ...$args [(column, value), (column, operator, value)]
*
* @throws BuilderException
*
* @return $this|self
*/
public function andWhereNot(mixed ...$args): self
{
$this->registerToken(
'AND NOT',
$args,
$this->whereTokens,
$this->whereWrapper()
);

return $this;
}

/**
* Simple OR WHERE NOT condition with various set of arguments.
*
* @param mixed ...$args [(column, value), (column, operator, value)]
*
* @throws BuilderException
*
* @return $this|self
*/
public function orWhereNot(mixed ...$args): self
{
$this->registerToken(
'OR NOT',
$args,
$this->whereTokens,
$this->whereWrapper()
);

return $this;
}

/**
* Convert various amount of where function arguments into valid where token.
*
Expand Down
Loading
Loading