youcanshop / queryoption
帮助在查询中进行过滤/搜索/排序的包
Requires
- php: ^7.4 || ^8.0
Requires (Dev)
- pestphp/pest: ^1.21
- squizlabs/php_codesniffer: ^3.6
Suggests
- illuminate/http: if you're using Laravel framework
- illuminate/pipeline: for the Laravel integration
- illuminate/support: for the Laravel integration
- symfony/http-foundation: to easilly work with http requests
README
此包可以帮助您将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]); } }
搜索标准在非空时使用 like
或 equal
进行搜索,并返回 $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]); } }
就是这样,分页将考虑按发布日期过滤、按标题搜索和按发布日期排序。