Changes for beta10 (#5)
- Add In/InArray support - Add ORDER BY support for `Find` functions - Update dependencies - Implement fixes identified via static analysis Reviewed-on: #5
This commit was merged in pull request #5.
This commit is contained in:
300
src/Field.php
300
src/Field.php
@@ -27,31 +27,67 @@ class Field
|
||||
* @param string $paramName The name of the parameter to which this should be bound
|
||||
* @param string $qualifier A table qualifier for the `data` column
|
||||
*/
|
||||
public function __construct(public string $fieldName = '', public Op $op = Op::EQ, public mixed $value = '',
|
||||
public function __construct(public string $fieldName = '', public Op $op = Op::Equal, public mixed $value = '',
|
||||
public string $paramName = '', public string $qualifier = '') { }
|
||||
|
||||
/**
|
||||
* Append the parameter name and value to the given associative array
|
||||
*
|
||||
* @param array $existing The existing parameters
|
||||
* @return array The given parameter array with this field's name and value appended
|
||||
* @param array<string, mixed> $existing The existing parameters
|
||||
* @return array<string, mixed> The given parameter array with this field's name and value(s) appended
|
||||
*/
|
||||
public function appendParameter(array $existing): array
|
||||
{
|
||||
switch ($this->op) {
|
||||
case Op::EX:
|
||||
case Op::NEX:
|
||||
case Op::Exists:
|
||||
case Op::NotExists:
|
||||
break;
|
||||
case Op::BT:
|
||||
case Op::Between:
|
||||
$existing["{$this->paramName}min"] = $this->value[0];
|
||||
$existing["{$this->paramName}max"] = $this->value[1];
|
||||
break;
|
||||
case Op::In:
|
||||
for ($idx = 0; $idx < count($this->value); $idx++) {
|
||||
$existing["{$this->paramName}_$idx"] = $this->value[$idx];
|
||||
}
|
||||
break;
|
||||
case Op::InArray:
|
||||
$mkString = Configuration::mode("Append parameters for InArray condition") === Mode::PgSQL;
|
||||
$values = $this->value['values'];
|
||||
for ($idx = 0; $idx < count($values); $idx++) {
|
||||
$existing["{$this->paramName}_$idx"] = $mkString ? "$values[$idx]" : $values[$idx];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$existing[$this->paramName] = $this->value;
|
||||
}
|
||||
return $existing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the path for this field
|
||||
*
|
||||
* @param bool $asJSON Whether the field should be treated as JSON in the query (optional, default false)
|
||||
* @return string The path for this field
|
||||
* @throws Exception If the database mode has not been set
|
||||
*/
|
||||
public function path(bool $asJSON = false): string
|
||||
{
|
||||
$extra = $asJSON ? '' : '>';
|
||||
if (str_contains($this->fieldName, '.')) {
|
||||
$mode = Configuration::mode('determine field path');
|
||||
if ($mode === Mode::PgSQL) {
|
||||
return "data#>$extra'{" . implode(',', explode('.', $this->fieldName)) . "}'";
|
||||
}
|
||||
if ($mode === Mode::SQLite) {
|
||||
$parts = explode('.', $this->fieldName);
|
||||
$last = array_pop($parts);
|
||||
return "data->'" . implode("'->'", $parts) . "'->$extra'$last'";
|
||||
}
|
||||
}
|
||||
return "data->$extra'$this->fieldName'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WHERE clause fragment for this parameter
|
||||
*
|
||||
@@ -60,26 +96,41 @@ class Field
|
||||
*/
|
||||
public function toWhere(): string
|
||||
{
|
||||
$mode = Configuration::mode('make field WHERE clause');
|
||||
$fieldName = (empty($this->qualifier) ? '' : "$this->qualifier.") . 'data' . match (true) {
|
||||
!str_contains($this->fieldName, '.') => "->>'$this->fieldName'",
|
||||
$mode === Mode::PgSQL => "#>>'{" . implode(',', explode('.', $this->fieldName)) . "}'",
|
||||
$mode === Mode::SQLite => "->>'" . implode("'->>'", explode('.', $this->fieldName)) . "'",
|
||||
};
|
||||
$fieldPath = match ($mode) {
|
||||
$mode = Configuration::mode('make field WHERE clause');
|
||||
$fieldName = (empty($this->qualifier) ? '' : "$this->qualifier.") . $this->path($this->op === Op::InArray);
|
||||
$fieldPath = match ($mode) {
|
||||
Mode::PgSQL => match (true) {
|
||||
$this->op === Op::BT => is_numeric($this->value[0]) ? "($fieldName)::numeric" : $fieldName,
|
||||
is_numeric($this->value) => "($fieldName)::numeric",
|
||||
default => $fieldName,
|
||||
$this->op === Op::Between,
|
||||
$this->op === Op::In => is_numeric($this->value[0]) ? "($fieldName)::numeric" : $fieldName,
|
||||
is_numeric($this->value) => "($fieldName)::numeric",
|
||||
default => $fieldName,
|
||||
},
|
||||
default => $fieldName,
|
||||
};
|
||||
$criteria = match ($this->op) {
|
||||
Op::EX, Op::NEX => '',
|
||||
Op::BT => " {$this->paramName}min AND {$this->paramName}max",
|
||||
default => " $this->paramName",
|
||||
Op::Exists,
|
||||
Op::NotExists => '',
|
||||
Op::Between => " {$this->paramName}min AND {$this->paramName}max",
|
||||
Op::In => ' (' . $this->inParameterNames() . ')',
|
||||
Op::InArray => $mode === Mode::PgSQL ? ' ARRAY[' . $this->inParameterNames() . ']' : '',
|
||||
default => " $this->paramName",
|
||||
};
|
||||
return $fieldPath . ' ' . $this->op->toSQL() . $criteria;
|
||||
return $mode === Mode::SQLite && $this->op === Op::InArray
|
||||
? "EXISTS (SELECT 1 FROM json_each({$this->value['table']}.data, '\$.$this->fieldName') WHERE value IN ("
|
||||
. $this->inParameterNames() . '))'
|
||||
: $fieldPath . ' ' . $this->op->toSQL() . $criteria;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create parameter names for an IN clause
|
||||
*
|
||||
* @return string A comma-delimited string of parameter names
|
||||
*/
|
||||
private function inParameterNames(): string
|
||||
{
|
||||
$values = $this->op === Op::In ? $this->value : $this->value['values'];
|
||||
return implode(', ',
|
||||
array_map(fn($value, $key) => "{$this->paramName}_$key", $values, range(0, count($values) - 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,11 +139,24 @@ class Field
|
||||
* @param string $fieldName The name of the field against which the value will be compared
|
||||
* @param mixed $value The value for which equality will be checked
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return static The field with the requested criterion
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function EQ(string $fieldName, mixed $value, string $paramName = ''): static
|
||||
public static function equal(string $fieldName, mixed $value, string $paramName = ''): self
|
||||
{
|
||||
return new static($fieldName, Op::EQ, $value, $paramName);
|
||||
return new self($fieldName, Op::Equal, $value, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an equals (=) field criterion _(alias for `Field.equal`)_
|
||||
*
|
||||
* @param string $fieldName The name of the field against which the value will be compared
|
||||
* @param mixed $value The value for which equality will be checked
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function EQ(string $fieldName, mixed $value, string $paramName = ''): self
|
||||
{
|
||||
return self::equal($fieldName, $value, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,11 +165,24 @@ class Field
|
||||
* @param string $fieldName The name of the field against which the value will be compared
|
||||
* @param mixed $value The value for the greater than comparison
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return static The field with the requested criterion
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function GT(string $fieldName, mixed $value, string $paramName = ''): static
|
||||
public static function greater(string $fieldName, mixed $value, string $paramName = ''): self
|
||||
{
|
||||
return new static($fieldName, Op::GT, $value, $paramName);
|
||||
return new self($fieldName, Op::Greater, $value, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a greater than (>) field criterion _(alias for `Field.greater`)_
|
||||
*
|
||||
* @param string $fieldName The name of the field against which the value will be compared
|
||||
* @param mixed $value The value for the greater than comparison
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function GT(string $fieldName, mixed $value, string $paramName = ''): self
|
||||
{
|
||||
return self::greater($fieldName, $value, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,11 +191,24 @@ class Field
|
||||
* @param string $fieldName The name of the field against which the value will be compared
|
||||
* @param mixed $value The value for the greater than or equal to comparison
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return static The field with the requested criterion
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function GE(string $fieldName, mixed $value, string $paramName = ''): static
|
||||
public static function greaterOrEqual(string $fieldName, mixed $value, string $paramName = ''): self
|
||||
{
|
||||
return new static($fieldName, Op::GE, $value, $paramName);
|
||||
return new self($fieldName, Op::GreaterOrEqual, $value, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a greater than or equal to (>=) field criterion _(alias for `Field.greaterOrEqual`)_
|
||||
*
|
||||
* @param string $fieldName The name of the field against which the value will be compared
|
||||
* @param mixed $value The value for the greater than or equal to comparison
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function GE(string $fieldName, mixed $value, string $paramName = ''): self
|
||||
{
|
||||
return self::greaterOrEqual($fieldName, $value, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,11 +217,24 @@ class Field
|
||||
* @param string $fieldName The name of the field against which the value will be compared
|
||||
* @param mixed $value The value for the less than comparison
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return static The field with the requested criterion
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function LT(string $fieldName, mixed $value, string $paramName = ''): static
|
||||
public static function less(string $fieldName, mixed $value, string $paramName = ''): self
|
||||
{
|
||||
return new static($fieldName, Op::LT, $value, $paramName);
|
||||
return new self($fieldName, Op::Less, $value, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a less than (<) field criterion _(alias for `Field.less`)_
|
||||
*
|
||||
* @param string $fieldName The name of the field against which the value will be compared
|
||||
* @param mixed $value The value for the less than comparison
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function LT(string $fieldName, mixed $value, string $paramName = ''): self
|
||||
{
|
||||
return self::less($fieldName, $value, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,11 +243,24 @@ class Field
|
||||
* @param string $fieldName The name of the field against which the value will be compared
|
||||
* @param mixed $value The value for the less than or equal to comparison
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return static The field with the requested criterion
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function LE(string $fieldName, mixed $value, string $paramName = ''): static
|
||||
public static function lessOrEqual(string $fieldName, mixed $value, string $paramName = ''): self
|
||||
{
|
||||
return new static($fieldName, Op::LE, $value, $paramName);
|
||||
return new self($fieldName, Op::LessOrEqual, $value, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a less than or equal to (<=) field criterion _(alias for `Field.lessOrEqual`)_
|
||||
*
|
||||
* @param string $fieldName The name of the field against which the value will be compared
|
||||
* @param mixed $value The value for the less than or equal to comparison
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function LE(string $fieldName, mixed $value, string $paramName = ''): self
|
||||
{
|
||||
return self::lessOrEqual($fieldName, $value, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,11 +269,24 @@ class Field
|
||||
* @param string $fieldName The name of the field against which the value will be compared
|
||||
* @param mixed $value The value for the not equals comparison
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return static The field with the requested criterion
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function NE(string $fieldName, mixed $value, string $paramName = ''): static
|
||||
public static function notEqual(string $fieldName, mixed $value, string $paramName = ''): self
|
||||
{
|
||||
return new static($fieldName, Op::NE, $value, $paramName);
|
||||
return new self($fieldName, Op::NotEqual, $value, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a not equals (<>) field criterion _(alias for `Field.notEqual`)_
|
||||
*
|
||||
* @param string $fieldName The name of the field against which the value will be compared
|
||||
* @param mixed $value The value for the not equals comparison
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function NE(string $fieldName, mixed $value, string $paramName = ''): self
|
||||
{
|
||||
return self::notEqual($fieldName, $value, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,32 +296,109 @@ class Field
|
||||
* @param mixed $minValue The lower value for range
|
||||
* @param mixed $maxValue The upper value for the range
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return static The field with the requested criterion
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function BT(string $fieldName, mixed $minValue, mixed $maxValue, string $paramName = ''): static
|
||||
public static function between(string $fieldName, mixed $minValue, mixed $maxValue, string $paramName = ''): self
|
||||
{
|
||||
return new static($fieldName, Op::BT, [$minValue, $maxValue], $paramName);
|
||||
return new self($fieldName, Op::Between, [$minValue, $maxValue], $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a BETWEEN field criterion _(alias for `Field.between`)_
|
||||
*
|
||||
* @param string $fieldName The name of the field against which the value will be compared
|
||||
* @param mixed $minValue The lower value for range
|
||||
* @param mixed $maxValue The upper value for the range
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function BT(string $fieldName, mixed $minValue, mixed $maxValue, string $paramName = ''): self
|
||||
{
|
||||
return self::between($fieldName, $minValue, $maxValue, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an IN field criterion
|
||||
*
|
||||
* @param string $fieldName The name of the field against which the values will be compared
|
||||
* @param mixed[] $values The potential matching values for the field
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function in(string $fieldName, array $values, string $paramName = ''): self
|
||||
{
|
||||
return new self($fieldName, Op::In, $values, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an IN ARRAY field criterion
|
||||
*
|
||||
* @param string $fieldName The name of the field against which the values will be compared
|
||||
* @param string $tableName The table name where this field is located
|
||||
* @param mixed[] $values The potential matching values for the field
|
||||
* @param string $paramName The name of the parameter to which this should be bound (optional; generated if blank)
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function inArray(string $fieldName, string $tableName, array $values, string $paramName = ''): self
|
||||
{
|
||||
return new self($fieldName, Op::InArray, ['table' => $tableName, 'values' => $values], $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an exists (IS NOT NULL) field criterion
|
||||
*
|
||||
* @param string $fieldName The name of the field for which existence will be checked
|
||||
* @return static The field with the requested criterion
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function EX(string $fieldName): static
|
||||
public static function exists(string $fieldName): self
|
||||
{
|
||||
return new static($fieldName, Op::EX, '', '');
|
||||
return new self($fieldName, Op::Exists, '', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an exists (IS NOT NULL) field criterion _(alias for `Field.exists`)_
|
||||
*
|
||||
* @param string $fieldName The name of the field for which existence will be checked
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function EX(string $fieldName): self
|
||||
{
|
||||
return self::exists($fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a not exists (IS NULL) field criterion
|
||||
*
|
||||
* @param string $fieldName The name of the field for which non-existence will be checked
|
||||
* @return static The field with the requested criterion
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function NEX(string $fieldName): static
|
||||
public static function notExists(string $fieldName): self
|
||||
{
|
||||
return new static($fieldName, Op::NEX, '', '');
|
||||
return new self($fieldName, Op::NotExists, '', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a not exists (IS NULL) field criterion _(alias for `Field.notExists`)_
|
||||
*
|
||||
* @param string $fieldName The name of the field for which non-existence will be checked
|
||||
* @return self The field with the requested criterion
|
||||
*/
|
||||
public static function NEX(string $fieldName): self
|
||||
{
|
||||
return self::notExists($fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a named fields (used for creating fields for ORDER BY clauses)
|
||||
*
|
||||
* Prepend the field name with 'n:' to treat the field as a number; prepend the field name with 'i:' to perform
|
||||
* a case-insensitive ordering.
|
||||
*
|
||||
* @param string $name The name of the field, plus any direction for the ordering
|
||||
* @return self
|
||||
*/
|
||||
public static function named(string $name): self
|
||||
{
|
||||
return new self($name, Op::Equal, '', '');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user