FiltersTrait.php

<?php

namespace LiaisonTrait;


/**
 * - declare and document more filters (Should I document them here or in the place where they're declared? Yes.)
 * - Document examples
 * 
 * @export(TODO.Filters)
 */
trait Filters {


    protected $filters = [];
    protected $filterSorters = [];
    
    /**
     * Remove unwanted array items with custom filter functions.  
     * To execute the filter do `$filteredItems = $lia->filter($filterName, array $itemsToFilter, ...$extraParamaters);`
     * 
     * 
     * See the [Filters Test](/test/Filters.php) for a thorough example.
     * 
     * @export(Usage.Filters.Execute)
     */
    public function filter($name, array $filterable, ...$extraArgs){
        $filters = $this->filters[$name] ?? null;
        if ($filters==null)return $filterable;
        $objs = [];
        foreach ($filters as $filter){
            $obj = new \Lia\Utility\FancyClosure($filter['callable'], $filter['bound']);
            $objs[] = $obj;
        }
        $filters = $objs;

        if (($origCount=count($filters))>1
            &&  ($sorter=
                    ($this->filterSorters[$name] ?? false)
                )
            ){
            //call the sorter
            $call = $sorter['callable'];
            $bound = $sorter['bound'];
            $args = array_merge($bound, [$filters]);
            // print_r($args);
            // exit;
            $filters = $call(...$args);
            if (($newCount = count($filters))!=$origCount){
                throw new \Exception("Your filter sorter function for '{$name}' MUST return the same number of filters as it was passed.
                Original count was '{$origCount}' and you return '{$newCount}'.");
            }
        }
        foreach ($filters as $f){
            $args = array_merge([$filterable], $extraArgs);
            $filterable = $f(...$args);
        }
        return $filterable;
    }
    /**
     * Routes might frequently need some filtering that can't be reliably provided by liaison, such as if you declare dynamic routes that sometimes conflict with static routes.
     * 
     * Your filter function can accept bound arguments prior to the filterable array of items. See the [Filters Test](8-build/test/Filters.php) for a full example.
     * Example:  
     * ```php
     * $priorityBaseUrl = '/blog/';
     * $lia->addFilter(Router.routes, [$yourObject, 'removeDynamicRoutes'], $priorityBaseUrl);
     * //any args after the callable will be passed to your callback before the filterable array of items and subsequent paramaters that are passed when filter() is called
     * ```
     * Then `removeDynamicRoutes` would be:
     * ```php
     * public function removeDynamicRoutes($priorityBaseUrl, array $routes, $url){
     *     if ($url does-not-start-with $priorityBaseUrl)return $routes; //We will only filter for '/blog/' urls
     *     $ret = [];
     *     foreach ($routes as $r){
     *         if ($r is-a-dynamic-route)continue;
     *         $ret[] = $r;
     *     }
     *     if (count($ret)>0)return $ret;
     *     else return $routes;
     * }
     * ```
     * 
     * @export(Usage.Filters.Add)
     */
    public function addFilter($name, $callable, ...$boundArgs){
        $this->filters[$name][] = ['callable'=>$callable,'bound'=>$boundArgs];
    }
    /**
     * In case you have added multiple filters for the same name, you can sort the order in which they execute.  
     * Your callback will be like `function(...$boundArgs, array $filterObjects)`  
     * - The $filterObjects will be an array of `\Lia\Utility\FancyClosure` objects
     * And you add the filter with `$lia->sortFilter($name, $callback, ...$boundArgs)`  
     * You don't have to pass bound args.
     * 
     * See the [Filters Test](8-build/test/Filters.php) for an example.
     * 
     * @export(Usage.Filters.Sort)
     */
    public function sortFilter($name, $callable, ...$boundArgs){
        $sorters = $this->filterSorters;
        if (isset($sorters[$name]))throw new \Exception("You can only have one sorter for each filter name. '{$name}' is already taken.");
        
        $this->filterSorters[$name] = ['callable'=>$callable, 'bound'=>$boundArgs];
    }

}