Skip to content

Commit

Permalink
Merge pull request #19 from Icinga/filterable-interface
Browse files Browse the repository at this point in the history
Introduce interface `Filterable`
  • Loading branch information
nilmerg authored Mar 19, 2021
2 parents 62237ae + 1aed5a0 commit 69fce35
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 0 deletions.
63 changes: 63 additions & 0 deletions src/Contract/Filterable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace ipl\Stdlib\Contract;

use ipl\Stdlib\Filter;

interface Filterable
{
/**
* Get the filter of the query
*
* @return Filter\Chain
*/
public function getFilter();

/**
* Add a filter to the query
*
* Note that this method does not override an already set filter. Instead, multiple calls to this function add
* the specified filter using a {@see Filter\All} chain.
*
* @param Filter\Rule $filter
*
* @return $this
*/
public function filter(Filter\Rule $filter);

/**
* Add a filter to the query
*
* Note that this method does not override an already set filter. Instead, multiple calls to this function add
* the specified filter using a {@see Filter\Any} chain.
*
* @param Filter\Rule $filter
*
* @return $this
*/
public function orFilter(Filter\Rule $filter);

/**
* Add a filter to the query
*
* Note that this method does not override an already set filter. Instead, multiple calls to this function add
* the specified filter wrapped by a {@see Filter\None} chain and using a {@see Filter\All} chain.
*
* @param Filter\Rule $filter
*
* @return $this
*/
public function notFilter(Filter\Rule $filter);

/**
* Add a filter to the query
*
* Note that this method does not override an already set filter. Instead, multiple calls to this function add
* the specified filter wrapped by a {@see Filter\None} chain and using a {@see Filter\Any} chain.
*
* @param Filter\Rule $filter
*
* @return $this
*/
public function orNotFilter(Filter\Rule $filter);
}
58 changes: 58 additions & 0 deletions src/Filters.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace ipl\Stdlib;

trait Filters
{
/** @var Filter\Chain */
protected $filter;

public function getFilter()
{
return $this->filter ?: Filter::all();
}

public function filter(Filter\Rule $filter)
{
$currentFilter = $this->getFilter();
if ($currentFilter instanceof Filter\All) {
$this->filter = $currentFilter->add($filter);
} else {
$this->filter = Filter::all($filter);
if (! $currentFilter->isEmpty()) {
$this->filter->insertBefore($currentFilter, $filter);
}
}

return $this;
}

public function orFilter(Filter\Rule $filter)
{
$currentFilter = $this->getFilter();
if ($currentFilter instanceof Filter\Any) {
$this->filter = $currentFilter->add($filter);
} else {
$this->filter = Filter::any($filter);
if (! $currentFilter->isEmpty()) {
$this->filter->insertBefore($currentFilter, $filter);
}
}

return $this;
}

public function notFilter(Filter\Rule $filter)
{
$this->filter(Filter::none($filter));

return $this;
}

public function orNotFilter(Filter\Rule $filter)
{
$this->orFilter(Filter::none($filter));

return $this;
}
}
125 changes: 125 additions & 0 deletions tests/FiltersTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php

namespace ipl\Tests\Stdlib;

use ipl\Stdlib\Contract\Filterable;
use ipl\Stdlib\Filter;
use ipl\Tests\Stdlib\FiltersTest\FiltersUser;

class FiltersTest extends \PHPUnit\Framework\TestCase
{
/** @var Filterable */
protected $filterable;

public function setUp()
{
$this->filterable = new FiltersUser();
}

public function testFilterKeepsCurrentHierarchy()
{
$this->filterable->filter(Filter::equal('', ''));
$this->filterable->filter(Filter::unequal('', ''));

$this->assertSameFilterHierarchy(Filter::all(
Filter::equal('', ''),
Filter::unequal('', '')
));
}

public function testFilterWrapsCurrentHierarchy()
{
$this->filterable->orFilter(Filter::equal('', ''));
$this->filterable->filter(Filter::unequal('', ''));

$this->assertSameFilterHierarchy(Filter::all(
Filter::any(Filter::equal('', '')),
Filter::unequal('', '')
));
}

public function testOrFilterKeepsCurrentHierarchy()
{
$this->filterable->orFilter(Filter::equal('', ''));
$this->filterable->orFilter(Filter::unequal('', ''));

$this->assertSameFilterHierarchy(Filter::any(
Filter::equal('', ''),
Filter::unequal('', '')
));
}

public function testOrFilterWrapsCurrentHierarchy()
{
$this->filterable->filter(Filter::equal('', ''));
$this->filterable->orFilter(Filter::unequal('', ''));

$this->assertSameFilterHierarchy(Filter::any(
Filter::all(Filter::equal('', '')),
Filter::unequal('', '')
));
}

public function testNotFilterKeepsCurrentHierarchy()
{
$this->filterable->notFilter(Filter::equal('', ''));
$this->filterable->notFilter(Filter::unequal('', ''));

$this->assertSameFilterHierarchy(Filter::all(
Filter::none(Filter::equal('', '')),
Filter::none(Filter::unequal('', ''))
));
}

public function testNotFilterWrapsCurrentHierarchy()
{
$this->filterable->orFilter(Filter::equal('', ''));
$this->filterable->notFilter(Filter::unequal('', ''));

$this->assertSameFilterHierarchy(Filter::all(
Filter::any(Filter::equal('', '')),
Filter::none(Filter::unequal('', ''))
));
}

public function testOrNotFilterKeepsCurrentHierarchy()
{
$this->filterable->orNotFilter(Filter::equal('', ''));
$this->filterable->orNotFilter(Filter::unequal('', ''));

$this->assertSameFilterHierarchy(Filter::any(
Filter::none(Filter::equal('', '')),
Filter::none(Filter::unequal('', ''))
));
}

public function testOrNotFilterWrapsCurrentHierarchy()
{
$this->filterable->filter(Filter::equal('', ''));
$this->filterable->orNotFilter(Filter::unequal('', ''));

$this->assertSameFilterHierarchy(Filter::any(
Filter::all(Filter::equal('', '')),
Filter::none(Filter::unequal('', ''))
));
}

protected function assertSameFilterHierarchy(Filter\Chain $expected)
{
$actual = $this->filterable->getFilter();

$checkHierarchy = function ($expected, $actual) use (&$checkHierarchy) {
$expectedArray = iterator_to_array($expected);
$actualArray = iterator_to_array($actual);
foreach ($expectedArray as $key => $rule) {
$this->assertTrue(isset($actualArray[$key]));
$this->assertInstanceOf(get_class($rule), $actualArray[$key]);
if ($rule instanceof Filter\Chain) {
$checkHierarchy($rule, $actualArray[$key]);
}
}
};

$checkHierarchy($expected, $actual);
}
}
11 changes: 11 additions & 0 deletions tests/FiltersTest/FiltersUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace ipl\Tests\Stdlib\FiltersTest;

use ipl\Stdlib\Contract\Filterable;
use ipl\Stdlib\Filters;

class FiltersUser implements Filterable
{
use Filters;
}

0 comments on commit 69fce35

Please sign in to comment.