- Basic HTML templates

- Created entities & new migration
- Added packages symfony/stimulus-bundle & symfony/webpack-encore-bundle
This commit is contained in:
2023-10-14 02:11:28 -06:00
parent e8df5bf019
commit 3ed53ac05e
33 changed files with 9009 additions and 42 deletions
+262
View File
@@ -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;
}
}
+9 -1
View File
@@ -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');
}
}
+229
View File
@@ -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
View File
@@ -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;
}
}
+184
View File
@@ -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;
}
}
+67
View File
@@ -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;
}
}
+117
View File
@@ -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;
}
}
+48
View File
@@ -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()
// ;
// }
}
+48
View File
@@ -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()
// ;
// }
}
+48
View File
@@ -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()
// ;
// }
}
+73
View File
@@ -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;
}
}