diff --git a/features/main/validation.feature b/features/main/validation.feature index 42aa8621f05..9dabd131bdd 100644 --- a/features/main/validation.feature +++ b/features/main/validation.feature @@ -113,12 +113,12 @@ Feature: Using validations groups { "@context": "/contexts/ConstraintViolationList", "@type": "ConstraintViolationList", - "title": "An error occurred", - "description": "This value should be of type unknown.\nqux: This value should be of type string.\nfoo: This value should be of type bool.\nbar: This value should be of type int.\nuuid: This value should be of type uuid.\nrelatedDummy: This value should be of type array|string.\nrelatedDummies: This value should be of type array.", + "hydra:title": "An error occurred", + "hydra:description": "baz: This value should be of type string.\nqux: This value should be of type string.\nfoo: This value should be of type bool.\nbar: This value should be of type int.\nuuid: This value should be of type uuid.\nrelatedDummy: This value should be of type array|string.\nrelatedDummies: This value should be of type array.", "violations": [ { - "propertyPath": "", - "message": "This value should be of type unknown.", + "propertyPath": "baz", + "message": "This value should be of type string.", "code": "ba785a8c-82cb-4283-967c-3cf342181b40", "hint": "Failed to create object because the class misses the \"baz\" property." }, diff --git a/phpstan.neon.dist b/phpstan.neon.dist index efecac430bb..d1a6aa0bb23 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -82,6 +82,17 @@ parameters: path: src/JsonApi/Tests/Serializer/ItemNormalizerTest.php - '#Method GraphQL\\Type\\Definition\\WrappingType::getWrappedType\(\) invoked with 1 parameter, 0 required\.#' - '#Access to an undefined property GraphQL\\Type\\Definition\\NamedType&GraphQL\\Type\\Definition\\Type::\$name\.#' + - + message: '#Instanceof between Symfony\\Component\\Serializer\\NameConverter\\NameConverterInterface and Symfony\\Component\\Serializer\\NameConverter\\MetadataAwareNameConverter will always evaluate to false\.#' + paths: + - src/GraphQl/Serializer/SerializerContextBuilder.php + - src/GraphQl/Type/FieldsBuilder.php + - + message: '#Instanceof between Symfony\\Component\\Serializer\\NameConverter\\NameConverterInterface\|null and Symfony\\Component\\Serializer\\NameConverter\\MetadataAwareNameConverter will always evaluate to false\.#' + paths: + - src/Serializer/AbstractConstraintViolationListNormalizer.php + - src/Symfony/Validator/Serializer/ValidationExceptionNormalizer.php + # See https://github.com/phpstan/phpstan-symfony/issues/27 - message: '#^Service "[^"]+" is private.$#' diff --git a/src/Doctrine/Orm/Extension/ParameterExtension.php b/src/Doctrine/Orm/Extension/ParameterExtension.php index aa65efbe577..6ae3a00100b 100644 --- a/src/Doctrine/Orm/Extension/ParameterExtension.php +++ b/src/Doctrine/Orm/Extension/ParameterExtension.php @@ -16,6 +16,7 @@ use ApiPlatform\Doctrine\Common\ParameterValueExtractorTrait; use ApiPlatform\Doctrine\Orm\Filter\FilterInterface; use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface; +use ApiPlatform\Metadata\Exception\InvalidArgumentException; use ApiPlatform\Metadata\Operation; use ApiPlatform\State\ParameterNotFound; use Doctrine\ORM\QueryBuilder; @@ -50,9 +51,11 @@ private function applyFilter(QueryBuilder $queryBuilder, QueryNameGeneratorInter } $filter = $this->filterLocator->has($filterId) ? $this->filterLocator->get($filterId) : null; - if ($filter instanceof FilterInterface) { - $filter->apply($queryBuilder, $queryNameGenerator, $resourceClass, $operation, ['filters' => $values, 'parameter' => $parameter] + $context); + if (!$filter instanceof FilterInterface) { + throw new InvalidArgumentException(\sprintf('Could not find filter "%s" for parameter "%s" in operation "%s" for resource "%s".', $filterId, $parameter->getKey(), $operation?->getShortName(), $resourceClass)); } + + $filter->apply($queryBuilder, $queryNameGenerator, $resourceClass, $operation, ['filters' => $values, 'parameter' => $parameter] + $context); } } diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php index d2a12b7039f..f33f1e9f5f6 100644 --- a/src/Serializer/AbstractItemNormalizer.php +++ b/src/Serializer/AbstractItemNormalizer.php @@ -332,7 +332,20 @@ protected function instantiateObject(array &$data, string $class, array &$contex $missingConstructorArguments[] = $constructorParameter->name; } - $exception = NotNormalizableValueException::createForUnexpectedDataType(\sprintf('Failed to create object because the class misses the "%s" property.', $constructorParameter->name), $data, ['unknown'], $context['deserialization_path'] ?? null, true); + $attributeContext = $this->getAttributeDenormalizationContext($class, $paramName, $context); + $constructorParameterType = 'unknown'; + $reflectionType = $constructorParameter->getType(); + if ($reflectionType instanceof \ReflectionNamedType) { + $constructorParameterType = $reflectionType->getName(); + } + + $exception = NotNormalizableValueException::createForUnexpectedDataType( + \sprintf('Failed to create object because the class misses the "%s" property.', $constructorParameter->name), + null, + [$constructorParameterType], + $attributeContext['deserialization_path'] ?? null, + true + ); $context['not_normalizable_value_exceptions'][] = $exception; } } diff --git a/src/Validator/Exception/ValidationException.php b/src/Validator/Exception/ValidationException.php index fbabac5e39a..ef38c3d7ec8 100644 --- a/src/Validator/Exception/ValidationException.php +++ b/src/Validator/Exception/ValidationException.php @@ -73,7 +73,7 @@ public function __construct(string|ConstraintViolationListInterface $message = ' if ($message instanceof ConstraintViolationListInterface) { $this->constraintViolationList = $message; - parent::__construct($code ?? $this->__toString(), $previous ?? 0, $errorTitle instanceof \Throwable ? $errorTitle : null); + parent::__construct($this->__toString(), $code ?? 0, $previous); return; } diff --git a/src/Validator/Tests/Exception/ValidationExceptionTest.php b/src/Validator/Tests/Exception/ValidationExceptionTest.php index 35afcd05cfa..09c77a198f2 100644 --- a/src/Validator/Tests/Exception/ValidationExceptionTest.php +++ b/src/Validator/Tests/Exception/ValidationExceptionTest.php @@ -39,4 +39,19 @@ public function testToString(): void TXT ), $e->__toString()); } + + public function testWithPrevious(): void + { + $previous = new \Exception(); + $e = new ValidationException(new ConstraintViolationList([ + new ConstraintViolation('message 1', '', [], '', '', 'invalid'), + ]), null, $previous); + $this->assertInstanceOf(\RuntimeException::class, $e); + + $this->assertSame(str_replace(\PHP_EOL, "\n", <<__toString()); + $this->assertSame($previous, $e->getPrevious()); + } }