"No item route associated with the type" for an ApiResource that's only available as an ApiSubresource? #4631
-
Hello I'm trying to figure out how to correctly define the GET item operation for an ApiResource that should only be available as an ApiSubresource. Currently, I've defined two models: As such, two So, in the end, my GET route for address should look like Also, I do not control this underlying database, so I cannot make any changes to add unique identifiers to them. They're only unique within a Range. I've already done the following, which just gives me the expected "No item route associated with the type" error. <?php
declare(strict_types=1);
namespace App\Entity\Reporting\IPAM;
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\Reporting\IPAM\AddressRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
#[ORM\Entity(repositoryClass: AddressRepository::class, readOnly: true)]
#[ORM\Table(name: 'ipaddress')]
#[ApiResource(
collectionOperations: [],
itemOperations: [],
normalizationContext: ['address:read']
)]
class Address
{
#[ORM\Id]
#[ORM\Column(name: 'ipaddress', type: 'string')]
#[Groups(['address:read'])]
private string $address;
#[ORM\Id]
#[ORM\ManyToOne(targetEntity: Range::class)]
#[ORM\JoinColumn(name: 'iprangeid', referencedColumnName: 'iprangeid')]
#[Groups(['address:read'])]
private Range $range;
public function getAddress(): string
{
return $this->address;
}
public function getRange(): Range
{
return $this->range;
}
} <?php
declare(strict_types=1);
namespace App\Entity\Reporting\IPAM;
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiSubresource;
use App\Repository\Reporting\IPAM\RangeRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
#[ORM\Entity(repositoryClass: RangeRepository::class, readOnly: true)]
#[ORM\Table(name: 'iprange')]
#[ApiResource(
collectionOperations: ['get'],
itemOperations: ['get'],
normalizationContext: ['range:read']
)]
class Range
{
#[ORM\Id]
#[ORM\Column(name: 'iprangeid', type: 'integer')]
#[Groups(['range:read'])]
private int $id;
#[ORM\OneToMany(mappedBy: 'range', targetEntity: Address::class)]
#[ORM\JoinColumn]
#[ApiSubresource]
private Collection $addresses;
public function __construct()
{
$this->addresses = new ArrayCollection();
}
public function getId(): int
{
return $this->id;
}
public function getAddresses(): Collection
{
return $this->addresses;
}
} Any help is appreciated. :) |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
For anyone interested in this in the future, I solved it the following way:
#[ApiProperty(identifier: true)]
public function getSlug(): string
{
return str_replace('.', '-', $this->address);
} I also updated my item route to say {slug} instead of {address} like so:
# services.yaml
App\DataProvider\AddressItemDataProvider:
bind:
$dataProvider: '@api_platform.doctrine.orm.default.item_data_provider' <?php
declare(strict_types=1);
namespace App\DataProvider;
use ApiPlatform\Core\DataProvider\DenormalizedIdentifiersAwareItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use App\Entity\Reporting\IPAM\Address;
use function str_replace;
class AddressItemDataProvider implements RestrictedDataProviderInterface, DenormalizedIdentifiersAwareItemDataProviderInterface
{
private ItemDataProviderInterface $dataProvider;
public function __construct(ItemDataProviderInterface $dataProvider)
{
$this->dataProvider = $dataProvider;
}
public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): ?Address
{
return $this->dataProvider->getItem(
$resourceClass,
$this->replaceSlugIdentifier($id),
$operationName,
$context
);
}
private function replaceSlugIdentifier(array $id): array
{
if (isset($id['slug'])) {
$id['address'] = str_replace('-', '.', $id['slug']);
unset($id['slug']);
}
return $id;
}
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
{
return $resourceClass === Address::class;
}
} You need to do this because by setting the |
Beta Was this translation helpful? Give feedback.
-
There's a better way of doing that I'll document that asap. |
Beta Was this translation helpful? Give feedback.
For anyone interested in this in the future, I solved it the following way:
I set the "composite_identifier" of the "attributes" key of the ApiResource attribute to false. This forces API Platform to use my two database identifiers, "range" and "address" instead of a single default "id" composed of both.
I then set the path of the default GET item route to a custom path.