feat : mise à jour de la structure du projet
This commit is contained in:
12
src/Module/Commercial/CommercialModule.php
Normal file
12
src/Module/Commercial/CommercialModule.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Commercial;
|
||||
|
||||
final class CommercialModule
|
||||
{
|
||||
public const string ID = 'commercial';
|
||||
public const string LABEL = 'Commercial';
|
||||
public const bool REQUIRED = false;
|
||||
}
|
||||
29
src/Module/Core/Application/DTO/UserOutput.php
Normal file
29
src/Module/Core/Application/DTO/UserOutput.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Application\DTO;
|
||||
|
||||
use App\Module\Core\Domain\Entity\User;
|
||||
use DateTimeImmutable;
|
||||
|
||||
final readonly class UserOutput
|
||||
{
|
||||
public function __construct(
|
||||
public int $id,
|
||||
public string $username,
|
||||
/** @var list<string> */
|
||||
public array $roles,
|
||||
public ?DateTimeImmutable $createdAt,
|
||||
) {}
|
||||
|
||||
public static function fromEntity(User $user): self
|
||||
{
|
||||
return new self(
|
||||
id: $user->getId(),
|
||||
username: $user->getUsername(),
|
||||
roles: $user->getRoles(),
|
||||
createdAt: $user->getCreatedAt(),
|
||||
);
|
||||
}
|
||||
}
|
||||
12
src/Module/Core/CoreModule.php
Normal file
12
src/Module/Core/CoreModule.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core;
|
||||
|
||||
final class CoreModule
|
||||
{
|
||||
public const string ID = 'core';
|
||||
public const string LABEL = 'Core';
|
||||
public const bool REQUIRED = true;
|
||||
}
|
||||
153
src/Module/Core/Domain/Entity/User.php
Normal file
153
src/Module/Core/Domain/Entity/User.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Domain\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Module\Core\Infrastructure\ApiPlatform\State\Processor\UserPasswordHasherProcessor;
|
||||
use App\Module\Core\Infrastructure\ApiPlatform\State\Provider\MeProvider;
|
||||
use App\Module\Core\Infrastructure\Doctrine\DoctrineUserRepository;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new Get(
|
||||
uriTemplate: '/me',
|
||||
provider: MeProvider::class,
|
||||
normalizationContext: ['groups' => ['me:read']],
|
||||
),
|
||||
new Get(
|
||||
normalizationContext: ['groups' => ['user:list']],
|
||||
),
|
||||
new GetCollection(
|
||||
normalizationContext: ['groups' => ['user:list']],
|
||||
),
|
||||
new Post(security: "is_granted('ROLE_ADMIN')", processor: UserPasswordHasherProcessor::class),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')", processor: UserPasswordHasherProcessor::class),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN')"),
|
||||
],
|
||||
denormalizationContext: ['groups' => ['user:write']],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: DoctrineUserRepository::class)]
|
||||
#[ORM\Table(name: '`user`')]
|
||||
class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['me:read', 'user:list'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 180, unique: true)]
|
||||
#[Groups(['me:read', 'user:list', 'user:write'])]
|
||||
private ?string $username = null;
|
||||
|
||||
/** @var list<string> */
|
||||
#[ORM\Column]
|
||||
#[Groups(['me:read', 'user:list', 'user:write'])]
|
||||
private array $roles = [];
|
||||
|
||||
#[ORM\Column]
|
||||
private ?string $password = null;
|
||||
|
||||
#[Groups(['user:write'])]
|
||||
private ?string $plainPassword = null;
|
||||
|
||||
#[ORM\Column(type: 'datetime_immutable')]
|
||||
private ?DateTimeImmutable $createdAt = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->createdAt = new DateTimeImmutable();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUsername(): ?string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function setUsername(string $username): static
|
||||
{
|
||||
$this->username = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserIdentifier(): string
|
||||
{
|
||||
return (string) $this->username;
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
public function getRoles(): array
|
||||
{
|
||||
$roles = $this->roles;
|
||||
$roles[] = 'ROLE_USER';
|
||||
|
||||
return array_values(array_unique($roles));
|
||||
}
|
||||
|
||||
/** @param list<string> $roles */
|
||||
public function setRoles(array $roles): static
|
||||
{
|
||||
$this->roles = $roles;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function setPassword(string $password): static
|
||||
{
|
||||
$this->password = $password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTimeImmutable $createdAt): static
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPlainPassword(): ?string
|
||||
{
|
||||
return $this->plainPassword;
|
||||
}
|
||||
|
||||
public function setPlainPassword(?string $plainPassword): static
|
||||
{
|
||||
$this->plainPassword = $plainPassword;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function eraseCredentials(): void
|
||||
{
|
||||
$this->plainPassword = null;
|
||||
}
|
||||
}
|
||||
22
src/Module/Core/Domain/Event/UserCreated.php
Normal file
22
src/Module/Core/Domain/Event/UserCreated.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Domain\Event;
|
||||
|
||||
use App\Shared\Domain\Event\DomainEventInterface;
|
||||
use DateTimeImmutable;
|
||||
|
||||
final readonly class UserCreated implements DomainEventInterface
|
||||
{
|
||||
public function __construct(
|
||||
public int $userId,
|
||||
public string $username,
|
||||
private DateTimeImmutable $occurredAt = new DateTimeImmutable(),
|
||||
) {}
|
||||
|
||||
public function occurredAt(): DateTimeImmutable
|
||||
{
|
||||
return $this->occurredAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Domain\Repository;
|
||||
|
||||
use App\Module\Core\Domain\Entity\User;
|
||||
|
||||
interface UserRepositoryInterface
|
||||
{
|
||||
public function findById(int $id): ?User;
|
||||
|
||||
public function findByUsername(string $username): ?User;
|
||||
|
||||
public function save(User $user): void;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Infrastructure\ApiPlatform\State\Processor;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Module\Core\Domain\Entity\User;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
|
||||
/**
|
||||
* @implements ProcessorInterface<User, User>
|
||||
*/
|
||||
class UserPasswordHasherProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
#[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')]
|
||||
private readonly ProcessorInterface $persistProcessor,
|
||||
private readonly UserPasswordHasherInterface $passwordHasher,
|
||||
) {}
|
||||
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
|
||||
{
|
||||
if ($data instanceof User && null !== $data->getPlainPassword()) {
|
||||
$data->setPassword(
|
||||
$this->passwordHasher->hashPassword($data, $data->getPlainPassword())
|
||||
);
|
||||
$data->eraseCredentials();
|
||||
}
|
||||
|
||||
return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Infrastructure\ApiPlatform\State\Provider;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
|
||||
/**
|
||||
* @implements ProviderInterface<object>
|
||||
*/
|
||||
class MeProvider implements ProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly \Symfony\Bundle\SecurityBundle\Security $security,
|
||||
) {}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?object
|
||||
{
|
||||
return $this->security->getUser();
|
||||
}
|
||||
}
|
||||
62
src/Module/Core/Infrastructure/Console/CreateUserCommand.php
Normal file
62
src/Module/Core/Infrastructure/Console/CreateUserCommand.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Infrastructure\Console;
|
||||
|
||||
use App\Module\Core\Domain\Entity\User;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:create-user',
|
||||
description: 'Create a new user',
|
||||
)]
|
||||
class CreateUserCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $em,
|
||||
private readonly UserPasswordHasherInterface $passwordHasher,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addArgument('username', InputArgument::REQUIRED, 'Username')
|
||||
->addArgument('password', InputArgument::REQUIRED, 'Plain password')
|
||||
->addOption('admin', null, InputOption::VALUE_NONE, 'Grant ROLE_ADMIN')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$username = $input->getArgument('username');
|
||||
$plainPassword = $input->getArgument('password');
|
||||
|
||||
$user = new User();
|
||||
$user->setUsername($username);
|
||||
$user->setPassword($this->passwordHasher->hashPassword($user, $plainPassword));
|
||||
|
||||
if ($input->getOption('admin')) {
|
||||
$user->setRoles(['ROLE_ADMIN']);
|
||||
}
|
||||
|
||||
$this->em->persist($user);
|
||||
$this->em->flush();
|
||||
|
||||
$io->success(sprintf('User "%s" created%s.', $username, $input->getOption('admin') ? ' with ROLE_ADMIN' : ''));
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
40
src/Module/Core/Infrastructure/DataFixtures/AppFixtures.php
Normal file
40
src/Module/Core/Infrastructure/DataFixtures/AppFixtures.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Infrastructure\DataFixtures;
|
||||
|
||||
use App\Module\Core\Domain\Entity\User;
|
||||
use Doctrine\Bundle\FixturesBundle\Fixture;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
|
||||
class AppFixtures extends Fixture
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UserPasswordHasherInterface $passwordHasher,
|
||||
) {}
|
||||
|
||||
public function load(ObjectManager $manager): void
|
||||
{
|
||||
$admin = new User();
|
||||
$admin->setUsername('admin');
|
||||
$admin->setRoles(['ROLE_ADMIN']);
|
||||
$admin->setPassword($this->passwordHasher->hashPassword($admin, 'admin'));
|
||||
$manager->persist($admin);
|
||||
|
||||
$alice = new User();
|
||||
$alice->setUsername('alice');
|
||||
$alice->setRoles(['ROLE_USER']);
|
||||
$alice->setPassword($this->passwordHasher->hashPassword($alice, 'alice'));
|
||||
$manager->persist($alice);
|
||||
|
||||
$bob = new User();
|
||||
$bob->setUsername('bob');
|
||||
$bob->setRoles(['ROLE_USER']);
|
||||
$bob->setPassword($this->passwordHasher->hashPassword($bob, 'bob'));
|
||||
$manager->persist($bob);
|
||||
|
||||
$manager->flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Infrastructure\Doctrine;
|
||||
|
||||
use App\Module\Core\Domain\Entity\User;
|
||||
use App\Module\Core\Domain\Repository\UserRepositoryInterface;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<User>
|
||||
*/
|
||||
class DoctrineUserRepository extends ServiceEntityRepository implements UserRepositoryInterface
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, User::class);
|
||||
}
|
||||
|
||||
public function findById(int $id): ?User
|
||||
{
|
||||
return $this->find($id);
|
||||
}
|
||||
|
||||
public function findByUsername(string $username): ?User
|
||||
{
|
||||
return $this->findOneBy(['username' => $username]);
|
||||
}
|
||||
|
||||
public function save(User $user): void
|
||||
{
|
||||
$this->getEntityManager()->persist($user);
|
||||
$this->getEntityManager()->flush();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user