Skip to content

Commit

Permalink
Add support for Constraint objects
Browse files Browse the repository at this point in the history
  • Loading branch information
cundd committed Feb 14, 2019
1 parent a915832 commit cf5d6b3
Show file tree
Hide file tree
Showing 4 changed files with 343 additions and 50 deletions.
102 changes: 102 additions & 0 deletions Classes/VirtualObject/Persistence/Backend/Constraint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);

namespace Cundd\Rest\VirtualObject\Persistence\Backend;

use Cundd\Rest\VirtualObject\Persistence\OperatorInterface;

class Constraint implements OperatorInterface
{
/**
* @var string|int
*/
private $operator;

/**
* @var mixed
*/
private $value;

/**
* Constraint constructor
*
* @param int|string $operator
* @param mixed $value
*/
public function __construct($operator, $value)
{
$this->operator = WhereClauseBuilder::normalizeOperator($operator);
$this->value = $value;
}

public static function equalTo($value): self
{
return new static(self::OPERATOR_EQUAL_TO, $value);
}

public static function notEqualTo($value): self
{
return new static(self::OPERATOR_NOT_EQUAL_TO, $value);
}

public static function lessThan($value): self
{
return new static(self::OPERATOR_LESS_THAN, $value);
}

public static function lessThanOrEqualTo($value): self
{
return new static(self::OPERATOR_LESS_THAN_OR_EQUAL_TO, $value);
}

public static function greaterThan($value): self
{
return new static(self::OPERATOR_GREATER_THAN, $value);
}

public static function greaterThanOrEqualTo($value): self
{
return new static(self::OPERATOR_GREATER_THAN_OR_EQUAL_TO, $value);
}

public static function like($value): self
{
return new static(self::OPERATOR_LIKE, $value);
}

public static function contains($value): self
{
return new static(self::OPERATOR_CONTAINS, $value);
}

public static function in($value): self
{
return new static(self::OPERATOR_IN, $value);
}

public static function isNull(): self
{
return new static(self::OPERATOR_IS_NULL, null);
}

public static function isEmpty(): self
{
return new static(self::OPERATOR_IS_EMPTY, null);
}

/**
* @return int|string
*/
public function getOperator()
{
return $this->operator;
}

/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}
}
18 changes: 15 additions & 3 deletions Classes/VirtualObject/Persistence/Backend/V7Backend.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,21 @@ protected function createWhereStatementFromQuery($query, $tableName)

$column = $configuration ? $configuration->getSourceKeyForProperty($property) : $property;
InvalidColumnNameException::assertValidColumnName($column);

$operator = null;
$comparisonValue = null;
if (is_scalar($value) || $value === null) {
$operator = '=';
$comparisonValue = $adapter->fullQuoteStr($value, $tableName);
} elseif (is_array($value)) {
} elseif ($value instanceof Constraint) {
$value = [
'operator' => $value->getOperator(),
'value' => $value->getValue(),
];

// Continue with the value-array
}

