Skip to content

Commit

Permalink
Made ObjectCollection::matching() criteria expressions to behave mo…
Browse files Browse the repository at this point in the history
…re like in Twig
  • Loading branch information
mahagr committed Jul 5, 2018
1 parent f3c559f commit 07f8dfb
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 1 deletion.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# v1.5.0-beta.1
# v1.5.0-beta.2
## mm/dd/2018

1. [](#improved)
* Made `ObjectCollection::matching()` criteria expressions to behave more like in Twig
1. [](#bugfix)
* Fixed regression in 1.5.0-beta.1 blueprint extend and embed

# v1.5.0-beta.1
## 06/19/2018

1. [](#new)
* Set minimum requirements to [PHP 5.6.4](https://getgrav.org/blog/raising-php-requirements-2018)
* Updated Doctrine Collections to 1.4
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<?php
/**
* @package Grav\Framework\Object
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

namespace Grav\Framework\Object\Collection;

use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor;
use Doctrine\Common\Collections\Expr\Comparison;

class ObjectExpressionVisitor extends ClosureExpressionVisitor
{
/**
* Accesses the field of a given object.
*
* @param object $object
* @param string $field
*
* @return mixed
*/
public static function getObjectFieldValue($object, $field)
{
if (isset($object[$field])) {
return $object[$field];
}

$accessors = array('', 'get', 'is');

foreach ($accessors as $accessor) {
$accessor .= $field;

if (!method_exists($object, $accessor)) {
continue;
}

return $object->{$accessor}();
}

return null;
}

/**
* Helper for sorting arrays of objects based on multiple fields + orientations.
*
* @param string $name
* @param int $orientation
* @param \Closure $next
*
* @return \Closure
*/
public static function sortByField($name, $orientation = 1, \Closure $next = null)
{
if (!$next) {
$next = function() {
return 0;
};
}

return function ($a, $b) use ($name, $next, $orientation) {
$aValue = static::getObjectFieldValue($a, $name);
$bValue = static::getObjectFieldValue($b, $name);

if ($aValue === $bValue) {
return $next($a, $b);
}

return (($aValue > $bValue) ? 1 : -1) * $orientation;
};
}

/**
* {@inheritDoc}
*/
public function walkComparison(Comparison $comparison)
{
$field = $comparison->getField();
$value = $comparison->getValue()->getValue(); // shortcut for walkValue()

switch ($comparison->getOperator()) {
case Comparison::EQ:
return function ($object) use ($field, $value) {
return static::getObjectFieldValue($object, $field) === $value;
};

case Comparison::NEQ:
return function ($object) use ($field, $value) {
return static::getObjectFieldValue($object, $field) !== $value;
};

case Comparison::LT:
return function ($object) use ($field, $value) {
return static::getObjectFieldValue($object, $field) < $value;
};

case Comparison::LTE:
return function ($object) use ($field, $value) {
return static::getObjectFieldValue($object, $field) <= $value;
};

case Comparison::GT:
return function ($object) use ($field, $value) {
return static::getObjectFieldValue($object, $field) > $value;
};

case Comparison::GTE:
return function ($object) use ($field, $value) {
return static::getObjectFieldValue($object, $field) >= $value;
};

case Comparison::IN:
return function ($object) use ($field, $value) {
return \in_array(static::getObjectFieldValue($object, $field), $value, true);
};

case Comparison::NIN:
return function ($object) use ($field, $value) {
return !\in_array(static::getObjectFieldValue($object, $field), $value, true);
};

case Comparison::CONTAINS:
return function ($object) use ($field, $value) {
return false !== strpos(static::getObjectFieldValue($object, $field), $value);
};

case Comparison::MEMBER_OF:
return function ($object) use ($field, $value) {
$fieldValues = static::getObjectFieldValue($object, $field);
if (!is_array($fieldValues)) {
$fieldValues = iterator_to_array($fieldValues);
}
return \in_array($value, $fieldValues, true);
};

case Comparison::STARTS_WITH:
return function ($object) use ($field, $value) {
return 0 === strpos(static::getObjectFieldValue($object, $field), $value);
};

case Comparison::ENDS_WITH:
return function ($object) use ($field, $value) {
return $value === substr(static::getObjectFieldValue($object, $field), -strlen($value));
};


default:
throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator());
}
}
}
35 changes: 35 additions & 0 deletions system/src/Grav/Framework/Object/ObjectCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@

namespace Grav\Framework\Object;

use Doctrine\Common\Collections\Criteria;
use Grav\Framework\Collection\ArrayCollection;
use Grav\Framework\Object\Access\NestedPropertyCollectionTrait;
use Grav\Framework\Object\Base\ObjectCollectionTrait;
use Grav\Framework\Object\Collection\ObjectExpressionVisitor;
use Grav\Framework\Object\Interfaces\NestedObjectInterface;
use Grav\Framework\Object\Interfaces\ObjectCollectionInterface;

Expand All @@ -36,6 +38,39 @@ public function __construct(array $elements = [], $key = null)
$this->setKey($key);
}

/**
* {@inheritDoc}
*/
public function matching(Criteria $criteria)
{
$expr = $criteria->getWhereExpression();
$filtered = $this->getElements();

if ($expr) {
$visitor = new ObjectExpressionVisitor();
$filter = $visitor->dispatch($expr);
$filtered = array_filter($filtered, $filter);
}

if ($orderings = $criteria->getOrderings()) {
$next = null;
foreach (array_reverse($orderings) as $field => $ordering) {
$next = ObjectExpressionVisitor::sortByField($field, $ordering == Criteria::DESC ? -1 : 1, $next);
}

uasort($filtered, $next);
}

$offset = $criteria->getFirstResult();
$length = $criteria->getMaxResults();

if ($offset || $length) {
$filtered = array_slice($filtered, (int)$offset, $length);
}

return $this->createFrom($filtered);
}

protected function getElements()
{
return $this->toArray();
Expand Down

0 comments on commit 07f8dfb

Please sign in to comment.