<?php
/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace ApiPlatform\Core\Filter;
use ApiPlatform\Core\Api\FilterLocatorTrait;
use ApiPlatform\Core\Exception\FilterValidationException;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Util\RequestAttributesExtractor;
use Psr\Container\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
/**
* Validates query parameters depending on filter description.
*
* @author Julien Deniau <julien.deniau@gmail.com>
*/
final class QueryParameterValidateListener
{
use FilterLocatorTrait;
private $resourceMetadataFactory;
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, ContainerInterface $filterLocator)
{
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->setFilterLocator($filterLocator);
}
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
if (
!$request->isMethodSafe()
|| !($attributes = RequestAttributesExtractor::extractAttributes($request))
|| !isset($attributes['collection_operation_name'])
|| 'get' !== ($operationName = $attributes['collection_operation_name'])
) {
return;
}
$resourceMetadata = $this->resourceMetadataFactory->create($attributes['resource_class']);
$resourceFilters = $resourceMetadata->getCollectionOperationAttribute($operationName, 'filters', [], true);
$errorList = [];
foreach ($resourceFilters as $filterId) {
if (!$filter = $this->getFilter($filterId)) {
continue;
}
foreach ($filter->getDescription($attributes['resource_class']) as $name => $data) {
if (!($data['required'] ?? false)) { // property is not required
continue;
}
if (!$this->isRequiredFilterValid($name, $request)) {
$errorList[] = sprintf('Query parameter "%s" is required', $name);
}
}
}
if ($errorList) {
throw new FilterValidationException($errorList);
}
}
/**
* Test if required filter is valid. It validates array notation too like "required[bar]".
*/
private function isRequiredFilterValid(string $name, Request $request): bool
{
$matches = [];
parse_str($name, $matches);
if (!$matches) {
return false;
}
$rootName = (string) (array_keys($matches)[0] ?? null);
if (!$rootName) {
return false;
}
if (\is_array($matches[$rootName])) {
$queryParameter = $request->query->all()[$rootName] ?? null;
return \is_array($queryParameter) && isset($queryParameter[array_keys($matches[$rootName])[0]]);
}
return isset($request->query->all()[$rootName]);
}
}