API Platform version(s) affected: v2.5.4
Description
Hi, I have 2 entities Booking and Department that has many to one relationship.
Booking entity
/**
* @ApiResource(
* itemOperations={
* "get"={
* "normalization_context"={
* "groups"={"read-item"}
* }
* },
* "put"={
* "denormalization_context"={
* "groups"={"put"}
* }
* }
* }
* @ORM\Entity(repositoryClass="App\Repository\BookingRepository")
class Booking
{
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Department", inversedBy="bookings")
* @ORM\JoinColumn(nullable=false)
* @Assert\NotBlank
* @Groups({"read-item", "put"})
*/
private $department;
}
Department entity
/**
* @ApiResource(
* collectionOperations={"get"},
* itemOperations={"get"}
* )
* @ORM\Entity(repositoryClass="App\Repository\DepartmentRepository")
*/
class Department
{
/**
* @ORM\OneToMany(targetEntity="App\Entity\Booking", mappedBy="department")
*/
private $bookings;
}
When I send a post request (to create a new booking ) with a request body
{
"department": ""
}
I was expecting a validation error but I got the below error
{
"@context": "/api/contexts/Error",
"@type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "Invalid IRI \"\".",
"trace": [
{
"namespace": "",
"short_class": "",
"class": "",
"type": "",
"function": "",
"file": "/var/www/html/vendor/api-platform/core/src/Serializer/AbstractItemNormalizer.php",
"line": 418,
"args": []
},
{
"namespace": "ApiPlatform\\Core\\Serializer",
"short_class": "AbstractItemNormalizer",
"class": "ApiPlatform\\Core\\Serializer\\AbstractItemNormalizer",
"type": "->",
"function": "denormalizeRelation",
"file": "/var/www/html/vendor/api-platform/core/src/Serializer/AbstractItemNormalizer.php",
"line": 685,
"args": [
[
"string",
"department"
],
[
"object",
"ApiPlatform\\Core\\Metadata\\Property\\PropertyMetadata"
],
[
"string",
"App\\Entity\\Department"
],
[
"string",
""
],
[
"string",
"json"
],
[
"array",
{
"operation_type": [
"string",
"collection"
],
"collection_operation_name": [
"string",
"post"
],
"api_allow_update": [
"boolean",
false
],
"resource_class": [
"string",
"App\\Entity\\Department"
],
"input": [
"null",
null
],
"output": [
"null",
null
],
"request_uri": [
"string",
"/api/bookings"
],
"uri": [
"string",
"http://localhost/api/bookings"
],
"skip_null_values": [
"boolean",
true
],
"api_denormalize": [
"boolean",
true
],
"cache_key": [
"string",
"db505854694e53aec6831eb427a03028"
]
}
]
]
},
}
Please may i ask as why it's not throwing a validation error like
```
{
"propertyPath": "department",
"message": "This value should not be blank."
}
Because validation occurs after deserialization. The serializer takes your empty value when denormalizing relation, but as it is invalid iri so it throws.
@norkunas Many Thanks. I've now added a custom denormaliser that removes the empty values and this fixed the issue :)
services.yml
App\Serializer\EmptyValueDenormalizer:
arguments: [ '@api_platform.serializer.normalizer.item' ]
tags:
- { name: serializer.normalizer, priority: 18 }
custom denormaliser
namespace App\Serializer;
use ApiPlatform\Core\Serializer\ItemNormalizer;
use App\Entity\Booking;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
/**
* Class EmptyValueDenormalizer
*
* @package App\Serializer
*/
class EmptyValueDenormalizer implements DenormalizerInterface
{
/**
* @var ItemNormalizer
*/
private $decorated;
/**
* EmptyValuesDenormalizer constructor.
*
* @param ItemNormalizer $decorated
*/
public function __construct(ItemNormalizer $decorated)
{
$this->decorated = $decorated;
}
/**
* @param mixed $data
* @param string $type
* @param null $format
* @param array $context
* @return array|object|null
*
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
*/
public function denormalize($data, $type, $format = null, array $context = [])
{
$data = $this->removeEmptyValues($data, $type);
return $this->decorated->denormalize($data, $type, $format, $context);
}
/**
* @param $data
* @param $type
* @return mixed
*/
private function removeEmptyValues($data, $type)
{
if ($type == Booking::class) {
if (array_key_exists('pickupTime', $data) && ($data['pickupTime'] == ''|| is_null($data['pickupTime']))) {
unset($data['pickupTime']);
}
if (array_key_exists('department', $data) && ($data['department'] == ''|| is_null($data['department']))) {
unset($data['department']);
}
}
return $data;
}
/**
* @inheritDoc
*/
public function supportsDenormalization($data, $type, $format = null)
{
return $this->decorated->supportsDenormalization($data, $type, $format);
}
}
Most helpful comment
Because validation occurs after deserialization. The serializer takes your empty value when denormalizing relation, but as it is invalid iri so it throws.