czim/laravel-filter

Laravel Eloquent查询过滤器,支持模块化构建过滤器

5.0.1 2024-07-30 13:26 UTC

This package is auto-updated.

Last update: 2024-08-30 13:36:22 UTC


README

Latest Version on Packagist Software License Build Status Latest Stable Version SensioLabsInsight

为Laravel提供可配置和模块化的过滤器设置。这使得使用典型的网上商店过滤器进行搜索和筛选变得容易。例如,如果您想根据产品属性、品牌名称、产品系列等筛选产品目录。

提供的标准过滤器类已设置,以将过滤器应用于给定的(Eloquent)查询构建器。此外,还提供了一个名为CountableFilter的类扩展,用于提供典型的计数,以确定应向访客显示哪些替代过滤器设置。

这不是一个即用型包,而是一个可以扩展以用于您自己特定应用的框架。

版本兼容性

变更日志

变更日志在此.

安装

通过Composer

$ composer require czim/laravel-filter

基本用法

创建一个扩展Czim\FilterData的类,并设置受保护的属性,用于验证规则$rules和这些属性的默认值$defaults。注意,$defaults是检测需要应用于查询的过滤器参数的主要手段,因此请确保所有您希望实现的过滤器参数都包含在其中。

简单地扩展您选择的(抽象)过滤器类,无论是Czim\Filter\Filter还是Czim\Filter\CountableFilter

每个类都有抽象方法必须实现,一旦所有这些方法都设置好了(见下文),您就可以简单地应用过滤器设置到查询上了

    $filterValues = [ 'attributename' => 'value', ... ];

    $filter = new SomeFilter($filterValues);

    // get an Eloquent builder query for a model
    $query = SomeEloquentModel::query();

    // apply the filter to the query
    $filteredQuery = $filter->apply($query);

    // normal get() call on the now filtered query
    $results = $filteredQuery->get();

CountableFilter有一个可以调用的额外方法

    $countResults = $filter->count();

有关可计数过滤器的更多信息,请见下文。

过滤器数据

您可以直接传递任何数组或Arrayable数据到过滤器中,它会为您创建一个FilterData对象。但是,如果您的过滤器没有重写$filterDataClass属性,那么您的过滤器将不起作用(因为没有为它设置属性和默认值,FilterData始终为空)。在您对Filter类的扩展中,您可以像这样重写属性,以便让过滤器自动创建它

    class YourFilter extends \Czim\Filter\Filter
    {
        protected $filterDataClass = \Your\FilterDataClass::class;

        ...
    }

您的FilterData类应如下所示

    class FilterDataClass extends \Czim\Filter\FilterData
    {
        // Validation rules for filter attributes passed in
        protected $rules = [
            'name'   => 'string|required',
            'brands' => 'array|each:integer',
            'before' => 'date',
            'active' => 'boolean',
        ];

        // Default values and the parameter names accessible to the Filter class
        // If (optional) filter attributes are not provided, these defaults will be used:
        protected $defaults = [
            'name'   => null,
            'brands' => [],
            'before' => null,
            'active' => true,
        ];
    }

过滤器验证规则是可选的。如果没有提供规则,验证总是通过。默认值是必需的,并定义了由过滤器应用的参数键。

然后,将数组(able)数据传递到您过滤器的构造函数中,将自动为您实例化该FilterData类。如果它是Czim\FilterData的(未修改的)扩展,它还将验证数据,并在数据不匹配您的数据类中定义的$rules时抛出异常。

或者,您可以为提供的FilterDataInterface实现自己的实现,并将其直接传递到过滤器中。

    $filter = new YourFilter( new YourFilterData($someData) );

它只需要实现接口;如果您以这种方式传递数据,数据将设置而无需任何进一步检查或验证,除非您在自己的FilterData实现中处理它。

过滤器

基本过滤器接收一个查询,并应用过滤器参数到它上面,然后再返回。注意,传入的查询对象将被修改;在过滤器修改之前不会对其进行克隆。

例如,如果您这样做

    $query = SomeModel::where('some_column', 1);

    $query = (new YourFilter([ 'name' => 'random' ]))->apply($query);

    echo $query->toSql();

您可能期望的结果类似于以下查询:select * from some_models where some_column = 1 and name LIKE '%random%'

具体的过滤器如何处理传入其构造函数的过滤数据必须在您的实现中定义。这可以通过两种主要方式完成,可以自由组合

  • 通过定义 策略(重写公共的 strategies() 方法)
  • 通过覆盖 applyParameter() 方法作为后备选项

重要:只有当参数提供的值不是空时,才会调用过滤逻辑。无论您选择哪种方法来应用过滤,它都只会应用于:! empty($value) || $value === false

策略和参数过滤器

您可以通过向过滤器添加一个策略方法来为每个过滤参数定义策略,如下所示

    protected function strategies(): array
    {
        return [
            // as a ParameterFilter instance
            'parameter_name_here' => new \Czim\Filter\ParameterFilters\SimpleString(),

            // as a ParameterFilter class string
            'another_parameter'   => \Czim\Filter\ParameterFilters\SimpleString::class,

            // as an anonymous function
            'yet_another'         => function($name, $value, $query) {
                                        return $query->where('some_column', '>', $value);
                                     },

            // as an array (passable to call_user_func())
            'and_another'         => [ $this, 'someMethodYouDefined' ],
        ];
    }

如果将具有与策略相同键名的过滤器数据传递给类,则将调用该策略方法。如上所示,有不同方式为过滤器提供可调用的方法,但所有方法都是将这些参数传递给一个函数

    /**
     * @param string          $name     the keyname of the parameter/strategy
     * @param mixed           $value    the value for this parameter set in the filter data
     * @param EloquentBuilder $query
     * @param FilterInterface $filter   the filter from which the strategy was invoked
     */
    public function apply(string $name, $value, $query);

