diff --git a/src/Controller/Blog/PostController.php b/src/Controller/Blog/PostController.php index 73755bf..e5b8336 100644 --- a/src/Controller/Blog/PostController.php +++ b/src/Controller/Blog/PostController.php @@ -4,14 +4,17 @@ namespace App\Controller\Blog; use App\Core\Annotation\UrlGenerator; use App\Core\Controller\Site\PageController; +use App\Core\Manager\EntityManager; use App\Core\Site\SiteRequest; +use App\Core\Site\SiteStore; use App\Entity\Blog\Category; use App\Entity\Blog\Post; +use App\Factory\Blog\CommentFactory; +use App\Form\Blog\UserCommentType; use App\Repository\Blog\PostRepositoryQuery; use App\UrlGenerator\PostGenerator; -use Symfony\Component\HttpFoundation\Response; -use App\Core\Site\SiteStore; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; class PostController extends PageController { @@ -27,8 +30,13 @@ class PostController extends PageController /** * @UrlGenerator(service=PostGenerator::class, method="post") */ - public function post(Post $post, string $slug): Response - { + public function post( + Post $post, + string $slug, + CommentFactory $commentFactory, + Request $request, + EntityManager $entityManager + ): Response { if (Post::DRAFT === $post->getStatus() && !$this->getUser()) { throw $this->createNotFoundException(); } @@ -43,8 +51,31 @@ class PostController extends PageController ); } + $form = $this->createForm(UserCommentType::class, $commentFactory->create($post)); + + if ($request->isMethod('POST')) { + $form->handleRequest($request); + + if ($form->isValid()) { + $data = $request->request->get($form->getName()); + $parentCommentId = (int) $data['parentCommentId']; + + foreach ($post->getComments(['id' => $parentCommentId]) as $comment) { + $form->getData()->setParentComment($comment); + } + + $entityManager->create($form->getData()); + + $this->addFlash('success', 'Commentaire publié !'); + + return $this->redirect($request->getUri()); + } + $this->addFlash('error', 'Le formulaire n\'est pas valide.'); + } + return $this->defaultRender('blog/post/post.html.twig', [ 'post' => $post, + 'form' => $form->createView(), ]); } diff --git a/src/Entity/Blog/Comment.php b/src/Entity/Blog/Comment.php index e43263c..d9a0d92 100644 --- a/src/Entity/Blog/Comment.php +++ b/src/Entity/Blog/Comment.php @@ -3,14 +3,15 @@ namespace App\Entity\Blog; use App\Core\Doctrine\Timestampable; +use App\Core\Entity\EntityInterface; use App\Repository\Blog\CommentRepository; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use App\Core\Entity\EntityInterface; /** * @ORM\Entity(repositoryClass=CommentRepository::class) + * @ORM\HasLifecycleCallbacks */ class Comment implements EntityInterface { @@ -56,6 +57,7 @@ class Comment implements EntityInterface /** * @ORM\ManyToOne(targetEntity=Comment::class, inversedBy="comments") + * @ORM\JoinColumn(nullable=true, onDelete="SET NULL") */ private $parentComment; @@ -159,11 +161,21 @@ class Comment implements EntityInterface } /** - * @return Collection|self[] + * @return Collection|Comment[] */ - public function getComments(): Collection + public function getComments(array $criteria = []): Collection { - return $this->comments; + $collection = new ArrayCollection(); + + foreach ($this->comments as $comment) { + if (isset($criteria['isActive']) && $comment->getIsActive() !== $criteria['isActive']) { + continue; + } + + $collection->add($comment); + } + + return $collection; } public function addComment(self $comment): self @@ -187,4 +199,12 @@ class Comment implements EntityInterface return $this; } + + /** + * Get the avatar URL using gravatar. + */ + public function getAvatar(): string + { + return 'https://secure.gravatar.com/avatar/'.md5($this->getEmail()).'.jpg?s=90&d=retro'; + } } diff --git a/src/Entity/Blog/Post.php b/src/Entity/Blog/Post.php index 6d9c9df..f4b0776 100644 --- a/src/Entity/Blog/Post.php +++ b/src/Entity/Blog/Post.php @@ -4,7 +4,6 @@ namespace App\Entity\Blog; use App\Core\Doctrine\Timestampable; use App\Core\Entity\EntityInterface; -use App\Entity\User; use App\Repository\Blog\PostRepository; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; @@ -23,6 +22,9 @@ class Post implements EntityInterface { use Timestampable; + const DRAFT = 0; + const PUBLISHED = 1; + /** * @ORM\Id * @ORM\GeneratedValue @@ -110,9 +112,6 @@ class Post implements EntityInterface */ private $comments; - const DRAFT = 0; - const PUBLISHED = 1; - public function __construct() { $this->categories = new ArrayCollection(); @@ -343,9 +342,27 @@ class Post implements EntityInterface /** * @return Collection|Comment[] */ - public function getComments(): Collection + public function getComments(array $criteria = []): Collection { - return $this->comments; + $collection = new ArrayCollection(); + + foreach ($this->comments as $comment) { + if (isset($criteria['isActive']) && $comment->getIsActive() !== $criteria['isActive']) { + continue; + } + + if (isset($criteria['isFirstLevel']) && $criteria['isFirstLevel'] !== (null === $comment->getParentComment())) { + continue; + } + + if (isset($criteria['id']) && $comment->getId() !== $criteria['id']) { + continue; + } + + $collection->add($comment); + } + + return $collection; } public function addComment(Comment $comment): self diff --git a/src/EventSuscriber/Blog/CommentEventSubscriber.php b/src/EventSuscriber/Blog/CommentEventSubscriber.php new file mode 100644 index 0000000..5292287 --- /dev/null +++ b/src/EventSuscriber/Blog/CommentEventSubscriber.php @@ -0,0 +1,54 @@ + + */ +class CommentEventSubscriber extends EntityManagerEventSubscriber +{ + protected MailNotifier $notifier; + protected UrlGeneratorInterface $urlGenerator; + + public function __construct(MailNotifier $notifier, UrlGeneratorInterface $urlGenerator) + { + $this->notifier = $notifier; + $this->urlGenerator = $urlGenerator; + } + + public function support(EntityInterface $entity) + { + return $entity instanceof Comment; + } + + public function onCreate(EntityManagerEvent $event) + { + if (!$this->support($event->getEntity())) { + return; + } + + $this->notifier + ->init() + ->addRecipient('simon@deblan.fr') + ->setSubject('[Deblan] Nouveau commentaire') + ->notify('mail/comment.html.twig', [ + 'post' => $event->getEntity()->getPost(), + 'links' => [ + 'post' => $this->urlGenerator->generate('blog_menu_post', [ + 'post' => $event->getEntity()->getPost()->getId(), + 'slug' => $event->getEntity()->getPost()->getSlug(), + ], UrlGeneratorInterface::ABSOLUTE_URL).'#review-'.$event->getEntity()->getId(), + ], + ], 'text/plain') + ; + } +} diff --git a/src/Factory/Blog/CommentFactory.php b/src/Factory/Blog/CommentFactory.php new file mode 100644 index 0000000..520f143 --- /dev/null +++ b/src/Factory/Blog/CommentFactory.php @@ -0,0 +1,25 @@ + + */ +class CommentFactory +{ + public function create(Post $post): Comment + { + $entity = new Comment(); + + $entity + ->setPost($post) + ->setIsActive(true); + + return $entity; + } +} diff --git a/src/Form/Blog/UserCommentType.php b/src/Form/Blog/UserCommentType.php new file mode 100644 index 0000000..b72674b --- /dev/null +++ b/src/Form/Blog/UserCommentType.php @@ -0,0 +1,93 @@ +add( + 'author', + TextType::class, + [ + 'required' => true, + 'label' => 'Auteur', + 'attr' => [ + ], + 'constraints' => [ + new NotBlank(), + ], + ] + ); + + $builder->add( + 'website', + UrlType::class, + [ + 'required' => false, + 'label' => 'Site web', + 'attr' => [ + ], + 'constraints' => [ + new Url(), + ], + ] + ); + + $builder->add( + 'email', + EmailType::class, + [ + 'label' => 'E-mail (non publié)', + 'required' => false, + 'attr' => [ + ], + 'constraints' => [ + new Email(), + ], + ] + ); + + $builder->add( + 'content', + TextareaType::class, + [ + 'label' => 'Commentaire', + 'required' => true, + 'attr' => [ + ], + 'constraints' => [ + new NotBlank(), + ], + ] + ); + + $builder->add( + 'parentCommentId', + HiddenType::class, + [ + 'mapped' => false, + ] + ); + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => Comment::class, + ]); + } +} diff --git a/templates/blog/post/_comment.html.twig b/templates/blog/post/_comment.html.twig new file mode 100644 index 0000000..db3ebbf --- /dev/null +++ b/templates/blog/post/_comment.html.twig @@ -0,0 +1,46 @@ +{% set level = min(7, level) %} +{% set col = 12 - (level - 1) %} + +
+ +
+ +{% set level = level + 1 %} + +{% for child in comment.comments %} + {{ include('blog/post/_comment.html.twig', {level: level, comment: child}) }} +{% endfor %} + diff --git a/templates/blog/post/_post.html.twig b/templates/blog/post/_post.html.twig index a8b892d..87aceb1 100644 --- a/templates/blog/post/_post.html.twig +++ b/templates/blog/post/_post.html.twig @@ -97,24 +97,26 @@ {% if full %} - {#

- {% set hasActiveComments = post.hasActiveComments(true) %} + {% set comments = post.comments({ + isActive: true, + isFirstLevel: true + }) %} - {% if hasActiveComments %} + {% if comments|length %}
- {% for comment in post.orderedComments(true) %} - {{ include('DeblanBlogBundle:skin2018:_comment.html.twig', {comment: comment, level: 1}) }} + {% for comment in comments %} + {{ include('blog/post/_comment.html.twig', {comment: comment, level: 1}) }} {% endfor %}
{% endif %}
-
- {% if hasActiveComments %} + + {% if comments|length %}
{% endif %} @@ -142,7 +144,7 @@

{{- 'Votre commentaire - Vous pouvez utiliser du markdown ' }} - [?] + [?]

{{ form_errors(form.content) }} @@ -164,11 +166,6 @@
-
- {{ form_errors(form.follow) }} - {{ form_widget(form.follow) }} - {{ form_label(form.follow) }} -
@@ -187,7 +184,6 @@
- #} {% if not post.isQuick %} {% diff --git a/templates/mail/comment.html.twig b/templates/mail/comment.html.twig new file mode 100644 index 0000000..e09928a --- /dev/null +++ b/templates/mail/comment.html.twig @@ -0,0 +1,4 @@ +Un nouveau commentaire a été déposé sur l'article suivant : + + {{ post.title|raw }} + {{ links.post }}