youcanshop/queryoption

帮助在查询中进行过滤/搜索/排序的包

安装: 7,374

依赖: 0

建议者: 0

安全性: 0

星标: 8

关注者: 2

分支: 0

开放性问题: 0

类型:项目

v0.1.3 2023-05-11 10:27 UTC

This package is auto-updated.

Last update: 2024-09-26 15:04:24 UTC


README

Query Option package logo
QueryOption

Tests Total Downloads License

此包可以帮助您将HTTP查询数据作为对象而不是通过应用程序的不同层传递数组来操作。

使用方法

在控制器内部,我们通常从请求中提取参数,然后将它们发送到我们的服务,再发送到存储库以执行搜索、排序或过滤。

class ListUsersController as Controller
{
    private UserService $userService;

    public function __construct(UserService $userService) {
        $this->userService = $userService;
    }

    public function __invoke(Request $request) {
        $queryOption = QueryOptionFactory::createFromIlluminateRequest($request);

        $this->userService->paginate($queryOption);
    }
}

从上面的示例中,QueryOptionFactory 帮助创建包含用于搜索、排序和过滤所需的值的 QueryOption 对象。由于在这个示例中我们有一个Laravel应用程序,我们可以使用一个特定的工厂方法直接从 Request 对象创建。

URL 参数

由于QueryOption包解析URL参数,我们将以下解释参数名称

查询选项

QueryOption 是连接所有其他组件的粘合剂。

$queryOption->getSort();
$queryOption->getFilters();
$queryOption->getSearch();

对于大多数应用程序,每个控制器都有在特定上下文(管理员与普通用户)中允许的一组过滤器。在这种情况下,您可以在控制器内部使用 allowedFilters() 方法来限制在错误上下文中传递过滤器。

一个例子是列出博客文章。管理员可以查看所有文章,而普通用户只能看到已发布的文章。

class AdminPostsController {
    private PostService $postService;

    public function __construct(PostService $postService) {
        $this->postService = $postService;
    }

    public function __invoke(Request $request) {
        $queryOption = QueryOptionFactory::createFromIlluminateRequest($request);

        $posts = $this->postService->paginate($queryOption);

        // return posts
    }
}
class UserPostsController {
    private PostService $postService;

    public function __construct(PostService $postService) {
        $this->postService = $postService;
    }

    public function __invoke(Request $request) {
        $queryOption = QueryOptionFactory::createFromIlluminateRequest($request);

        // explicitly specify the filter names allowed. 
        $queryOption->allowedFilters(['published_date', 'author']);

        $posts = $this->postService->paginate($queryOption);

        // return posts
    }
}

Laravel 桥接器

对于Laravel应用程序,您可以在 config/app.php 中添加Query Option提供者。

<?php

use YouCanShop\QueryOption\Laravel\QueryOptionProvider;

return [
    // ...
    
    'providers' => [
        // ...
        QueryOptionProvider::class,    
    ],

    // ...
];

之后,您可以受益于以下助手:

$queryOption = $request->queryOption();

在您的存储库中,您可以使用我们所说的 标准。以下是如何工作的示例

<?php

use YouCanShop\QueryOption\Laravel\UsesQueryOption;

class PostRepository {
    use UsesQueryOption;

    public function paginated(QueryOption $queryOption)
    {
        $query = Post::query();

        [$query, $queryOption] = $this->pipeThroughCriterias($query, $queryOption);

        return $query->paginate(
            $queryOption->getLimit(),
            '*',
            'page',
            $queryOption->getPage()
        );
    }

    protected function getQueryOptionCriterias(): array
    {
        return [
            SearchCriteria::class,
            FilterByPublishedAtCriteria::class,
            SortByCriteria::class
        ];
    }
}

因此,调用 paginate() 方法将传递查询通过标准列表,以添加每个定义的查询选项所需的逻辑。然后,我们返回修改后的查询实例以继续分页。

以下是每个标准内部的代码,以说明其工作方式。

class SearchCriteria
{
    public function handle(array $data, Closure $next)
    {
        [$query, $queryOption] = $data;

        $search = $queryOption->getSearch();
        if (!empty($search->getTerm())) {
            if ($search->getType() === 'like') {
                $query->where('title', 'like', "%" . $search->getTerm() . "%");
            }

            if ($search->getType() === 'equal') {
                $query->where('title', '=', $search->getTerm());
            }
        }
        
        return $next([$query, $queryOption]);
    }
}

搜索标准在非空时使用 likeequal 进行搜索,并返回 $query$queryOption 以供下一个标准。

class SortByCriteria
{
    public function handle(array $data, Closure $next)
    {
        [$query, $queryOption] = $data;

        $sort = $queryOption->getSort();

        // allow sorting only by publish date and title
        if(!in_array($sort->getField(), ['published_date','title'])) {
            return $next([$query, $queryOption]);
        }

        $query->orderBy($sort->getField(), $sort->getDirection());

        return $next([$query, $queryOption]);
    }
}

在这个示例中,排序非常直接。一个重要的事情是防止使用不允许的字段进行排序。

class FilterByPublishedAtCriteria
{
    public function handle(array $data, Closure $next)
    {
        [$query, $queryOption] = $data;

        $filter = $queryOption->getFilters()->findByName('publish_date');
        
        $creationDate = Carbon::parse($filter->getValue());
        $query->whereDate('published_at', $filter->getOperator(), $creationDate);

        return $next([$query, $queryOption]);
    }
}

就是这样,分页将考虑按发布日期过滤、按标题搜索和按发布日期排序。