if (is_array($value)) {
/**
* If you don't want the given value to be escaped set the constraint's "doNotEscapeValue" key to the
* name of it's property key
Expand All @@ -185,7 +195,9 @@ protected function createWhereStatementFromQuery($query, $tableName)
$comparisonValue = $adapter->fullQuoteStr($value['value'], $tableName);
}
$operator = isset($value['operator']) ? $this->resolveOperator($value['operator']) : '=';
} else {
}

if ($operator === null) {
throw new InvalidOperatorException('Operator could not be detected', 1404821478);
}
$constraints[] = ''
Expand Down
202 changes: 160 additions & 42 deletions Classes/VirtualObject/Persistence/Backend/WhereClauseBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Cundd\Rest\VirtualObject\Exception\InvalidOperatorException;
use Cundd\Rest\VirtualObject\Exception\MissingConfigurationException;
use Cundd\Rest\VirtualObject\Persistence\Exception\InvalidColumnNameException;
use Cundd\Rest\VirtualObject\Persistence\OperatorInterface;
use Cundd\Rest\VirtualObject\Persistence\QueryInterface;

/**
Expand Down Expand Up @@ -129,22 +130,7 @@ public function addConstraint(
}

InvalidColumnNameException::assertValidColumnName($column);

if (is_scalar($value) || $value === null || is_object($value)) {
$operator = '=';
$comparisonValue = $value;
} elseif (is_array($value)) {
$operator = isset($value['operator']) ? $this->resolveOperator($value['operator']) : '=';
$comparisonValue = $value['value'];
} else {
throw new InvalidOperatorException(
sprintf(
'Operator could not be detected for type "%s"',
is_object($value) ? get_class($value) : gettype($value)
),
1404821478
);
}
list('operator' => $operator, 'value' => $comparisonValue) = $this->extractOperatorAndValue($value);

if ($prepareValue === null) {
$prepareValue = $this->getDefaultPrepareValueCallback();
Expand All @@ -153,17 +139,27 @@ public function addConstraint(
$escapeColumnName = $this->getDefaultEscapeColumnNameCallback();
}

$bindingKey = $bindingPrefix . $column;
$this->appendSql(
$escapeColumnName($column)
. ' ' . $operator . ' '
. ':' . $bindingKey . '',
$combinator
);
$this->bindVariable(
$bindingKey,
$prepareValue($comparisonValue)
);
if ($operator === OperatorInterface::OPERATOR_IN) {
$this->addInConstraint(
$prepareValue($comparisonValue),
$column,
$bindingPrefix,
$combinator,
$escapeColumnName
);
} else {
$bindingKey = $bindingPrefix . $column;
$this->appendSql(
$escapeColumnName($column)
. ' ' . $this->resolveOperator($operator) . ' '
. ':' . $bindingKey . '',
$combinator
);
$this->bindVariable(
$bindingKey,
$prepareValue($comparisonValue)
);
}

return $this;
}
Expand Down Expand Up @@ -199,38 +195,75 @@ public function getWhere()
*/
public static function resolveOperator($operator)
{
if (!is_scalar($operator)) {
throw new InvalidOperatorException('Operator must be a scalar value', 1541074670);
}
switch ($operator) {
switch (static::normalizeOperator($operator)) {
case QueryInterface::OPERATOR_IN:
case 'IN':
case 'in':
return 'IN';
case QueryInterface::OPERATOR_EQUAL_TO:
case '=':
case '==':
return '=';
case QueryInterface::OPERATOR_NOT_EQUAL_TO:
case '!=':
case '<>':
return '!=';
case QueryInterface::OPERATOR_LESS_THAN:
case '<':
return '<';
case QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO:
case '<=':
return '<=';
case QueryInterface::OPERATOR_GREATER_THAN:
case '>':
return '>';
case QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO:
case '>=':
return '>=';
case QueryInterface::OPERATOR_LIKE:
return 'LIKE';
default:
throw new \OutOfRangeException('Unsupported operator encountered. Normalization failed', 1242816074);
}
}

/**
* Returns the SQL operator constant for the given operator
*
* @param string $operator One of the OPERATOR_* constants
* @throws InvalidOperatorException
* @return int One of the OPERATOR_* constants
*/
public static function normalizeOperator($operator): int
{
if (!is_scalar($operator)) {
throw new InvalidOperatorException(
sprintf(
'Operator must be a scalar value, "%s" given',
is_object($operator) ? get_class($operator) : gettype($operator)
),
1541074670
);
}
switch ($operator) {
case 'IN':
case 'in':
case QueryInterface::OPERATOR_IN:
return QueryInterface::OPERATOR_IN;
case '=':
case '==':
case QueryInterface::OPERATOR_EQUAL_TO:
return QueryInterface::OPERATOR_EQUAL_TO;
case '!=':
case '<>':
case QueryInterface::OPERATOR_NOT_EQUAL_TO:
return QueryInterface::OPERATOR_NOT_EQUAL_TO;
case '<':
case QueryInterface::OPERATOR_LESS_THAN:
return QueryInterface::OPERATOR_LESS_THAN;
case '<=':
case QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO:
return QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO;
case '>':
case QueryInterface::OPERATOR_GREATER_THAN:
return QueryInterface::OPERATOR_GREATER_THAN;
case '>=':
case QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO:
return QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO;
case 'LIKE':
case 'like':
return 'LIKE';
case QueryInterface::OPERATOR_LIKE:
return QueryInterface::OPERATOR_LIKE;
default:
throw new InvalidOperatorException('Unsupported operator encountered.', 1242816073);
}
Expand Down Expand Up @@ -283,4 +316,89 @@ protected function getDefaultEscapeColumnNameCallback()
return '`' . $propertyName . '`';
};
}

/**
* @param $value
* @return array
*/
private function extractOperatorAndValue($value): array
{
if ($value instanceof Constraint) {
return [
'operator' => $this->normalizeOperator($value->getOperator()),
'value' => $value->getValue(),
];
}

if (is_scalar($value) || $value === null || is_object($value)) {
return [
'operator' => OperatorInterface::OPERATOR_EQUAL_TO,
'value' => $value,
];
}

if (is_array($value)) {
$operator = isset($value['operator'])
? $this->normalizeOperator($value['operator'])
: OperatorInterface::OPERATOR_EQUAL_TO;

return [
'operator' => $operator,
'value' => $value['value'],
];
}

throw new InvalidOperatorException(
sprintf(
'Operator could not be detected for type "%s"',
is_object($value) ? get_class($value) : gettype($value)
),
1404821478
);
}

/**
* @param array $values
* @param string|null $column
* @param string $bindingPrefix
* @param string $combinator
* @param callable $escapeColumnName
* @return WhereClauseBuilder
*/
private function addInConstraint(
array $values,
string $column,
string $bindingPrefix,
string $combinator,
callable $escapeColumnName
): self {
$bindingKeyBase = ':' . $bindingPrefix . $column;

$hasOnlyIntegerValues = count(array_filter($values, 'is_int')) === count($values);
if ($hasOnlyIntegerValues) {
return $this->appendSql(
$escapeColumnName($column)
. ' ' . $this->resolveOperator(OperatorInterface::OPERATOR_IN) . ' '
. '(' . implode(',', array_values($values)) . ')',
$combinator
);
}

$i = 0;
$bindings = [];
foreach ($values as $value) {
$currentKey = $bindingKeyBase . $i;
$bindings[$currentKey] = $value;

$this->bindVariable($currentKey, $value);
$i += 1;
}

return $this->appendSql(
$escapeColumnName($column)
. ' ' . $this->resolveOperator(OperatorInterface::OPERATOR_IN) . ' '
. '(' . implode(',', array_keys($bindings)) . ')',
$combinator
);
}
}
Loading

0 comments on commit cf5d6b3

Please sign in to comment.