Skip to content

Commit

Permalink
Merge pull request #10 from tattersoftware/wheres
Browse files Browse the repository at this point in the history
Implement Model-like methods
  • Loading branch information
MGatner authored Sep 19, 2020
2 parents b35b610 + 99a841b commit c509ca7
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 44 deletions.
163 changes: 142 additions & 21 deletions src/Handlers.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ class Handlers
protected $cache;

/**
* Array of attribute criteria.
* Array of filters.
*
* @var array<string, mixed>
* @var array of [key, operator, value, combine]
*/
protected $criteria = [];
protected $filters = [];

/**
* Array of discovered HandlerInterface class names and their attributes.
Expand All @@ -57,7 +57,7 @@ public function __construct(string $path = '', HandlersConfig $config = null, Ca
}

/**
* Returns the curent configuration.
* Returns the current configuration.
*
* @return HandlersConfig
*/
Expand Down Expand Up @@ -97,27 +97,41 @@ public function setPath(string $path): self
//--------------------------------------------------------------------

/**
* Adds attribute criteria.
* Adds attribute filters.
*
* @param array<string, mixed> $criteria
*
* @return $this
*/
public function where(array $criteria): self
{
$this->criteria = array_merge($this->criteria, $criteria);
$this->parseCriteria($criteria, true);

return $this;
}

/**
* Resets criteria between returns.
* Adds attribute filters that do not combine.
*
* @param array<string, mixed> $criteria
*
* @return $this
*/
public function orWhere(array $criteria): self
{
$this->parseCriteria($criteria, false);

return $this;
}

/**
* Resets filters between returns.
*
* @return $this
*/
public function reset(): self
{
$this->criteria = [];
$this->filters = [];

return $this;
}
Expand All @@ -142,7 +156,7 @@ public function first(): ?string
*
* @return array<string>
*/
public function all(): array
public function findAll(): array
{
$classes = $this->filterHandlers();
$this->reset();
Expand All @@ -157,7 +171,7 @@ public function all(): array
*
* @return string|null The full class name, or null if none found
*/
public function named(string $name): ?string
public function find(string $name): ?string
{
$this->discoverHandlers();

Expand Down Expand Up @@ -196,6 +210,58 @@ public function named(string $name): ?string

//--------------------------------------------------------------------

/**
* Returns an array of all matched classes.
*
* @return array<string>
* @deprecated Use findAll()
*/
public function all(): array
{
return $this->findAll();
}

/**
* Returns a handler with a given name. Ignores filters.
* Searches: attribute "name" or "uid", namespaced class, and short class name.
*
* @param string $name The name of the handler
*
* @return string|null The full class name, or null if none found
* @deprecated Use find()
*/
public function named(string $name): ?string
{
return $this->find($name);
}

//--------------------------------------------------------------------

/**
* Parses "where" $criteria and adds to $filters
*
* @param array $criteria Array of 'key [operator]' => 'value'
* @param bool $combine Whether the resulting filter should be combined with others
*/
protected function parseCriteria(array $criteria, bool $combine): void
{
foreach ($criteria as $key => $value)
{
// Check for an operator
$key = trim($key);
if (strpos($key, ' '))
{
list($key, $operator) = explode(' ', $key);
}
else
{
$operator = '==';
}

$this->filters[] = [$key, $operator, $value, $combine];
}
}

/**
* Iterates through discovered handlers and attempts to register them.
*
Expand Down Expand Up @@ -231,18 +297,19 @@ public function register(): array
//--------------------------------------------------------------------

/**
* Filters discovered classes by the criteria.
* Filters discovered classes by the defined criteria.
*
* @param int|null $limit Limit on how many classes to match
*
* @return array<string>
* @throws \RuntimeException
*/
protected function filterHandlers(int $limit = null): array
{
$this->discoverHandlers();

// Make sure there is work to do
if (empty($this->criteria) || empty($this->discovered))
if (empty($this->filters) || empty($this->discovered))
{
$classes = array_keys($this->discovered);

Expand All @@ -252,21 +319,75 @@ protected function filterHandlers(int $limit = null): array
$classes = [];
foreach ($this->discovered as $class => $attributes)
{
// Check each attribute against the criteria
foreach ($this->criteria as $key => $value)
$result = true;

// Check each attribute against the filters
foreach ($this->filters as $filter)
{
if ($attributes[$key] !== $value)
// Split out the array to make it easier to read
list($key, $operator, $value, $combine) = $filter;

if (! isset($attributes[$key]))
{
$result = false;
continue;
}

switch ($operator)
{
continue 2;
case '==':
case '=':
$test = $attributes[$key] == $value;
break;

case '===':
$test = $attributes[$key] === $value;
break;

case '>':
$test = $attributes[$key] > $value;
break;

case '>=':
$test = $attributes[$key] >= $value;
break;

case '<':
$test = $attributes[$key] < $value;
break;

case '<=':
$test = $attributes[$key] <= $value;
break;

// Assumes the attribute is a CSV
case 'has':
$test = in_array($value, explode(',', $attributes[$key]));
break;

default:
throw new \RuntimeException($operator . ' is not a vald criteria operator');
}
}

// A match!
$classes[] = $class;
// If this filter was sufficient on its own then skip the rest of the filters
if ($test && ! $combine)
{
$result = true;
break;
}

$result = $result && $test;
}

if ($limit && count($classes) >= $limit)
// Check for a match
if ($result)
{
return $classes;
$classes[] = $class;

if ($limit && count($classes) >= $limit)
{
return $classes;
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions tests/_support/Factories/PopFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class PopFactory implements HandlerInterface
'name' => 'Pop Factory',
'uid' => 'pop',
'summary' => 'Makes pop',
'cost' => 1,
'list' => 'five,six',
];

public function process()
Expand Down
2 changes: 2 additions & 0 deletions tests/_support/Factories/WidgetFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class WidgetFactory extends BaseHandler
'name' => 'Widget Plant',
'uid' => 'widget',
'summary' => "The world's largest supplier of widgets!",
'cost' => 10,
'list' => 'one,two,three,four',
];

public function process()
Expand Down
19 changes: 12 additions & 7 deletions tests/unit/LibraryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,30 @@ public function testSetPathChangesPath()
$this->assertEquals($path, $result);
}

public function testWhereMergesCriteria()
public function testWhereCombinesFilters()
{
$this->handlers->where(['group' => 'East']);

$result = $this->getPrivateProperty($this->handlers, 'criteria');
$this->assertEquals($result, ['group' => 'East']);
$result = $this->getPrivateProperty($this->handlers, 'filters');
$this->assertEquals($result, [
['group', '==', 'East', true],
]);

$this->handlers->where(['uid' => 'pop']);

$result = $this->getPrivateProperty($this->handlers, 'criteria');
$this->assertEquals($result, ['group' => 'East', 'uid' => 'pop']);
$result = $this->getPrivateProperty($this->handlers, 'filters');
$this->assertEquals($result, [
['group', '==', 'East', true],
['uid', '==', 'pop', true],
]);
}

public function testResetClearsCriteria()
public function testResetClearsFilters()
{
$this->handlers->where(['group' => 'East']);
$this->handlers->reset();

$result = $this->getPrivateProperty($this->handlers, 'criteria');
$result = $this->getPrivateProperty($this->handlers, 'filters');
$this->assertEquals($result, []);
}

Expand Down
Loading

0 comments on commit c509ca7

Please sign in to comment.