Describe entities as API Platform resources
This commit is contained in:
@@ -2,38 +2,60 @@
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata;
|
||||
use ApiPlatform\Metadata\ApiProperty;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use App\Repository\PostRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ORM\Entity(repositoryClass: PostRepository::class)]
|
||||
#[ORM\HasLifecycleCallbacks]
|
||||
#[ApiResource(
|
||||
operations: [new Metadata\Post(), new Metadata\Get(), new Metadata\Put(), new Metadata\Delete(), new Metadata\Patch()],
|
||||
normalizationContext: ['groups' => ['post:collection:read', 'post:read']],
|
||||
)]
|
||||
#[GetCollection(normalizationContext: ['groups' => ['post:collection:read']])]
|
||||
class Post
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['post:collection:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column]
|
||||
#[Groups(['post:collection:read'])]
|
||||
private ?\DateTimeImmutable $foundDate = null;
|
||||
|
||||
#[ORM\Column]
|
||||
#[ApiProperty(writable: false)]
|
||||
#[Groups(['post:collection:read'])]
|
||||
private ?\DateTimeImmutable $publicationDate = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
#[Groups(['post:collection:read'])]
|
||||
private ?float $latitude = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
#[Groups(['post:collection:read'])]
|
||||
private ?float $longitude = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
#[Groups(['post:collection:read'])]
|
||||
private ?float $altitude = null;
|
||||
|
||||
#[ORM\Column(type: Types::TEXT)]
|
||||
#[Groups(['post:read'])]
|
||||
private ?string $commentary = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'posts')]
|
||||
#[ApiProperty(readableLink: false)]
|
||||
#[Groups(['post:collection:read'])]
|
||||
private ?Species $species = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
@@ -122,4 +144,11 @@ class Post
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[ORM\PrePersist]
|
||||
#[ORM\PreUpdate]
|
||||
public function setPublicationDateValue(): void
|
||||
{
|
||||
$this->publicationDate = new \DateTimeImmutable();
|
||||
}
|
||||
}
|
||||
|
@@ -2,32 +2,45 @@
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use App\Repository\SpeciesRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ORM\Entity(repositoryClass: SpeciesRepository::class)]
|
||||
#[ApiResource(
|
||||
operations: [new Metadata\Post(), new Metadata\Get(), new Metadata\Put(), new Metadata\Delete(), new Metadata\Patch()],
|
||||
normalizationContext: ['groups' => ['species:collection:read', 'species:read']],
|
||||
)]
|
||||
#[Metadata\GetCollection(normalizationContext: ['groups' => ['species:collection:read']])]
|
||||
class Species
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['species:collection:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['species:collection:read'])]
|
||||
private ?string $scientific_name = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['species:collection:read'])]
|
||||
private ?string $vernacular_name = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['species:collection:read'])]
|
||||
private ?string $region = null;
|
||||
|
||||
/**
|
||||
* @var Collection<int, Post>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: Post::class, mappedBy: 'species')]
|
||||
#[Groups(['species:read'])]
|
||||
private Collection $posts;
|
||||
|
||||
public function __construct()
|
||||
|
@@ -2,15 +2,36 @@
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Put;
|
||||
use App\Repository\UserRepository;
|
||||
use App\State\UserPasswordHasher;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ORM\Entity(repositoryClass: UserRepository::class)]
|
||||
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_EMAIL', fields: ['email'])]
|
||||
#[UniqueEntity(fields: ['email'], message: 'There is already an account with this email')]
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(),
|
||||
new \ApiPlatform\Metadata\Post(validationContext: ['groups' => ['Default', 'user:create']], processor: UserPasswordHasher::class),
|
||||
new Get(),
|
||||
new Put(processor: UserPasswordHasher::class),
|
||||
new Patch(processor: UserPasswordHasher::class),
|
||||
new Delete(),
|
||||
],
|
||||
normalizationContext: ['groups' => ['user:read']],
|
||||
denormalizationContext: ['groups' => ['user:create', 'user:update']],
|
||||
)]
|
||||
class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
{
|
||||
#[ORM\Id]
|
||||
@@ -18,6 +39,8 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[Assert\Email]
|
||||
#[Groups(['user:read', 'user:create', 'user:update'])]
|
||||
#[ORM\Column(length: 180)]
|
||||
private ?string $email = null;
|
||||
|
||||
@@ -33,6 +56,10 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
#[ORM\Column]
|
||||
private ?string $password = null;
|
||||
|
||||
#[Assert\NotBlank(groups: ['user:create'])]
|
||||
#[Groups(['user:create', 'user:update'])]
|
||||
private ?string $plainPassword = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
@@ -99,6 +126,18 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPlainPassword(): ?string
|
||||
{
|
||||
return $this->plainPassword;
|
||||
}
|
||||
|
||||
public function setPlainPassword(?string $plainPassword): self
|
||||
{
|
||||
$this->plainPassword = $plainPassword;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UserInterface
|
||||
*/
|
||||
|
44
src/State/UserPasswordHasher.php
Normal file
44
src/State/UserPasswordHasher.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\State;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
|
||||
/**
|
||||
* @implements ProcessorInterface<User, User>
|
||||
*/
|
||||
final readonly class UserPasswordHasher implements ProcessorInterface
|
||||
{
|
||||
/**
|
||||
* @param ProcessorInterface<User, User> $processor
|
||||
* @param UserPasswordHasherInterface $passwordHasher
|
||||
*/
|
||||
public function __construct(
|
||||
private ProcessorInterface $processor,
|
||||
private UserPasswordHasherInterface $passwordHasher,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $data
|
||||
*/
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): User
|
||||
{
|
||||
if (!$data->getPlainPassword()) {
|
||||
return $this->processor->process($data, $operation, $uriVariables, $context);
|
||||
}
|
||||
|
||||
$hashedPassword = $this->passwordHasher->hashPassword(
|
||||
$data,
|
||||
$data->getPlainPassword()
|
||||
);
|
||||
$data->setPassword($hashedPassword);
|
||||
$data->eraseCredentials();
|
||||
|
||||
return $this->processor->process($data, $operation, $uriVariables, $context);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user