В настоящее время я обнаруживаю API-Plateform с Symfony, и я хочу создать собственный фильтр.
Я создал объекты, описанные в документации: у меня есть объект Book иПросмотрите сущность, и я хочу, чтобы мой пользовательский фильтр возвращал книги со средним рейтингом, превосходящим или равным данному значению.
Мне удалось сделать следующее в своем классе 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 Filter, но следующее возвращает все книги:
// 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 и установил его в свойстве «reviews» (это немне это кажется чрезвычайно логичным, но если я не установлю его для свойства, фильтр не появится в моем API):
* @ApiFilter(RatingFilter::class, properties={"reviews"})
Затем я проверил свой фильтр, чтобы получить книги со средним рейтингом 3 илиподробнее.
Только 4 из 10 книг имеют средний рейтинг более 3/5. Но API возвращает все мои книги.
curl -X GET "http://127.0.0.1:8000/api/books?averageRatingMoreThan=3" -H "accept: application/ld+json"
Любое указание, почемуэто работает в моем репозитории бно не как фильтр, пожалуйста?