ParameterFilter 是一个类(任何实现了 ParameterFilterInterface 的类),它可以被设置为过滤策略。当应用过滤器时,将调用该类上的 apply() 方法。如果将 ParameterFilter 作为字符串用于策略,则在应用过滤器时将其实例化。

策略也可以定义为闭包或数组(只要它们可以传递给 call_user_func())。由这个调用的方法将接收上述四个参数。

只有在未为参数定义策略时,才会调用过滤器自身的 applyParameter() 回调方法。默认情况下,将抛出异常。

此包包含一些常见的参数过滤器

  • SimpleInteger:用于查找(整数)值,可选运算符(默认为'=')
  • SimpleString:用于查找字符串值,默认使用 LIKE % + value + % 匹配
  • SimpleTranslatedString:(使用 JoinKey::Translations 作为连接键)

后备选项:applyParameter()

如果您愿意,您还可以使用后备方法来处理任何或所有可应用的参数。只需向您的过滤器类添加以下方法

    protected function applyParameter(string $name, $value, $query)
    {
        switch ($name) {

            case 'parameter_name_here':

                // your implementation of the filter ...
                return $query;

            ...
        }

        // as a safeguard, you can call the parent method,
        // which will throw exceptions for unhandled parameters
        parent::applyParameter($name, $value, $query);
    }

您可以将此方法与上述提到的策略定义自由组合。唯一的限制是,如果为参数定义了策略,则不会为该参数调用 applyParameter() 后备。

可计数过滤器

CountableFilter 是正常过滤器的扩展,它有助于编写针对特殊过滤器的代码,例如,对于在线商店,根据当前过滤选择显示相关替代品是有意义的。

例如,在一个产品目录中,您可以根据特定的品牌名称和价格范围进行筛选。在显示的过滤选项中,您可能希望显示其他品牌,访客可以对其进行筛选,但 显示具有所选价格范围内产品的品牌。目的是防止访客选择一个不同的品牌,却发现没有结果。

CountableFilters 通过使用当前设置的过滤器来生成替代选项的计数,帮助您做到这一点。例如,您有品牌 X、Y 和 Z,并且只筛选品牌 X 的产品以及给定的价格范围。可计数过滤器使您可以轻松地获取一个列表,显示有多少产品与品牌 Y 和 Z 的价格范围匹配。

要设置一个 CountableFilter,像通常设置 Filter 一样,但还需要额外配置 $countablescountStrategies()。计数策略的配置/实现方式与过滤策略类似。

CountableFilter::count() 的返回值是 Czim\CountableResults 的一个实例,这基本上是一个标准的 Laravel Collection 实例。

计数策略

可以为 CountableFilter 的每个参数定义 count() 的影响策略,就像正常过滤策略一样。

    protected function countStrategies(): array
    {
        return [
            'parameter_name_here' => new \Czim\Filter\ParameterCounters\SimpleInteger(),
            ...
        ];
    }

定义策略的方法与上面提到的 strategies() 方法类似:实例(在这种情况下为 ParameterCounters)、字符串、闭包和数组。

未定义策略的参数的默认值是 countParameter()

    /**
     * @param string          $parameter countable name
     * @param EloquentBuilder $query
     */
    protected function countParameter(string $parameter, $query)
    {
        // your implementation for each $parameter name
    }

ParameterCounters

就像 ParameterFilters 对于 Filter 一样,ParameterCounters 可以用作 CountableFilter 的 '插件'。

    protected function countStrategies(): array
    {
        return [
            'parameter_name_here' => new ParameterCounters\YourParameterCounter()
        ];
    }

ParameterCounters 必须实现 ParameterCounterInterface,具有以下方法

    /**
     * @param string                   $name
     * @param EloquentBuilder          $query
     * @param CountableFilterInterface $filter
     */
    public function count(string $name, $query, CountableFilterInterface $filter);

设置和额外内容

连接

在为过滤参数连接表时,可能会发生不同参数需要相同的连接(s)。为了防止重复连接表,Filter 类内置了一个用于处理连接的辅助工具。

    // within your applyParameter implementation
    // the first parameter is a keyname you define, see the JoinKey enum provided
    // the second parameter is an array of parameters that is passed on directly
    //     to Laravel's query builder join() method.
    $this->addJoin('keyname_for_your_join', [ 'table', 'column_a', '=', 'column_b' ]);

    // or within a ParameterFilter apply() method, call it on the filter
    $filter->addJoin( ... , [ ... ]);

添加的连接将在所有参数应用后自动应用于过滤器。

全局过滤设置

有时让全局设置影响过滤器的行为可能很有用。您可以直接在过滤器类上设置这些设置,使用 setSetting()。或者,您可以将过滤参数策略定义为 Filter::SETTING,它将在过滤器应用之前作为设置加载。

    // in your Filter class:
    protected function strategies(): array
    {
        return [
            ...

            'global_setting_name' => static::SETTING
        ];
    }

无论哪种方式设置了设置,您都可以使用 setting() 方法检查它。请注意,ParameterFilter/ParameterCounter 也会接收到 $filter 本身作为参数,并且方法是公开的。

如果没有定义设置,该设置的 setting() 方法将返回 null

示例

这里有一些使用 Filter 和 CountableFilter 类的 示例

贡献

有关详细信息,请参阅 CONTRIBUTING

致谢

许可证

MIT 许可证(MIT)。请参阅 许可证文件 获取更多信息。