Skip to content

Instantly share code, notes, and snippets.

@G-Rath
Created March 26, 2019 03:13
Show Gist options
  • Save G-Rath/934fabe5ed91fefe61d6f3e656527cd5 to your computer and use it in GitHub Desktop.
Save G-Rath/934fabe5ed91fefe61d6f3e656527cd5 to your computer and use it in GitHub Desktop.
<?php
/**
* A object that provides a predicate for filtering based on
* comparing a given value against a search term in some way.
*/
interface FieldFilterer
{
/**
* Compares the given `$value` against the search `$term`.
*
* The actual comparison is an implementation detail.
*
* @param $value
* @param $term
*
* @return bool
*/
static function shouldKeep($value, $term): bool;
}
/**
* Filterer that returns `true` if the `$value` contains the given `$term`.
*/
class ContainsStringFilterer implements FieldFilterer
{
/**
* @inheritDoc
*
* @param string $value
* @param string $term
*
* @return bool
*/
static function shouldKeep($value, $term): bool
{
return strpos($value, $term) !== FALSE;
}
}
/**
* Filterer that never returns `true`, meaning nothing is kept.
*/
class NeverFilterer implements FieldFilterer
{
static function shouldKeep($value, $term): bool
{
return false;
}
}
/**
* Filterer that always returns `true`, meaning everything is kept.
*/
class AlwaysFilterer implements FieldFilterer
{
static function shouldKeep($value, $term): bool
{
return true;
}
}
class FilterRules
{
private $collections = [
'events' => [
'tags' => ContainsStringFilterer::class
]
];
/**
* Gets all the fields that are configured with `FieldFilterer`s for the given `$collection`
*
* @param string $collection
*
* @return FieldFilterer[]
*/
public function getFilterableFields(string $collection): array
{
if (!isset($this->collections[$collection])) {
return [];
}
return $this->collections[$collection];
}
/**
* Filters the `$entities` of the named `$collection` based on the provided `$terms`,
* using filterers configured for specific fields.
*
* @param string $collection the name of the collection.
* @param array &$entities the entities to filter.
* @param array $filters an array of terms associated with the field they should be compared to.
*/
public function filterEntities(string $collection, array &$entities, array $filters): void
{
// only use the filterers for fields that are in `$filters`
$filterers = array_filter(
$this->getFilterableFields($collection),
function (string $field) use ($filters) {
return isset($filters[$field]);
},
ARRAY_FILTER_USE_KEY
);
foreach ($entities as $key => $entity) {
if (!$this->shouldKeepEntity($entity, $filterers, $filters)) {
unset($entities[$key]);
}
}
}
/**
* Checks if the given `entity` should be kept, based on the result of all the `$filterers`.
*
* @param $entity
* @param FieldFilterer[] $filterers an array of `FieldFilterer`s associated with the field they be compared to.
* @param array $filters an array of terms associated with the field they should be compared to.
*
* @return bool
*/
private function shouldKeepEntity($entity, array $filterers, array $filters): bool
{
foreach ($filterers as $field => $filterer) {
if (!isset($filters[$field])) {
// trigger_error maybe ...?
continue;
} // just in case
if (!$filterer::shouldKeep($entity[$field], $filters[$field])) {
return false;
}
}
return true;
}
}
$filterRules = new FilterRules();
// remove the filter terms we want to filter against, otherwise cockpit won't include them in after for us
$app->on('collections.find.before', function (string $collection, array &$options) use ($filterRules) {
/*
array(1) {
["filter"]=>
array(2) {
["status"]=>
string(9) "Published"
["tags"]=>
string(12) "Storytelling"
}
}
*/
if (!isset($options['filter'])) {
return;
}
foreach ($filterRules->getFilterableFields($collection) as $field => $filterer) {
unset($options['filter'][$field]);
}
});
// filters the $entities further based on defined FilterRules
$app->on('collections.find.after', function (string $collection, array &$entities) use ($filterRules) {
/*
array(2) {
["token"]=>
string(30) "<private>"
["filter"]=>
array(2) {
["status"]=>
string(9) "Published"
["tags"]=>
string(12) "Storytelling"
}
}
*/
if (!isset($_REQUEST['filter'])) {
return;
}
$filters = $_REQUEST['filter'];
$filterRules->filterEntities($collection, $entities, $filters);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment