aldemeery/sieve 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

aldemeery/sieve

Composer 安装命令:

composer require aldemeery/sieve

包简介

A simple, clean and elegant way to filter Eloquent models.

README 文档

README

Build Status Total Downloads Latest Version License

A minimalist, ultra-lightweight package for clean, intuitive query filtering.

With Sieve, your filtration logic is simplified from something like this:

public function index(Request $request)
{
    $query = Product::query();

    if ($request->has('color')) {
        $query->where('color', $request->get('color'));
    }

    if ($request->has('condition')) {
        $query->where('condition', $request->get('condition'));
    }

    if ($request->has('price')) {
        $direction = $request->get('price') === 'highest' ? 'desc' : 'asc';
        $query->orderBy('price', $direction);
    }

    return $query->get();
}

to this:

public function index(Request $request)
{
    return Product::filter($request->query())->get();
}

Installation

Important

This package requires Laravel 11.0 or higher and PHP 8.2 or higher.

You can install the package via composer:

composer require aldemeery/sieve

Usage

Enabling filtration for a model is as easy as adding the Aldemeery\Sieve\Concerns\Filterable trait to it:

use Aldemeery\Sieve\Concerns\Filterable;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use Filterable;

    // ...
}

The Filterable trait introduces a filter local scope to your model, which accepts an associative array for filtration:

public function index(Request $request)
{
    return Product::filter($request->query())->get();
}

Now you're ready to create your filter classes.

Creating filters

To create a filter, create a class that implements the Aldemeery\Sieve\Contracts\Filter interface.

You can either create a filter class using the make:filter artisan command, which will place the filter in the app/Http/Filters directory. Alternatively, you can create a filter class manually and place it wherever you prefer:

php artisan make:filter Product/ColorFilter

This generates a ColorFilter class in the app/Filters/Product directory:

<?php

namespace App\Filters\Product;

use Aldemeery\Sieve\Contracts\Filter;
use Illuminate\Database\Eloquent\Builder;

/** @implements Filter<\App\Models\Product> */
class ColorFilter implements Filter
{
    public function map(mixed $value): mixed
    {
        return match ($value) {
            default => $value,
        };
    }

    public function apply(Builder $query, mixed $value): void
    {
        // $query->where('id', $value);
    }
}

Here, apply defines the filtration logic, while map can transform input values if needed before passing them to apply

Important

Before a value is passed to the apply method, it's first passed to the map method.

If you do not need to map values into other values, you should just leave the map method as it is.

Check out this examples:

public function map(mixed $value): mixed
{
    return match ($value) {
        'yes' => true,
        'no' => false,
        '1' => true,
        '0' => true,
        default => $value,
    };
}

public function apply(Builder $query, mixed $value): void
{
    // Assuming filter was called like this: Product::filter(['in_stock' => 'yes'])->get();
    // Or like this: Product::filter(['in_stock' => '1'])->get();
    // In both cases, $value would be `true`

    $query->where('in_stock', $value);
}

With an instance of Illuminate\Database\Eloquent\Builder passed to apply, you gain access to its full capabilities, allowing you to perform a wide range of operations:

Example 1 - Ordering:

public function apply(Builder $query, mixed $value): void
{
    $query->orderBy('price', $value);
}

Example 2 - Relations:

public function apply(Builder $query, mixed $value): void
{
    $query->whereHas('category', function($query) use ($value): void {
        $query->where('name', $value);
    });
}

Filtering

Once you have created your filters and defined your filtration logic, It's time now to actually use the filter, which can be done in two ways:

Passing a filters array:

Use this when you want to apply a filter to a single query:

public function index(Request $request)
{
    return Product::filter($request->query(), [
        // "color" here is the key to be used in the query string
        // e.g. https://example.com/products?color=red
        "color" => \App\Filters\Product\ColorFilter::class,
    ])->get();
}

In the above example, the ColorFilter is applied only for this query.

Defining model filters:

Alternatively, if you want a filter to be associated with a model and applied every time the filter method is called, you can add a filters method to your model that returns an array mapping keys to their corresponding filter classes:

<?php

namespace App\Models;

use Aldemeery\Sieve\Concerns\Filterable;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use Filterable;

    /** @return array<string, string> */
    private function filters(): array
    {
        return [
            'color' => \App\Filters\Product\ColorFilter::class,
        ];
    }
}

Now everytime you call the filter method on the model, you will have the ColorFilter applied to your query:

public function index(Request $request)
{
    // The `ColorFilter` filter is applied.
    return Product::filter($request->query())->get();
}

Important

Only filters with keys present in the data array will be applied. Any filters not included in the array will be ignored.

For instance, if your filter array includes only the color key, only the corresponding ColorFilter will be executed, while any other filters will have no effect on the query.

Mapping Values

In some cases, you may want to use more user-friendly values that do not directly correspond to the values needed for filtration. This is where the map method comes in handy.

Before any value reaches the apply method, it is first processed by the map method. This allows you to transform incoming values into something more meaningful for your application.

Example:

Imagine you want to sort products by price but using the query string, but you prefer using labels like ..?price=lowest or ..?price=highest instead of technical terms like ..?price=asc or ..?price=desc.

You can achieve this by using the map method, as shown below:

<?php

namespace App\Filters\Product;

use Aldemeery\Sieve\Contracts\Filter;
use Illuminate\Database\Eloquent\Builder;

/** @implements Filter<\App\Models\Product> */
class PriceFilter implements Filter
{
    public function map(mixed $value): mixed
    {
        return match ($value) {
            'lowest' => 'asc',
            'highest' => 'desc',
            default => $value,
        };
    }

    public function apply(Builder $query, mixed $value): void
    {
        // After mapping, $value will be 'asc' for 'lowest' and 'desc' for 'highest'.
        $query->orderBy('price', $value);
    }
}

With this implementation, you can present a more intuitive interface to users while maintaining the necessary functionality for sorting in your queries.

aldemeery/sieve 适用场景与选型建议

aldemeery/sieve 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 6.44k 次下载、GitHub Stars 达 138, 最近一次更新时间为 2019 年 03 月 06 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「search」 「filter」 「query」 「laravel」 「filters」 「eloquent」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 aldemeery/sieve 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 aldemeery/sieve 我们能提供哪些服务?
定制开发 / 二次开发

基于 aldemeery/sieve 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 6.44k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 139
  • 点击次数: 26
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 138
  • Watchers: 5
  • Forks: 2
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2019-03-06