check24 / apitk-url-bundle
此包为RESTful API提供过滤、排序和分页功能
Requires
- php: ^7.4 || ^8.0
- check24/apitk-common-bundle: ^2.2 || ^3.0
- check24/apitk-header-bundle: ^2.2 || ^3.0
- doctrine/annotations: ^1.8
- nelmio/api-doc-bundle: ^3.4
- sensio/framework-extra-bundle: ^5.1 || ^6.0
- symfony/config: >=5.3 <6.0
- symfony/dependency-injection: >=5.3 <6.0
- symfony/framework-bundle: >=5.3 <6.0
- symfony/http-kernel: >=5.3 <6.0
Requires (Dev)
- captainhook/captainhook: ^5.0
- captainhook/plugin-composer: ^5.1
- doctrine/doctrine-bundle: >=1.8 <3.0
- doctrine/orm: ^2.6
- friendsofphp/php-cs-fixer: ^3.0
- phpmd/phpmd: ^2.6
- phpstan/phpstan: ^0.12.0
- phpstan/phpstan-deprecation-rules: ^0.12.5
- roave/security-advisories: dev-latest
- dev-master
- 3.0.3
- 3.0.2
- 3.0.1
- 3.0.0
- 2.3.0
- 2.2.0
- 2.1.0
- 2.0.6
- 2.0.5
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0.0
- 1.0.15
- 1.0.14
- 1.0.13
- 1.0.12
- 1.0.10
- 1.0.9
- 1.0.7
- 1.0.6
- 1.0.5
- 1.0.4
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
- 0.9.0
- 0.1.0
- dev-dependabot/composer/symfony/http-kernel-5.4.20
- dev-feature/symfony-6
- dev-feature/php8-annotations
- dev-bugfix/bc-query-all
- dev-develop
- dev-feature/like-filter
- dev-bugfix/service-repository
- dev-bugfix/adjust-docu-for-repo
- dev-bugfix/filter-neq-value-null
This package is auto-updated.
Last update: 2024-09-05 01:53:10 UTC
README
安装
使用composer安装包
composer require check24/apitk-url-bundle
用法
定义可能的过滤器、排序和分页
过滤
您可以在操作注解中指定哪些字段应该由客户端进行过滤
use Shopping\ApiTKUrlBundle\Annotation as ApiTK;
/**
* Returns the users in the system.
*
* @Rest\Get("/v1/users")
* @Rest\View()
*
* @ApiTK\Filter(name="username")
* @ApiTK\Filter(name="created", allowedComparisons={"gt","lt"})
* @ApiTK\Filter(name="active", enum={"true","false"})
* @ApiTK\Filter(name="country", queryBuilderName="a.country")
*
* @return User[]
*/
使用allowedComparisons
和enum
选项限制用户的可能性。
如果您想使用内置的查询构建器应用程序,并且实体字段名与过滤器字段名不同(例如,因为它是一个带有别名的连接表中的字段),请使用queryBuilderName
选项。
客户端现在可以调用API端点,并带有过滤器选项,如GET /v1/users?filter[created][gt]=2018-01-01&filter[country][in]=DE,AT
。如果客户端指定了无效的过滤器(未配置/允许的字段或比较),客户端将收到400响应。
您还可以使用路由参数进行过滤输入。只需声明与路由占位符同名的过滤器即可。
/**
* Returns the addresses for the given user.
*
* @Rest\Get("/v1/users/{id}/addresses")
* @Rest\View()
*
* @ApiTK\Filter(name="id", queryBuilderName="u.id")
*
* @return Address[]
*/
排序
您可以在操作注解中指定哪些字段应该由客户端进行排序
use Shopping\ApiTKUrlBundle\Annotation as ApiTK;
/**
* Returns the users in the system.
*
* @Rest\Get("/v1/users")
* @Rest\View()
*
* @ApiTK\Sort(name="username")
* @ApiTK\Sort(name="zipcode", queryBuilderName="a.zipCode", allowedDirections={"asc"})
*
* @return User[]
*/
使用allowedDirections
选项限制用户的可能性(例如,如果您只想支持升序排序)。
如果您想使用内置的查询构建器应用程序,并且实体字段名与排序字段名不同(例如,因为它是一个带有别名的连接表中的字段),请使用queryBuilderName
选项。
客户端现在可以调用API端点,并带有排序选项,如GET /v1/users?sort[zipcode]=asc&sort[username]=desc
。如果客户端指定了无效的排序(未配置/允许的字段或方向),客户端将收到400响应。
分页
您可以在操作注解中指定结果是否应该由客户端进行分页
use Shopping\ApiTKUrlBundle\Annotation as ApiTK;
/**
* Returns the users in the system.
*
* @Rest\Get("/v1/users")
* @Rest\View()
*
* @ApiTK\Pagination
* Or:
* @ApiTK\Pagination(maxEntries=25)
*
* @return User[]
*/
如果您想限制客户端每页获取的项目数量,您可以指定maxEntries
选项。但请只添加一个Pagination
注解(不要像上面的示例那样)。
客户端现在可以调用API端点,并带有限制选项,如GET /v1/users?limit=10
(获取前10个条目)或GET /v1/users?limit=30,10
(获取带有偏移量30(=第4页)的10个条目)。如果客户端尝试分页而您没有启用,或者客户端想要获取比您指定的更多的项目,客户端将收到400响应。
此外,响应中还发送了一个新的头x-apitk-pagination-total
,其中包含条目的总数,以便客户端可以调整其分页按钮。
访问客户端输入
通过参数转换器自动加载数组
您可以通过轻松注入到您的操作中,自动获取所有过滤器在正确顺序和分页子集中实体结果数组。
首先在您的doctrine.yaml
中将默认存储库设置为我们的ApiToolkitRepository
。
doctrine: orm: default_repository_class: Shopping\ApiTKUrlBundle\Repository\ApiToolkitRepository
对于所有自定义存储库,从ApiToolkitRepository
扩展它们(或者如果您想使它们可注入,从ApiToolkitServiceRepository
扩展,它从ServiceEntityRepository
扩展,因此请确保以正确的方式实现构造函数)以便它们也获得所需的功能。
之后,将一个 @ApiTK\Result
注解添加到您的控制器操作中。操作参数将自动填充给定实体存储库的过滤、排序和分页结果集。
//ItemController.php
/**
* @ApiTK\Filter(name="name")
* @ApiTK\Sort(name="name")
* @ApiTK\Pagination
*
* @ApiTK\Result("items", entity="App\Entity\Item")
*/
public function getItems(array $items)
{
return $items;
}
如果您需要为联接的实体中的字段进行过滤/排序,只需在自定义实体存储库中定义自己的 findByRequest()
方法。
//UserRepository.php
use Shopping\ApiTKUrlBundle\Repository\ApiToolkitRepository;
class UserRepository extends ApiToolkitRepository
{
public function findByRequest(ApiService $apiService): array
{
$qb = $this->createQueryBuilder('u');
$qb->leftJoin('u.addresses', 'a')->distinct();
$apiService->applyToQueryBuilder($qb);
return $qb->getQuery()->getResult();
}
}
//UserController.php
/**
* @ApiTK\Filter(name="username")
* @ApiTK\Filter(name="country", queryBuilderName="a.country")
* @ApiTK\Pagination
*
* @ApiTK\Result("users", entity="App\Entity\User")
*/
public function getUsers(array $users)
{
return $users;
}
如果您需要为存储库添加不同的方法,这些方法可以通过 Result
注解执行,那么您可以将 methodName="findBySomethingElse"
参数添加到注解中。它将然后在您的存储库中查找此方法,而不是默认的 findByResult()
方法。请确保只接受 ApiService
作为唯一参数。
因此,这也可行
//UserRepository.php
use Shopping\ApiTKUrlBundle\Repository\ApiToolkitRepository;
class UserRepository extends ApiToolkitRepository
{
public function findBarBaz(ApiService $apiService): array
{
// your own logic
}
}
//UserController.php
/**
* @ApiTK\Result("users", entity="App\Entity\User", methodName="findBarBaz")
*/
public function getUsers(array $users)
{
return $users;
}
注意:如果您的注解中启用了分页器,则查询将通过您的存储库中的 applyToQueryBuilder()
方法执行以确定总计数。请确保在构建查询的其他部分之后调用此方法。
如果您需要使用默认以外的实体管理器/连接,可以通过添加 entityManager="foobar"
到您的注解中指定实体管理器。
手动访问
如果您必须实现带有过滤、排序和分页的定制逻辑,您还可以注入 ApiService
并使用其方法。
//UserController.php
public function getUsersV1(EntityManagerInterface $entityManager, ApiService $apiService)
{
$users = $entityManager->getRepository(User::class)->findAll();
//Filtering
if ($apiService->hasFilteredField('username')) {
$usernameFilter = $apiService->getFilteredField('username');
$users = array_filter($users, function($user) use ($usernameFilter) {
if ($usernameFilter->getComparison() === ApiTK\Filter::COMPARISON_EQUALS) {
return $user->getUsername() === $usernameFilter->getValue();
}
return false;
});
}
/*...*/
//Sorting
foreach (array_reverse($apiService->getSortedFields()) as $sortField) {
if ($sortField->getName() === 'username') {
usort($users, function($user1, $user2) use ($sortField) {
if ($sortField->getDirection === ApiTK\Sort::ASCENDING) {
return $user1->getUsername() <=> $user2->getUsername();
} else {
return $user2->getUsername() <=> $user1->getUsername();
}
});
}
/*...*/
}
//Pagination
$apiService->setPaginationTotal(count($users));
$users = array_slice($users, $apiService->getPaginationOffset(), $apiService->getPaginationLimit());
return $users;
}
手动实现一些过滤器
如果您有一些需要一些自定义逻辑的“虚拟”字段,您可以使用 applyToQueryBuilder 并
仍然手动定义您的字段。
假设您想要实现一个搜索参数。这个搜索将查看用户名和电子邮件。
//UserController.php
/**
* @ApiTK\Filter(name="search", autoApply=false)
*
* @ApiTK\Result("users", entity="App\Entity\User")
*/
public function getUsers(array $users)
{
return $users;
}
为了使参数可用,您需要注册一个新的 Filter
字段。必须为此过滤器设置 autoApply=false
,因为没有“搜索”字段在实体上,我们希望我们自己组装这部分查询。
//UserRepository.php
use Shopping\ApiTKUrlBundle\Repository\ApiToolkitRepository;
class UserRepository extends ApiToolkitRepository
{
public function findByRequest(ApiService $apiService): array
{
$qb = $this->createQueryBuilder('u');
if ($apiService->hasFilteredField('search')) {
$search = $apiService->getFilteredField('search');
$qb->andWhere(
$qb->expr()->orX(
$qb->expr()->like('u.username', ':query'),
$qb->expr()->like('u.email', ':query')
)
)->setParameter('query', '%' . $search . '%');
}
$apiService->applyToQueryBuilder($qb);
return $qb->getQuery()->getResult();
}
}
applyToQueryBuilder
将跳过我们的 autoApply=false
字段,因此我们可以自己添加它。
重要:当使用分页器时,在手动过滤后调用 $apiService->applyToQueryBuilder()
方法,以便分页器可以在过滤后的结果上构建总计数标头。否则,您的标头中会有不正确的值。
手动实现排序
与手动过滤属性相同,您可以像上面那样实现手动排序
//UserController.php
/**
* @ApiTK\Sort(name="mySortProperty", autoApply=false)
*
* @ApiTK\Result("users", entity="App\Entity\User")
*/
public function getUsers(array $users)
{
return $users;
}
//UserRepository.php
use Shopping\ApiTKUrlBundle\Repository\ApiToolkitRepository;
class UserRepository extends ApiToolkitRepository
{
public function findByRequest(ApiService $apiService): array
{
$qb = $this->createQueryBuilder('u');
$apiService->applyToQueryBuilder($qb);
if ($apiService->hasSortedField('mySortProperty')) {
// perform your own sorting logic for this field
}
return $qb->getQuery()->getResult();
}
}
对于更高级的用途,请参阅上面的“手动访问”部分。
文档
定义的过滤器、排序和分页将自动添加到 NelmioApiDoc 输出(即 Swagger UI)中。您不必担心这一点。