I am struggling to use plain identifiers in API Platform. I have related entities and have followed the official documentation of API Platform, creating a Denormalizer, but I always get the error Unable to generate an IRI for the item of type "App\Entity\Address
My goal is to work with plain IDs instead of IRIs (later I want to switch to UUIDs).
Current Request Body (POST)
{
"name": "Test",
"address": {
"street": "Test",
"zip": "11111",
"city": "Test",
"country": "Test",
"type": "/api/address_types/1" //Address Type ID
},
"cover": "/api/medias/17" //Cover ID
"type": "/api/stores/1" // Store Type ID
}
Future Request Body (I have currently problem with this)
{
"name": "Test",
"address": {
"street": "Test",
"zip": "11111",
"city": "Test",
"country": "Test",
"type": 1 //Address Type ID
},
"cover": 17 //Cover ID
"type": 1 // Store Type ID
}
StoreEntity (Getters and Setters were not included in this example)
<?php
#[ORMEntity(repositoryClass: StoreRepository::class)]
#[ApiResource(
operations: [
new GetCollection(normalizationContext: ['groups' => 'store_collection_read']),
new Get(normalizationContext: ['groups' => 'store_item_read']),
new Post(denormalizationContext: ['groups' => 'store:write']),
new Put(denormalizationContext: ['groups' => 'store:write']),
new Patch(denormalizationContext: ['groups' => 'store:write']),
new Delete()
]
)]
class Store
{
#[ORMId]
#[ORMGeneratedValue]
#[ORMColumn]
#[Groups(['store_collection_read', 'store_item_read'])]
private ?int $id = null;
#[ORMColumn(length: 255)]
#[Groups(['store_collection_read', 'store_item_read', 'store:write'])]
private ?string $name = null;
#[ORMColumn(length: 255, nullable: true)]
#[Groups(['store_item_read', 'store:write'])]
private ?string $description = null;
#[ORMOneToOne(cascade: ['persist', 'remove', 'refresh'])]
#[Groups(['store_collection_read', 'store_item_read', 'store:write'])]
private ?Address $address = null;
#[ORMManyToOne(targetEntity: Media::class)]
#[ORMJoinColumn(nullable: true)]
#[ApiProperty(types: ['https://schema.org/image'])]
#[Groups(['store_collection_read', 'store_item_read', 'store:write'])]
public ?Media $cover = null;
#[ORMManyToOne]
#[Groups(['store_item_read', 'store:write', 'refresh'])]
private ?StoreType $type = null;
Address Entity (Getters and Setters were not included in this example)
class Address
{
#[ORMId]
#[ORMGeneratedValue]
#[ORMColumn]
#[Groups(['address:read', 'store_collection_read', 'store_item_read', 'store:write'])]
private ?int $id = null;
#[ORMColumn(length: 255)]
#[Groups(['address:read', 'address:write', 'store_collection_read', 'store_item_read', 'store:write'])]
private ?string $street = null;
#[ORMColumn(length: 255, nullable: true)]
#[Groups(['address:read', 'address:write', 'store_collection_read', 'store_item_read', 'store:write'])]
private ?string $additionalAddress = null;
#[ORMColumn(length: 100)]
#[Groups(['address:read', 'address:write', 'store_collection_read', 'store_item_read', 'store:write'])]
private ?string $zip = null;
#[ORMColumn(length: 100)]
#[Groups(['address:read', 'address:write', 'store_collection_read', 'store_item_read', 'store:write'])]
private ?string $city = null;
#[ORMColumn(length: 100)]
#[Groups(['address:read', 'address:write', 'store_collection_read', 'store_item_read', 'store:write'])]
private ?string $country = null;
#[ORMManyToOne(inversedBy: 'addresses')]
#[Groups(['address:read', 'address:write', 'store_item_read', 'store:write'])]
private ?AddressType $type = null;
PlainIdentifierDenormalizer
class PlainIdentifierDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface
{
use DenormalizerAwareTrait;
private IriConverterInterface $iriConverter;
private LoggerInterface $logger;
public function __construct(IriConverterInterface $iriConverter, LoggerInterface $logger)
{
$this->iriConverter = $iriConverter;
$this->logger = $logger;
}
public function denormalize($data, $class, $format = null, array $context = []): mixed
{
if (isset($data['address']['type'])) {
$data['address']['type'] =
$this
->iriConverter
->getIriFromResource(
resource: Address::class,
context: ['uri_variables' => ['id' => $data['address']['type']]]
);
}
if (isset($data['cover'])) {
$data['cover'] =
$this
->iriConverter
->getIriFromResource(
resource: Media::class,
context: ['uri_variables' => ['id' => $data['cover']]]
);
}
if (isset($data['type'])) {
$data['type'] =
$this
->iriConverter
->getIriFromResource(
resource: StoreType::class,
context: ['uri_variables' => ['id' => $data['type']]]
);
}
return $this->denormalizer->denormalize($data, $class, $format, $context + [__CLASS__ => true]);
}
public function supportsDenormalization($data, $type, $format = null, array $context = []): bool
{
return in_array($format, ['json', 'jsonld'], true) && is_a($type, Store::class, true) && !isset($context[__CLASS__]);
}
public function getSupportedTypes(?string $format): array
{
return [
'object' => null,
'*' => false,
Store::class => true
];
}
}
It seems that the problem is in wrong resource class here
$data['address']['type'] =
$this
->iriConverter
->getIriFromResource(
resource: Address::class,
context: ['uri_variables' => ['id' => $data['address']['type']]]
);
It should be AddressType
instead of Address