/** * De inhoud van de WHERE or HAVING samenstellen. * * @param array $restrictions * @param bool $addBraces * * @return string */ private function composeRestrictions($restrictions, $addBraces = false) { // [string] if (is_string($restrictions)) { // Is the restrictionsTree already parsed? if ($addBraces) { return '(' . $restrictions . ')'; } return $restrictions; } $logicalOperator = \Sledgehammer\extract_logical_operator($restrictions); if ($logicalOperator === false) { if (count($restrictions) !== 1) { throw new InfoException('where[] statements require an logical operator, Example: array("AND", "x = 1", "y = 2")', $restrictions); } } else { unset($restrictions[0]); // Remove the operator from the array. } if (count($restrictions) === 1) { reset($restrictions); return $this->composeRestrictions(current($restrictions)); } switch ($logicalOperator) { case 'AND': case 'OR': break; default: throw new Exception('Unknown logical operator: "' . $logicalOperator . '"'); } $expressions = []; foreach ($restrictions as $restriction) { if (is_array($restriction)) { // Is het geen sql maar nog een 'restriction'? $operatorSwitch = \Sledgehammer\extract_logical_operator($restriction) !== $logicalOperator; // If the subnode has the same logical operator, don't add braces. "x = 1 AND (y = 5 AND z = 8)" has same meaning as "x = 1 AND y = 5 AND z = 8" $restriction = $this->composeRestrictions($restriction, $operatorSwitch); } if ($restriction != '') { // ignore empty statements. $expressions[] = $restriction; } } if (count($expressions) == 0) { // No statements (a restriction with just the operator "array(AND')" but no restrictions). return ''; } $sql = implode(' ' . $logicalOperator . ' ', $expressions); // Join the sql statements with the logical operator. if ($addBraces) { // moeten er haakjes omheen? return '(' . $sql . ')'; } return $sql; }
/** * Return a subsection of the collection based on the conditions. * * Convert the $conditions to SQL object when appropriate. * * auto converts * ['x_id' => null] to "x_id IS NULL" * ['x_id !=' => null] to "x_id IS NOT NULL" * 'hits' => 0] to "hits = '0'" (Because in mysql '' = 0 evaluates to true, '' = '0' to false) * * @param array $conditions * * @return Collection */ public function where($conditions) { if ($this->data !== null || is_string($this->sql) || is_object($conditions) && is_callable($conditions) || $this->sql->limit !== false || $this->sql->offset != 0) { return parent::where($conditions); } $db = Connection::instance($this->dbLink); $sql = $this->sql; $logicalOperator = \Sledgehammer\extract_logical_operator($conditions); if ($logicalOperator === false) { if (count($conditions) > 1) { \Sledgehammer\notice('Conditions with multiple conditions require a logical operator.', "Example: array('AND', 'x' => 1, 'y' => 5)"); } $logicalOperator = 'AND'; } else { unset($conditions[0]); } if ($logicalOperator === 'AND') { $method = 'andWhere'; } elseif ($logicalOperator === 'OR') { $method = 'orWhere'; } else { throw new Exception('Unsupported logical operator "' . $logicalOperator . '", expecting "AND" or "OR"'); } // The result are rows(fetch_assoc arrays), all conditions must be columnnames (or invalid) foreach ($conditions as $path => $value) { if (preg_match('/^(.*) (' . \Sledgehammer\COMPARE_OPERATORS . ')$/', $path, $matches)) { $column = $this->convertPathToColumn($matches[1]); $operator = $matches[2]; } else { $column = $this->convertPathToColumn($path); $operator = '=='; } if ($column === false) { // Converting to path failed? \Sledgehammer\array_key_unshift($conditions, 0, $logicalOperator); return parent::where($conditions); } if ($value === null) { switch ($operator) { case '==': $operator = 'IS'; $expectation = 'NULL'; break; case '!=': $operator = 'IS NOT '; $expectation = 'NULL'; break; case '>': case '<': case '>=': case '<=': $expectation = "''"; break; default: \Sledgehammer\warning('Unknown behavior for NULL values with operator "' . $operator . '"'); $expectation = $db->quote($expectation); break; } $sql = $sql->{$method}($column . ' ' . $operator . ' ' . $expectation); } else { if ($operator === '!=') { $sql = $sql->{$method}('(' . $column . ' != ' . $db->quote($value, PDO::PARAM_STR) . ' OR ' . $column . ' IS NULL)'); } elseif ($operator === 'IN') { if ((is_array($value) || $value instanceof Traversable) === false) { \Sledgehammer\notice('Operator IN expects an array or Traversable', $value); $value = explode(',', $value); } $quoted = []; foreach ($value as $val) { $quoted[] = $this->quote($db, $column, $val); } $sql = $sql->{$method}($column . ' ' . $operator . ' (' . implode(', ', $quoted) . ')'); } else { if ($operator === '==') { $operator = '='; } $sql = $sql->{$method}($column . ' ' . $operator . ' ' . $this->quote($db, $column, $value)); } } } return new self($sql, $this->dbLink); }
public function where($conditions) { if ($this->isConverted) { return parent::where($conditions); } if ($this->data instanceof Collection && is_array($conditions)) { $logicalOperator = \Sledgehammer\extract_logical_operator($conditions); $convertedConditions = []; if ($logicalOperator === false) { if (count($conditions) > 1) { notice('Conditions with multiple conditions require a logical operator.', "Example: array('AND', 'x' => 1, 'y' => 5)"); } $minimum = 0; } else { $minimum = 1; $convertedConditions[0] = $logicalOperator; } foreach ($conditions as $path => $value) { if (preg_match('/^(.*) (' . \Sledgehammer\COMPARE_OPERATORS . ')$/', $path, $match)) { $column = $match[1]; $columnOperator = ' ' . $match[2]; } else { $column = $path; $columnOperator = ''; } if (($path !== 0 || $logicalOperator === false) && isset($this->options['mapping'][$column])) { $convertCondition = true; if (isset($this->options['writeFilters'][$column])) { if (in_array($columnOperator, array('', '==', '!='))) { $value = filter($value, $this->options['writeFilters'][$column]); } else { $convertCondition = false; // operation can't work reliably with filters } } if ($convertCondition) { $convertedConditions[$this->options['mapping'][$column] . $columnOperator] = $value; unset($conditions[$path]); } } } if (count($convertedConditions) > $minimum) { // There are conditions the low-level collection can handle? $collection = new self($this->data->where($convertedConditions), $this->model, $this->repository, $this->options); if (count($conditions) === $minimum) { return $collection; } return $collection->where($conditions); // Apply the remaining conditions } elseif (count($conditions) === $minimum) { // An empty array was given as $conditions? return new self(clone $this->data, $this->model, $this->repository, $this->options); } } return parent::where($conditions); }
/** * Build a closure which validates an item with the gives $conditions. * * @param mixed $conditions array|Closure|expression See Collection::where() for condition options * * @return callable */ protected function buildFilter($conditions) { if (\Sledgehammer\is_closure($conditions)) { return $conditions; } if (is_array($conditions)) { // Create filter that checks all conditions $logicalOperator = \Sledgehammer\extract_logical_operator($conditions); if ($logicalOperator === false) { if (count($conditions) > 1) { \Sledgehammer\notice('Conditions with multiple conditions require a logical operator.', "Example: array('AND', 'x' => 1, 'y' => 5)"); } $logicalOperator = 'AND'; } else { unset($conditions[0]); } $operators = []; foreach ($conditions as $path => $expectation) { if (preg_match('/^(.*) (' . \Sledgehammer\COMPARE_OPERATORS . ')$/', $path, $matches)) { unset($conditions[$path]); $conditions[$matches[1]] = $expectation; $operators[$matches[1]] = $matches[2]; } else { $operators[$path] = false; } } // @todo Build an optimized closure for when a single conditions is given. if ($logicalOperator === 'AND') { return function ($item) use($conditions, $operators) { foreach ($conditions as $path => $expectation) { $actual = PropertyPath::get($path, $item); $operator = $operators[$path]; if ($operator) { if (\Sledgehammer\compare($actual, $operator, $expectation) === false) { return false; } } elseif (\Sledgehammer\equals($actual, $expectation) === false) { return false; } } return true; // All conditions are met. }; } elseif ($logicalOperator === 'OR') { return function ($item) use($conditions, $operators) { foreach ($conditions as $path => $expectation) { $actual = PropertyPath::get($path, $item); $operator = $operators[$path]; if ($operator) { if (\Sledgehammer\compare($actual, $operator, $expectation) !== false) { return true; } } elseif (\Sledgehammer\equals($actual, $expectation) !== false) { return true; } } return false; // None of conditions are met. }; } else { throw new Exception('Unsupported logical operator "' . $logicalOperator . '", expecting "AND" or "OR"'); } } //'<= 5' or '10' // Compare the item directly with value given as $condition. if (is_string($conditions) && preg_match('/^(' . \Sledgehammer\COMPARE_OPERATORS . ') (.*)$/', $conditions, $matches)) { $operator = $matches[1]; $expectation = $matches[2]; } else { $expectation = $conditions; $operator = '=='; } return function ($value) use($expectation, $operator) { return \Sledgehammer\compare($value, $operator, $expectation); }; }