- Basic HTML templates
- Created entities & new migration - Added packages symfony/stimulus-bundle & symfony/webpack-encore-bundle
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Repository\UserRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Exception;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Exception\RuntimeException;
|
||||
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;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use function Symfony\Component\String\u;
|
||||
|
||||
/**
|
||||
* A console command that creates users and stores them in the database.
|
||||
*
|
||||
* To use this command, open a terminal window, enter into your project
|
||||
* directory and execute the following:
|
||||
*
|
||||
* $ php bin/console app:add-user
|
||||
*
|
||||
* To output detailed information, increase the command verbosity:
|
||||
*
|
||||
* $ php bin/console app:add-user -vv
|
||||
*
|
||||
* See https://symfony.com/doc/current/console.html
|
||||
*
|
||||
* We use the default services.yaml configuration, so command classes are registered as services.
|
||||
* See https://symfony.com/doc/current/console/commands_as_services.html
|
||||
*
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
||||
*/
|
||||
#[AsCommand(
|
||||
name: 'app:add-user',
|
||||
description: 'Creates users and stores them in the database'
|
||||
)]
|
||||
final class AddUserCommand extends Command
|
||||
{
|
||||
private SymfonyStyle $io;
|
||||
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly UserPasswordHasherInterface $passwordHasher,
|
||||
private readonly ValidatorInterface $validator,
|
||||
private readonly UserRepository $users
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setHelp($this->getCommandHelp())
|
||||
// commands can optionally define arguments and/or options (mandatory and optional)
|
||||
// see https://symfony.com/doc/current/components/console/console_arguments.html
|
||||
->addArgument('display-name', InputArgument::OPTIONAL, 'The display name of the new user')
|
||||
->addArgument('password', InputArgument::OPTIONAL, 'The plain password of the new user')
|
||||
->addArgument('email', InputArgument::OPTIONAL, 'The email of the new user')
|
||||
->addOption('admin', null, InputOption::VALUE_NONE, 'If set, the user is created as an administrator')
|
||||
->addOption('owner', null, InputOption::VALUE_NONE, 'If set, the user is created as an owner')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* This optional method is the first one executed for a command after configure()
|
||||
* and is useful to initialize properties based on the input arguments and options.
|
||||
*/
|
||||
protected function initialize(InputInterface $input, OutputInterface $output): void
|
||||
{
|
||||
// SymfonyStyle is an optional feature that Symfony provides so you can
|
||||
// apply a consistent look to the commands of your application.
|
||||
// See https://symfony.com/doc/current/console/style.html
|
||||
$this->io = new SymfonyStyle($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is executed after initialize() and before execute(). Its purpose
|
||||
* is to check if some of the options/arguments are missing and interactively
|
||||
* ask the user for those values.
|
||||
*
|
||||
* This method is completely optional. If you are developing an internal console
|
||||
* command, you probably should not implement this method because it requires
|
||||
* quite a lot of work. However, if the command is meant to be used by external
|
||||
* users, this method is a nice way to fall back and prevent errors.
|
||||
*/
|
||||
protected function interact(InputInterface $input, OutputInterface $output): void
|
||||
{
|
||||
if (null !== $input->getArgument('password') && null !== $input->getArgument('email') && null !== $input->getArgument('display-name')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->io->title('Add User Command Interactive Wizard');
|
||||
$this->io->text([
|
||||
'If you prefer to not use this interactive wizard, provide the',
|
||||
'arguments required by this command as follows:',
|
||||
'',
|
||||
' $ php bin/console app:add-user display-name password email@example.com',
|
||||
'',
|
||||
'Now we\'ll ask you for the value of all the missing command arguments.',
|
||||
]);
|
||||
|
||||
// Ask for the display name if it's not defined
|
||||
$displayName = $input->getArgument('display-name');
|
||||
if (null !== $displayName) {
|
||||
$this->io->text(' > <info>Display Name</info>: '.$displayName);
|
||||
} else {
|
||||
$displayName = $this->io->ask('display Name', null, function($answer){
|
||||
$validation = $this->validator->validatePropertyValue(User::class, 'displayName', $answer);
|
||||
if($validation->count()) {
|
||||
foreach($validation as $validationError) {
|
||||
$this->io->error("{$validationError->getMessage()}");
|
||||
}
|
||||
throw new Exception("Invalid display name");
|
||||
}
|
||||
});
|
||||
$input->setArgument('display-name', $displayName);
|
||||
}
|
||||
|
||||
// Ask for the password if it's not defined
|
||||
/** @var string|null $password */
|
||||
$password = $input->getArgument('password');
|
||||
|
||||
if (null !== $password) {
|
||||
$this->io->text(' > <info>Password</info>: '.u('*')->repeat(u($password)->length()));
|
||||
} else {
|
||||
$password = $this->io->askHidden('Password (your type will be hidden)', function($answer){
|
||||
// from the CLI we don't really want to impose too many restrictions on the password
|
||||
if (empty($plainPassword)) {
|
||||
throw new InvalidArgumentException('The password can not be empty.');
|
||||
}
|
||||
|
||||
if (u($plainPassword)->trim()->length() < 6) {
|
||||
throw new InvalidArgumentException('The password must be at least 6 characters long.');
|
||||
}
|
||||
|
||||
return $answer;
|
||||
});
|
||||
$input->setArgument('password', $password);
|
||||
}
|
||||
|
||||
// Ask for the email if it's not defined
|
||||
$email = $input->getArgument('email');
|
||||
if (null !== $email) {
|
||||
$this->io->text(' > <info>Email</info>: '.$email);
|
||||
} else {
|
||||
$email = $this->io->ask('Email', null, $this->validator->validateEmail(...));
|
||||
$input->setArgument('email', $email);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is executed after interact() and initialize(). It usually
|
||||
* contains the logic to execute to complete this command task.
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$stopwatch = new Stopwatch();
|
||||
$stopwatch->start('add-user-command');
|
||||
|
||||
/** @var string $plainPassword */
|
||||
$plainPassword = $input->getArgument('password');
|
||||
|
||||
/** @var string $email */
|
||||
$email = $input->getArgument('email');
|
||||
|
||||
/** @var string $displayName */
|
||||
$displayName = $input->getArgument('display-name');
|
||||
|
||||
$isAdmin = $input->getOption('admin');
|
||||
$isOwner = $input->getOption('owner');
|
||||
|
||||
// make sure to validate the user data is correct
|
||||
$this->validateUserData($plainPassword, $email, $displayName);
|
||||
|
||||
// create the user and hash its password
|
||||
$user = new User();
|
||||
$user->setDisplayName($displayName)
|
||||
->setEmail($email)
|
||||
->setRoles([$isAdmin ? User::ROLE_ADMIN : User::ROLE_USER])
|
||||
->addRole(User::ROLE_USER);
|
||||
|
||||
if($isAdmin) {
|
||||
$user->addRole(User::ROLE_ADMIN);
|
||||
}
|
||||
|
||||
if($isOwner) {
|
||||
$user->addRole(User::ROLE_OWNER);
|
||||
}
|
||||
|
||||
// See https://symfony.com/doc/5.4/security.html#registering-the-user-hashing-passwords
|
||||
$hashedPassword = $this->passwordHasher->hashPassword($user, $plainPassword);
|
||||
$user->setPassword($hashedPassword);
|
||||
|
||||
$this->entityManager->persist($user);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$this->io->success(sprintf('%s was successfully created: %s (%s)', $isAdmin ? 'Administrator user' : 'User', $user->getDisplayName(), $user->getEmail()));
|
||||
|
||||
$event = $stopwatch->stop('add-user-command');
|
||||
if ($output->isVerbose()) {
|
||||
$this->io->comment(sprintf('New user database id: %d / Elapsed time: %.2f ms / Consumed memory: %.2f MB', $user->getId(), $event->getDuration(), $event->getMemory() / (1024 ** 2)));
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function validateUserData(string $plainPassword, string $email, string $displayName): void
|
||||
{
|
||||
// validate password and email if is not this input means interactive.
|
||||
$this->validator->validatePassword($plainPassword);
|
||||
$this->validator->validateEmail($email);
|
||||
$this->validator->validateDisplayName($displayName);
|
||||
|
||||
// check if a user with the same email already exists.
|
||||
$existingEmail = $this->users->findOneBy(['email' => $email]);
|
||||
|
||||
if (null !== $existingEmail) {
|
||||
throw new RuntimeException(sprintf('There is already a user registered with the "%s" email.', $email));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The command help is usually included in the configure() method, but when
|
||||
* it's too long, it's better to define a separate method to maintain the
|
||||
* code readability.
|
||||
*/
|
||||
private function getCommandHelp(): string
|
||||
{
|
||||
return <<<'HELP'
|
||||
The <info>%command.name%</info> command creates new users and saves them in the database:
|
||||
|
||||
<info>php %command.full_name%</info> <comment>display-name password email</comment>
|
||||
|
||||
By default the command creates regular users. To create administrator users,
|
||||
add the <comment>--admin</comment> option:
|
||||
|
||||
<info>php %command.full_name%</info> display-name password email <comment>--admin</comment>
|
||||
|
||||
If you omit any of the three required arguments, the command will ask you to
|
||||
provide the missing values:
|
||||
|
||||
# command will ask you for the email
|
||||
<info>php %command.full_name%</info> <comment>display-name password</comment>
|
||||
|
||||
# command will ask you for the email and password
|
||||
<info>php %command.full_name%</info> <comment>display-name</comment>
|
||||
|
||||
# command will ask you for all arguments
|
||||
<info>php %command.full_name%</info>
|
||||
HELP;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,11 @@ class SecurityController extends AbstractController
|
||||
#[Route('/login', name: 'app_login')]
|
||||
public function login(AuthenticationUtils $authenticationUtils): Response
|
||||
{
|
||||
// redirect already logged users to the dashboard
|
||||
if($this->getUser()) {
|
||||
return $this->redirectToRoute('app_dashboard');
|
||||
}
|
||||
|
||||
// get the login error if there is one
|
||||
$error = $authenticationUtils->getLastAuthenticationError();
|
||||
|
||||
@@ -29,6 +34,9 @@ class SecurityController extends AbstractController
|
||||
#[Route('/logout', name: 'app_logout', methods: ['GET'])]
|
||||
public function logout(Security $security): Response
|
||||
{
|
||||
return $security->logout(false);
|
||||
if($this->getUser()) {
|
||||
$security->logout(false);
|
||||
}
|
||||
return $this->redirectToRoute('app_login');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\CommentRepository;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: CommentRepository::class)]
|
||||
class Comment
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'comments')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?DomainPage $page = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'comments')]
|
||||
private ?User $user = null;
|
||||
|
||||
#[ORM\Column(type: Types::TEXT)]
|
||||
private ?string $markdown = null;
|
||||
|
||||
#[ORM\Column(type: Types::TEXT)]
|
||||
private ?string $html = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'childComments')]
|
||||
private ?self $parentComment = null;
|
||||
|
||||
#[ORM\OneToMany(mappedBy: 'parentComment', targetEntity: self::class)]
|
||||
private Collection $childComments;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $score = null;
|
||||
|
||||
#[ORM\Column(length: 32)]
|
||||
private ?string $state = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?DateTimeImmutable $createdAt = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||
private ?DateTimeInterface $updatedAt = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||
private ?DateTimeInterface $deletedAt = null;
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
private ?User $deletedByUser = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->childComments = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser(?User $user): static
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMarkdown(): ?string
|
||||
{
|
||||
return $this->markdown;
|
||||
}
|
||||
|
||||
public function setMarkdown(string $markdown): static
|
||||
{
|
||||
$this->markdown = $markdown;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHtml(): ?string
|
||||
{
|
||||
return $this->html;
|
||||
}
|
||||
|
||||
public function setHtml(string $html): static
|
||||
{
|
||||
$this->html = $html;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getParentComment(): ?self
|
||||
{
|
||||
return $this->parentComment;
|
||||
}
|
||||
|
||||
public function setParentComment(?self $parentComment): static
|
||||
{
|
||||
$this->parentComment = $parentComment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, self>
|
||||
*/
|
||||
public function getChildComments(): Collection
|
||||
{
|
||||
return $this->childComments;
|
||||
}
|
||||
|
||||
public function addChildComment(self $childComment): static
|
||||
{
|
||||
if (!$this->childComments->contains($childComment)) {
|
||||
$this->childComments->add($childComment);
|
||||
$childComment->setParentComment($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeChildComment(self $childComment): static
|
||||
{
|
||||
if ($this->childComments->removeElement($childComment)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($childComment->getParentComment() === $this) {
|
||||
$childComment->setParentComment(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getScore(): ?int
|
||||
{
|
||||
return $this->score;
|
||||
}
|
||||
|
||||
public function setScore(int $score): static
|
||||
{
|
||||
$this->score = $score;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getState(): ?string
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
public function setState(string $state): static
|
||||
{
|
||||
$this->state = $state;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTimeImmutable $createdAt): static
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUpdatedAt(): ?DateTimeInterface
|
||||
{
|
||||
return $this->updatedAt;
|
||||
}
|
||||
|
||||
public function setUpdatedAt(?DateTimeInterface $updatedAt): static
|
||||
{
|
||||
$this->updatedAt = $updatedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDeletedAt(): ?DateTimeInterface
|
||||
{
|
||||
return $this->deletedAt;
|
||||
}
|
||||
|
||||
public function setDeletedAt(?DateTimeInterface $deletedAt): static
|
||||
{
|
||||
$this->deletedAt = $deletedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDeletedByUser(): ?User
|
||||
{
|
||||
return $this->deletedByUser;
|
||||
}
|
||||
|
||||
public function setDeletedByUser(?User $deletedByUser): static
|
||||
{
|
||||
$this->deletedByUser = $deletedByUser;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPage(): ?DomainPage
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
|
||||
public function setPage(?DomainPage $page): static
|
||||
{
|
||||
$this->page = $page;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+81
-6
@@ -3,6 +3,9 @@
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\DomainRepository;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Mapping\Annotation\Timestampable;
|
||||
@@ -32,11 +35,23 @@ class Domain
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||
#[Timestampable(on: "update")]
|
||||
private ?\DateTimeInterface $updatedAt = null;
|
||||
private ?DateTimeInterface $updatedAt = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
|
||||
#[Timestampable(on: "create")]
|
||||
private ?\DateTimeInterface $createdAt = null;
|
||||
private ?DateTimeInterface $createdAt = null;
|
||||
|
||||
#[ORM\OneToMany(mappedBy: 'domain', targetEntity: Comment::class)]
|
||||
private Collection $comments;
|
||||
|
||||
#[ORM\OneToMany(mappedBy: 'domain', targetEntity: DomainPage::class)]
|
||||
private Collection $domainPages;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->comments = new ArrayCollection();
|
||||
$this->domainPages = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
@@ -103,27 +118,87 @@ class Domain
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUpdatedAt(): ?\DateTimeInterface
|
||||
public function getUpdatedAt(): ?DateTimeInterface
|
||||
{
|
||||
return $this->updatedAt;
|
||||
}
|
||||
|
||||
public function setUpdatedAt(?\DateTimeInterface $updatedAt): static
|
||||
public function setUpdatedAt(?DateTimeInterface $updatedAt): static
|
||||
{
|
||||
$this->updatedAt = $updatedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?\DateTimeInterface
|
||||
public function getCreatedAt(): ?DateTimeInterface
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(\DateTimeInterface $createdAt): static
|
||||
public function setCreatedAt(DateTimeInterface $createdAt): static
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, Comment>
|
||||
*/
|
||||
public function getComments(): Collection
|
||||
{
|
||||
return $this->comments;
|
||||
}
|
||||
|
||||
public function addComment(Comment $comment): static
|
||||
{
|
||||
if (!$this->comments->contains($comment)) {
|
||||
$this->comments->add($comment);
|
||||
$comment->setDomain($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeComment(Comment $comment): static
|
||||
{
|
||||
if ($this->comments->removeElement($comment)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($comment->getDomain() === $this) {
|
||||
$comment->setDomain(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, DomainPage>
|
||||
*/
|
||||
public function getDomainPages(): Collection
|
||||
{
|
||||
return $this->domainPages;
|
||||
}
|
||||
|
||||
public function addDomainPage(DomainPage $domainPage): static
|
||||
{
|
||||
if (!$this->domainPages->contains($domainPage)) {
|
||||
$this->domainPages->add($domainPage);
|
||||
$domainPage->setDomain($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeDomainPage(DomainPage $domainPage): static
|
||||
{
|
||||
if ($this->domainPages->removeElement($domainPage)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($domainPage->getDomain() === $this) {
|
||||
$domainPage->setDomain(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\DomainPageRepository;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: DomainPageRepository::class)]
|
||||
class DomainPage
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'domainPages')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?Domain $domain = null;
|
||||
|
||||
#[ORM\Column(type: Types::TEXT)]
|
||||
private ?string $path = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $locked = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $commentCount = null;
|
||||
|
||||
#[ORM\Column(type: Types::TEXT)]
|
||||
private ?string $title = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?DateTimeImmutable $createdAt = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||
private ?DateTimeInterface $updatedAt = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||
private ?DateTimeInterface $deletedAt = null;
|
||||
|
||||
#[ORM\OneToMany(mappedBy: 'page', targetEntity: Comment::class)]
|
||||
private Collection $comments;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->comments = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getDomain(): ?Domain
|
||||
{
|
||||
return $this->domain;
|
||||
}
|
||||
|
||||
public function setDomain(?Domain $domain): static
|
||||
{
|
||||
$this->domain = $domain;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPath(): ?string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function setPath(string $path): static
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isLocked(): ?bool
|
||||
{
|
||||
return $this->locked;
|
||||
}
|
||||
|
||||
public function setLocked(bool $locked): static
|
||||
{
|
||||
$this->locked = $locked;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCommentCount(): ?int
|
||||
{
|
||||
return $this->commentCount;
|
||||
}
|
||||
|
||||
public function setCommentCount(int $commentCount): static
|
||||
{
|
||||
$this->commentCount = $commentCount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): static
|
||||
{
|
||||
$this->title = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTimeImmutable $createdAt): static
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUpdatedAt(): ?DateTimeInterface
|
||||
{
|
||||
return $this->updatedAt;
|
||||
}
|
||||
|
||||
public function setUpdatedAt(?DateTimeInterface $updatedAt): static
|
||||
{
|
||||
$this->updatedAt = $updatedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDeletedAt(): ?DateTimeInterface
|
||||
{
|
||||
return $this->deletedAt;
|
||||
}
|
||||
|
||||
public function setDeletedAt(?DateTimeInterface $deletedAt): static
|
||||
{
|
||||
$this->deletedAt = $deletedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, Comment>
|
||||
*/
|
||||
public function getComments(): Collection
|
||||
{
|
||||
return $this->comments;
|
||||
}
|
||||
|
||||
public function addComment(Comment $comment): static
|
||||
{
|
||||
if (!$this->comments->contains($comment)) {
|
||||
$this->comments->add($comment);
|
||||
$comment->setPage($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeComment(Comment $comment): static
|
||||
{
|
||||
if ($this->comments->removeElement($comment)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($comment->getPage() === $this) {
|
||||
$comment->setPage(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\PageViewRepository;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: PageViewRepository::class)]
|
||||
class PageView
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?DomainPage $page = null;
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
private ?User $user = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?DateTimeImmutable $createdAt = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getPage(): ?DomainPage
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
|
||||
public function setPage(?DomainPage $page): static
|
||||
{
|
||||
$this->page = $page;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser(?User $user): static
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTimeImmutable $createdAt): static
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -3,22 +3,44 @@
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\UserRepository;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Mapping\Annotation\Timestampable;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ORM\Entity(repositoryClass: UserRepository::class)]
|
||||
#[ORM\Table(name: '`user`')]
|
||||
class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
{
|
||||
const ROLE_USER = 'ROLE_USER';
|
||||
const ROLE_OWNER = 'ROLE_OWNER';
|
||||
const ROLE_ADMIN = 'ROLE_ADMIN';
|
||||
const ROLE_SUPER_ADMIN = 'ROLE_SUPER_ADMIN';
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 180, unique: true)]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Email(mode: Assert\Email::VALIDATION_MODE_HTML5)]
|
||||
#[Assert\Length(max: 180)]
|
||||
#[Assert\NoSuspiciousCharacters()]
|
||||
private ?string $email = null;
|
||||
|
||||
#[ORM\Column(length: 64)]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Length(max: 64)]
|
||||
#[Assert\NoSuspiciousCharacters()]
|
||||
#[Assert\Regex(pattern: "/^[a-zA-Z0-9 _\-]+$/", message: "Display name can only contain characters [a-zA-Z0-9 _-]")]
|
||||
private ?string $displayName = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private array $roles = [];
|
||||
|
||||
@@ -28,6 +50,22 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
#[ORM\Column]
|
||||
private ?string $password = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
|
||||
#[Timestampable(on: "create")]
|
||||
private ?DateTimeInterface $createdAt = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||
#[Timestampable(on: "update")]
|
||||
private ?DateTimeInterface $updatedAt = null;
|
||||
|
||||
#[ORM\OneToMany(mappedBy: 'user', targetEntity: Comment::class)]
|
||||
private Collection $comments;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->comments = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
@@ -45,6 +83,18 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDisplayName(): ?string
|
||||
{
|
||||
return $this->displayName;
|
||||
}
|
||||
|
||||
public function setDisplayName(string $displayName): static
|
||||
{
|
||||
$this->displayName = $displayName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A visual identifier that represents this user.
|
||||
*
|
||||
@@ -74,6 +124,19 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addRole(string $role): static
|
||||
{
|
||||
if(!$this->roles) {
|
||||
$this->roles = [];
|
||||
}
|
||||
|
||||
if(!in_array($role, $this->roles)) {
|
||||
$this->roles[] = $role;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PasswordAuthenticatedUserInterface
|
||||
*/
|
||||
@@ -97,4 +160,58 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
// If you store any temporary, sensitive data on the user, clear it here
|
||||
// $this->plainPassword = null;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?DateTimeInterface
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTimeInterface $createdAt): static
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUpdatedAt(): ?DateTimeInterface
|
||||
{
|
||||
return $this->updatedAt;
|
||||
}
|
||||
|
||||
public function setUpdatedAt(?DateTimeInterface $updatedAt): static
|
||||
{
|
||||
$this->updatedAt = $updatedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, Comment>
|
||||
*/
|
||||
public function getComments(): Collection
|
||||
{
|
||||
return $this->comments;
|
||||
}
|
||||
|
||||
public function addComment(Comment $comment): static
|
||||
{
|
||||
if (!$this->comments->contains($comment)) {
|
||||
$this->comments->add($comment);
|
||||
$comment->setUser($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeComment(Comment $comment): static
|
||||
{
|
||||
if ($this->comments->removeElement($comment)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($comment->getUser() === $this) {
|
||||
$comment->setUser(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Comment;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Comment>
|
||||
*
|
||||
* @method Comment|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method Comment|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method Comment[] findAll()
|
||||
* @method Comment[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class CommentRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Comment::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return Comment[] Returns an array of Comment objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('c')
|
||||
// ->andWhere('c.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('c.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?Comment
|
||||
// {
|
||||
// return $this->createQueryBuilder('c')
|
||||
// ->andWhere('c.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\DomainPage;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<DomainPage>
|
||||
*
|
||||
* @method DomainPage|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method DomainPage|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method DomainPage[] findAll()
|
||||
* @method DomainPage[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class DomainPageRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, DomainPage::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return DomainPage[] Returns an array of DomainPage objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('d')
|
||||
// ->andWhere('d.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('d.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?DomainPage
|
||||
// {
|
||||
// return $this->createQueryBuilder('d')
|
||||
// ->andWhere('d.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\PageView;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<PageView>
|
||||
*
|
||||
* @method PageView|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method PageView|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method PageView[] findAll()
|
||||
* @method PageView[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class PageViewRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, PageView::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return PageView[] Returns an array of PageView objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('p')
|
||||
// ->andWhere('p.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('p.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?PageView
|
||||
// {
|
||||
// return $this->createQueryBuilder('p')
|
||||
// ->andWhere('p.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use function Symfony\Component\String\u;
|
||||
|
||||
/**
|
||||
* This class is used to provide an example of integrating simple classes as
|
||||
* services into a Symfony application.
|
||||
* See https://symfony.com/doc/current/service_container.html#creating-configuring-services-in-the-container.
|
||||
*
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
*/
|
||||
final class UserValidator
|
||||
{
|
||||
public function validateDisplayName(?string $displayName): string
|
||||
{
|
||||
if (empty($displayName)) {
|
||||
throw new InvalidArgumentException('The display name can not be empty.');
|
||||
}
|
||||
|
||||
if (1 !== preg_match('/^[0-9a-zA-Z _\-]+$/', $displayName)) {
|
||||
throw new InvalidArgumentException('The display name must contain only latin characters and underscores.');
|
||||
}
|
||||
|
||||
return $displayName;
|
||||
}
|
||||
|
||||
public function validatePassword(?string $plainPassword): string
|
||||
{
|
||||
if (empty($plainPassword)) {
|
||||
throw new InvalidArgumentException('The password can not be empty.');
|
||||
}
|
||||
|
||||
if (u($plainPassword)->trim()->length() < 6) {
|
||||
throw new InvalidArgumentException('The password must be at least 6 characters long.');
|
||||
}
|
||||
|
||||
return $plainPassword;
|
||||
}
|
||||
|
||||
public function validateEmail(?string $email): string
|
||||
{
|
||||
if (empty($email)) {
|
||||
throw new InvalidArgumentException('The email can not be empty.');
|
||||
}
|
||||
|
||||
if (null === u($email)->indexOf('@')) {
|
||||
throw new InvalidArgumentException('The email should look like a real email.');
|
||||
}
|
||||
|
||||
return $email;
|
||||
}
|
||||
|
||||
public function validateFullName(?string $fullName): string
|
||||
{
|
||||
if (empty($fullName)) {
|
||||
throw new InvalidArgumentException('The full name can not be empty.');
|
||||
}
|
||||
|
||||
return $fullName;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user