Reputation: 1
I'm trying to define an entity with multiple endpoints using Api Platform. When I try to define multiple Get or GetCollection endpointd, it returns a 404 error. Looking for the reason, the only conclusion I can find was a bad uriTemplate definition, but does not make sense to me.
I try the next and it work, but I don't understand why.
If I define the endpoint like /sport_centers_top_rated it works If I define the endpoint like /sport_centers/top_rated it does not work.
I can don't understand why.
Here is my class definition:
<?php
namespace App\Entity\App;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use App\Controller\App\SportCenter\SportCenterCreateController;
use App\Controller\App\SportCenter\SportCenterDeleteController;
use App\Controller\App\SportCenter\SportCenterReadController;
use App\Controller\App\SportCenter\SportCenterUpdateController;
use App\Entity\Master\City;
use App\Repository\App\SportCenterRepository;
use App\Traits\Entity\DatetimeTrait;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Groups;
#[ORM\Table(schema: 'app')]
#[ORM\Entity(repositoryClass: SportCenterRepository::class)]
#[ORM\HasLifecycleCallbacks]
#[ApiResource(
operations: [
new Get(
name: 'sport_center:read'
),
new Get(
uriTemplate: '/sport_centers/{id}/sports',
controller: SportCenterReadController::class,
name: 'sport_center:read:sport_types'
),
new Get(
uriTemplate: '/sport_centers/{id}/reserves_availability',
controller: SportCenterReadController::class,
name: 'sport_center:read:reserves_availability'
),
new GetCollection(
uriTemplate: '/sport_centers',
controller: SportCenterReadController::class,
name: 'sport_center:read:list'
),
new GetCollection(
uriTemplate: '/sport_centers/top_reserves',
controller: SportCenterReadController::class,
normalizationContext: [
'groups' => [
'sport_center:read:top_rated'
]
],
name: 'sport_center:read:list:top_reserves'
),
new Post(
uriTemplate: '/sport_centers',
controller: SportCenterCreateController::class,
name: 'sport_center:write:create'
),
new Put(
uriTemplate: '/sport_centers/{id}',
controller: SportCenterUpdateController::class,
denormalizationContext: [
'groups' => [
'sport_center:write:update',
]
],
name: 'sport_center:write:update'
),
new Delete(
uriTemplate: '/sport_centers/{id}',
controller: SportCenterDeleteController::class,
name: 'sport_center:write:delete'
),
],
normalizationContext: [
'groups' => [
'sport_center:read',
]
],
denormalizationContext: [
'groups' => [
'sport_center:write',
]
]
)]
class SportCenter
{
use DatetimeTrait;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(
groups: [
'sport_center:read',
'court:write',
'court:read',
'reserve:read',
'reserve:read:search_reserves',
'city:read'
]
)]
private int $id;
#[ORM\Column(length: 255)]
#[Groups(
groups: [
'sport_center:write',
'sport_center:read',
'court:read',
'sport_center:write:update',
'reserve:read',
'reserve:read:search_reserves',
'city:read'
]
)]
private string $name;
#[ORM\Column(length: 255)]
#[Groups(
groups: [
'sport_center:write',
'sport_center:read',
'court:read',
'reserve:read',
'sport_center:write:update',
'city:read'
]
)]
private string $street;
#[ORM\Column(length: 255)]
#[Groups(
groups: [
'sport_center:write',
'sport_center:read',
'court:read',
'sport_center:write:update',
]
)]
private string $postalCode;
#[ORM\OneToMany(mappedBy: 'sportCenter', targetEntity: Court::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[Groups(
groups: [
'sport_center:read',
'sport_center:read:details:admin'
]
)]
private Collection $courts;
#[ORM\ManyToOne(inversedBy: 'sportCenters')]
#[ORM\JoinColumn(nullable: false)]
private User $admin;
#[ORM\ManyToOne(inversedBy: 'sportCenters')]
#[Groups(
groups: [
'sport_center:write',
'sport_center:read',
'court:read',
'sport_center:write:update',
'reserve:read',
'reserve:read:search_reserves'
]
)]
private City $city;
public function __construct()
{
$this->courts = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getStreet(): ?string
{
return $this->street;
}
public function setStreet(string $street): self
{
$this->street = $street;
return $this;
}
public function getPostalCode(): ?string
{
return $this->postalCode;
}
public function setPostalCode(string $postalCode): self
{
$this->postalCode = $postalCode;
return $this;
}
/**
* @return Collection<int, Court>
*/
public function getCourts(): Collection
{
return $this->courts;
}
public function addCourt(Court $court): self
{
if (!$this->courts->contains($court)) {
$this->courts->add($court);
$court->setSportCenter($this);
}
return $this;
}
public function removeCourt(Court $court): self
{
if ($this->courts->removeElement($court)) {
// set the owning side to null (unless already changed)
if ($court->getSportCenter() === $this) {
$court->setSportCenter(null);
}
}
return $this;
}
public function getAdmin(): ?User
{
return $this->admin;
}
public function setAdmin(?User $admin): self
{
$this->admin = $admin;
return $this;
}
public function getCity(): ?City
{
return $this->city;
}
public function setCity(?City $city): static
{
$this->city = $city;
return $this;
}
public function getSportTypes(): array
{
$sports = [];
foreach ($this->courts as $court) {
$sports[] = $court->getSport()->value;
}
return array_unique($sports);
}
#[Groups([
'sport_center:read'
])]
public function getNCourts()
{
return sizeof($this->getCourts());
}
}
Upvotes: 0
Views: 307
Reputation: 1096
You should add a requirement the first GET operation :
new Get(
uriTemplate: '/sport_centers/{id}',
name: 'sport_center:read',
requirements: {"id"="\d+"}
),
So a call to "sport_centers/top_rated " won't go through this route and the right operation will apply.
As a side note you can debug your routing with this command :
php bin/console router:match /sport_centers/top_rated --method=GET
You will see that you go through the first operation. As there is no sport center with id = top_rated, you get a not found as expected.
Upvotes: 0