Skip to content

Commit

Permalink
Added orWhere method (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
robinsonjohn authored Oct 5, 2024
1 parent 1e2c9e5 commit ce947e7
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 69 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `Fixed` for any bug fixes.
- `Security` in case of vulnerabilities

## [5.1.0] - 2024.10.05

### Added

- Added `orWhere` method.

## [5.0.0] - 2024.09.16

### Added
Expand Down
27 changes: 26 additions & 1 deletion docs/query-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ $query = new Query($pdo); // $pdo as a PDO instance
- [rightjoin](#rightjoin)
- [select](#select)
- [where](#where)
- [orWhere](#orwhere)
- [orderBy](#orderby)
- [orderByRand](#orderbyrand)
- [limit](#limit)
Expand Down Expand Up @@ -156,7 +157,7 @@ JSON fields which do not exist are returned with a value of `null`.

**Description:**

Adds a `WHERE` clause to the query.
Adds a `WHERE/AND WHERE` clause to the query.

If the column type is `JSON`, keys from within the JSON string can be searched with the format of `COLUMN->KEY`.
JSON fields which do not exist are treated as `null`.
Expand Down Expand Up @@ -205,6 +206,30 @@ The `VALUE_*` constants can be used for this purpose.

<hr />

### orWhere

**Description:**

Adds an `OR/AND OR` clause to the query.

See [where](#where).

**Parameters:**

- `$column` (string)
- `$operator` (string)
- `$value` (mixed)

**Returns:**

- (self)

**Throws:**

- `Bayfront\SimplePdo\Exceptions\QueryException`

<hr />

### orderBy

**Description:**
Expand Down
178 changes: 110 additions & 68 deletions src/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -267,53 +267,24 @@ private function parseConditionColumn(string $column): string
public const VALUE_TRUE = 'true';
public const VALUE_FALSE = 'false';

private const CONDITION_AND = 'AND';
private const CONDITION_OR = 'OR';

/**
* Adds a WHERE clause to the query.
*
* If the column type is JSON, keys from within the JSON string can be searched with the format of COLUMN->KEY.
* JSON fields which do not exist are treated as null.
*
* Available operators are:
*
* - eq (equals)
* - !eq (does not equal)
* - lt (less than)
* - gt (greater than)
* - le (less than or equal to)
* - ge (greater than or equal to)
* - sw (starts with)
* - !sw (does not start with)
* - ew (ends with)
* - !ew (does not end with)
* - has (has)
* - !has (does not have)
* - in (in)
* - !in (not in)
* - null (is or is not null)
*
* The OPERATOR_* constants can be used for this purpose.
*
* The in and !in operators accept multiple comma-separated values.
*
* The "null" operator accepts two values: true and false for is null or is not null.
* The VALUE_* constants can be used for this purpose.
*
* NOTE: Some native MySQL functions can be used as the $value, however, they will be
* injected into the query as strings, so they can be vulnerable to SQL injection.
*
* @param string $condition (and/or)
* @param string $column
* @param string $operator
* @param mixed $value
* @return self
* @param $value
* @return void
* @throws QueryException
*/
public function where(string $column, string $operator, mixed $value): self
private function addCondition(string $condition, string $column, string $operator, $value): void
{

if (!isset($this->query[self::QUERY_WHERE])) {
$this->query[self::QUERY_WHERE] = ' WHERE ';
$condition = ' WHERE (';
} else {
$this->query[self::QUERY_WHERE] .= ' AND ';
$condition = ' ' . $condition . ' (';
}

if (!in_array($operator, [
Expand Down Expand Up @@ -356,78 +327,80 @@ public function where(string $column, string $operator, mixed $value): self

// Check operators

$placeholders = [];

switch ($operator) {

case self::OPERATOR_STARTS_WITH:

if ($this->is_function($value)) {
$this->query[self::QUERY_WHERE] .= $column . ' LIKE ' . $value;
$condition .= $column . ' LIKE ' . $value;
break;
}

$this->placeholders[] = $value . '%';
$this->query[self::QUERY_WHERE] .= $column . ' LIKE ?';
$placeholders[] = $value . '%';
$condition .= $column . ' LIKE ?';
break;

case self::OPERATOR_DOES_NOT_START_WITH:

if ($this->is_function($value)) {
$this->query[self::QUERY_WHERE] .= $column . ' NOT LIKE ' . $value;
$condition .= $column . ' NOT LIKE ' . $value;
break;
}

$this->placeholders[] = $value . '%';
$this->query[self::QUERY_WHERE] .= $column . ' NOT LIKE ?';
$placeholders[] = $value . '%';
$condition .= $column . ' NOT LIKE ?';
break;

case self::OPERATOR_ENDS_WITH:

if ($this->is_function($value)) {
$this->query[self::QUERY_WHERE] .= $column . ' LIKE ' . $value;
$condition .= $column . ' LIKE ' . $value;
break;
}

$this->placeholders[] = '%' . $value;
$this->query[self::QUERY_WHERE] .= $column . ' LIKE ?';
$placeholders[] = '%' . $value;
$condition .= $column . ' LIKE ?';
break;

case self::OPERATOR_DOES_NOT_END_WITH:

if ($this->is_function($value)) {
$this->query[self::QUERY_WHERE] .= $column . ' NOT LIKE ' . $value;
$condition .= $column . ' NOT LIKE ' . $value;
break;
}

$this->placeholders[] = '%' . $value;
$this->query[self::QUERY_WHERE] .= $column . ' NOT LIKE ?';
$placeholders[] = '%' . $value;
$condition .= $column . ' NOT LIKE ?';
break;

case self::OPERATOR_HAS:

if ($this->is_function($value)) {
$this->query[self::QUERY_WHERE] .= $column . ' LIKE ' . $value;
$condition .= $column . ' LIKE ' . $value;
break;
}

$this->placeholders[] = '%' . $value . '%';
$this->query[self::QUERY_WHERE] .= $column . ' LIKE ?';
$placeholders[] = '%' . $value . '%';
$condition .= $column . ' LIKE ?';
break;

case self::OPERATOR_DOES_NOT_HAVE:

if ($this->is_function($value)) {
$this->query[self::QUERY_WHERE] .= $column . ' NOT LIKE ' . $value;
$condition .= $column . ' NOT LIKE ' . $value;
break;
}

$this->placeholders[] = '%' . $value . '%';
$this->query[self::QUERY_WHERE] .= $column . ' NOT LIKE ?';
$placeholders[] = '%' . $value . '%';
$condition .= $column . ' NOT LIKE ?';
break;

case self::OPERATOR_IN:

if ($this->is_function($value)) {
$this->query[self::QUERY_WHERE] .= $column . ' IN (' . $value . ')';
$condition .= $column . ' IN (' . $value . ')';
break;
}

Expand All @@ -437,18 +410,18 @@ public function where(string $column, string $operator, mixed $value): self

foreach ($in_values as $val) {

$this->placeholders[] = $val;
$placeholders[] = $val;

}

$this->query[self::QUERY_WHERE] .= $column . ' IN (' . $in . ')';
$condition .= $column . ' IN (' . $in . ')';

break;

case self::OPERATOR_NOT_IN:

if ($this->is_function($value)) {
$this->query[self::QUERY_WHERE] .= $column . ' NOT IN (' . $value . ')';
$condition .= $column . ' NOT IN (' . $value . ')';
break;
}

Expand All @@ -458,23 +431,23 @@ public function where(string $column, string $operator, mixed $value): self

foreach ($in_values as $val) {

$this->placeholders[] = $val;
$placeholders[] = $val;

}

$this->query[self::QUERY_WHERE] .= $column . ' NOT IN (' . $in . ')';
$condition .= $column . ' NOT IN (' . $in . ')';

break;

case self::OPERATOR_NULL:

if ($value == self::VALUE_TRUE) {

$this->query[self::QUERY_WHERE] .= $column . ' IS NULL';
$condition .= $column . ' IS NULL';

} else if ($value == self::VALUE_FALSE) {

$this->query[self::QUERY_WHERE] .= $column . ' IS NOT NULL';
$condition .= $column . ' IS NOT NULL';

} else {

Expand All @@ -488,23 +461,92 @@ public function where(string $column, string $operator, mixed $value): self

if ($value == '') { // Empty string needs no placeholder

$this->query[self::QUERY_WHERE] .= $column . " " . $operator . " ''";
$condition .= $column . " " . $operator . " ''";

} else if ($this->is_function($value)) {

$this->query[self::QUERY_WHERE] .= $column . ' ' . $operator . ' ' . $value;
$condition .= $column . ' ' . $operator . ' ' . $value;

} else {

$this->placeholders[] = $value;
$this->query[self::QUERY_WHERE] .= $column . ' ' . $operator . ' ?';
$placeholders[] = $value;
$condition .= $column . ' ' . $operator . ' ?';

}

}

if (!isset($this->query[self::QUERY_WHERE])) {
$this->query[self::QUERY_WHERE] = $condition . ')';
} else {
$this->query[self::QUERY_WHERE] .= $condition . ')';
}

$this->placeholders = array_merge($this->placeholders, $placeholders);

}

/**
* Adds a WHERE/AND WHERE clause to the query.
*
* If the column type is JSON, keys from within the JSON string can be searched with the format of COLUMN->KEY.
* JSON fields which do not exist are treated as null.
*
* Available operators are:
*
* - eq (equals)
* - !eq (does not equal)
* - lt (less than)
* - gt (greater than)
* - le (less than or equal to)
* - ge (greater than or equal to)
* - sw (starts with)
* - !sw (does not start with)
* - ew (ends with)
* - !ew (does not end with)
* - has (has)
* - !has (does not have)
* - in (in)
* - !in (not in)
* - null (is or is not null)
*
* The OPERATOR_* constants can be used for this purpose.
*
* The in and !in operators accept multiple comma-separated values.
*
* The "null" operator accepts two values: true and false for is null or is not null.
* The VALUE_* constants can be used for this purpose.
*
* NOTE: Some native MySQL functions can be used as the $value, however, they will be
* injected into the query as strings, so they can be vulnerable to SQL injection.
*
* @param string $column
* @param string $operator
* @param mixed $value
* @return self
* @throws QueryException
*/
public function where(string $column, string $operator, mixed $value): self
{
$this->addCondition(self::CONDITION_AND, $column, $operator, $value);
return $this;
}

/**
* Adds an OR/AND OR clause to the query.
*
* See where().
*
* @param string $column
* @param string $operator
* @param mixed $value
* @return $this
* @throws QueryException
*/
public function orWhere(string $column, string $operator, mixed $value): self
{
$this->addCondition(self::CONDITION_OR, $column, $operator, $value);
return $this;
}

/**
Expand Down

0 comments on commit ce947e7

Please sign in to comment.