我目前正在使用Symfony发现API-Plateform,并且想创建一个自定义过滤器。
我创建了文档中描述的实体:我有一个Book实体和一个Review实体,并且我希望我的自定义过滤器返回平均评分高于或等于给定值的书籍。
我设法在BookRepository类中执行以下操作:
// src/Repository/BookRepository.php
/**
* @return Book[] Returns an array of Book objects
*/
public function findWithAverageRating($value)
{
/*
* HERE IS THE EQUIVALENT IN SQL :
*
* SELECT book.id, book.title, AVG(review.rating) AS average_rating
* FROM book INNER JOIN review
* ON book.id = review.book_id
* GROUP BY book.id
* HAVING AVG(review.rating) >= 3
*/
$qb = $this->createQueryBuilder('b')
->join('b.reviews', 'r');
$qb->having($qb->expr()->avg('r.rating').' >= :rating')
->setParameter('rating', $value)
->orderBy('b.id', 'ASC')
->groupBy('b.id')
;
// var_dump($qb->getQuery()->getSql());
return $qb->getQuery()->getResult();
}
现在,我想作为Api筛选器执行以下操作,但是以下操作将返回所有书籍:
// src/Filter/RatingFilter.php
namespace App\Filter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractContextAwareFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Doctrine\ORM\QueryBuilder;
final class RatingFilter extends AbstractContextAwareFilter
{
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
{
// otherwise filter is applied to order and page as well
if (
!$this->isPropertyEnabled($property, $resourceClass) ||
!$this->isPropertyMapped($property, $resourceClass)
) {
return;
}
$parameterName = $queryNameGenerator->generateParameterName($property); // Generate a unique parameter name to avoid collisions with other filters
$queryBuilder->join('o.reviews', 'r');
$queryBuilder->having($queryBuilder->expr()->avg('r.%s').' >= :%s', $property, $parameterName)
->setParameter($parameterName, $value)
// ->orderBy('b.id', 'ASC')
// ->groupBy('b.id')
;
}
// This function is only used to hook in documentation generators (supported by Swagger and Hydra)
public function getDescription(string $resourceClass): array
{
if (!$this->properties) {
return [];
}
$description = [];
foreach ($this->properties as $property => $strategy) {
$description["rating_$property"] = [
'property' => $property,
'type' => 'string',
'required' => false,
'swagger' => [
'description' => 'Filter by average rating (rating goes from 0 to 5).',
'name' => 'averageRatingMoreThan',
'type' => 'number',
],
];
}
return $description;
}
}
我在Book实体中启用了过滤器,并将其设置在“评论”属性上(这对我来说似乎并不合逻辑,但是如果我未在属性上设置过滤器,则该过滤器不会出现在我的API中) :
* @ApiFilter(RatingFilter::class, properties={"reviews"})
然后我测试了我的过滤器,以获取平均评分为3或更高的图书。
在10本书中,只有4本书的平均评分超过3 /5。但是API会返回我的所有图书。
curl -X GET "http://127.0.0.1:8000/api/books?averageRatingMoreThan=3" -H "accept: application/ld+json"
为什么它可以在我的存储库中使用,但不能用作过滤器?