Implement comments #10
@@ -0,0 +1 @@
|
||||
import './bootstrap.js';
|
||||
|
3
assets/bootstrap.js
vendored
Normal file
3
assets/bootstrap.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { startStimulusApp } from '@symfony/stimulus-bundle';
|
||||
|
||||
const app = startStimulusApp();
|
@@ -2,7 +2,10 @@
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Comment;
|
||||
use App\Entity\Post;
|
||||
use App\Entity\User;
|
||||
use App\Form\CommentType;
|
||||
use App\Form\PostType;
|
||||
use App\Repository\PostRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -10,7 +13,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\CurrentUser;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
use Symfony\UX\Turbo\TurboBundle;
|
||||
|
||||
class PostController extends AbstractController
|
||||
{
|
||||
@@ -62,8 +67,12 @@ class PostController extends AbstractController
|
||||
#[Route('/post/{id}', name: 'app_post_show', methods: ['GET'])]
|
||||
public function show(Post $post): Response
|
||||
{
|
||||
$form = $this->createForm(CommentType::class, new Comment(), [
|
||||
'action' => $this->generateUrl('app_post_comment', ['id' => $post->getId()]),
|
||||
]);
|
||||
return $this->render('post/show.html.twig', [
|
||||
'post' => $post,
|
||||
'form' => $form,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -97,4 +106,71 @@ class PostController extends AbstractController
|
||||
|
||||
return $this->redirectToRoute('app_posts', [], Response::HTTP_SEE_OTHER);
|
||||
}
|
||||
|
||||
#[Route('/post/{id}/comment', name: 'app_post_comment', methods: ['POST'])]
|
||||
public function publishComment(Request $request, Post $post, EntityManagerInterface $entityManager, #[CurrentUser] User $user): Response
|
||||
{
|
||||
$comment = new Comment();
|
||||
$form = $this->createForm(CommentType::class, $comment);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$comment->setRelatedPost($post)
|
||||
->setAuthor($user);
|
||||
$entityManager->persist($comment);
|
||||
$entityManager->flush();
|
||||
|
||||
if (TurboBundle::STREAM_FORMAT === $request->getPreferredFormat()) {
|
||||
$request->setRequestFormat(TurboBundle::STREAM_FORMAT);
|
||||
return $this->renderBlock('comment/new.stream.html.twig', 'success_stream', ['comment' => $comment]);
|
||||
}
|
||||
}
|
||||
return $this->redirectToRoute('app_post_show', ['id' => $post->getId()], Response::HTTP_SEE_OTHER);
|
||||
}
|
||||
|
||||
#[Route('/comment/{id}/edit', name: 'app_post_comment_edit', methods: ['GET', 'POST'])]
|
||||
public function editComment(Request $request, Comment $comment, EntityManagerInterface $entityManager): Response
|
||||
{
|
||||
$form = $this->createForm(CommentType::class, $comment, [
|
||||
'action' => $this->generateUrl('app_post_comment_edit', ['id' => $comment->getId()]),
|
||||
]);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$entityManager->flush();
|
||||
|
||||
if ($comment->getRelatedPost() === null) {
|
||||
return $this->redirectToRoute('app_posts', [], Response::HTTP_SEE_OTHER);
|
||||
}
|
||||
return $this->redirectToRoute('app_post_show', [
|
||||
'id' => $comment->getRelatedPost()->getId()
|
||||
], Response::HTTP_SEE_OTHER);
|
||||
}
|
||||
|
||||
return $this->render('comment/edit.html.twig', [
|
||||
'comment' => $comment,
|
||||
'form' => $form,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/comment/{id}', name: 'app_post_comment_delete', methods: ['POST'])]
|
||||
#[IsGranted('COMMENT_EDIT', subject: 'comment')]
|
||||
public function deleteComment(Request $request, Comment $comment, EntityManagerInterface $entityManager): Response
|
||||
{
|
||||
if ($this->isCsrfTokenValid('delete'.$comment->getId(), (string) $request->getPayload()->get('_token'))) {
|
||||
$id = $comment->getId();
|
||||
$entityManager->remove($comment);
|
||||
$entityManager->flush();
|
||||
|
||||
if (TurboBundle::STREAM_FORMAT === $request->getPreferredFormat()) {
|
||||
$request->setRequestFormat(TurboBundle::STREAM_FORMAT);
|
||||
return $this->renderBlock('comment/deleted.stream.html.twig', 'success_stream', ['comment' => $id]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($comment->getRelatedPost() === null) {
|
||||
return $this->redirectToRoute('app_posts');
|
||||
}
|
||||
return $this->redirectToRoute('app_post_show', ['id' => $comment->getRelatedPost()->getId()]);
|
||||
}
|
||||
}
|
||||
|
119
src/Entity/Comment.php
Normal file
119
src/Entity/Comment.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
||||
use ApiPlatform\Metadata\ApiFilter;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use App\Repository\CommentRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: CommentRepository::class)]
|
||||
#[ORM\HasLifecycleCallbacks]
|
||||
#[ApiResource(operations: [new GetCollection()])]
|
||||
#[ApiFilter(filterClass: SearchFilter::class, properties: ['author', 'related_post'])]
|
||||
class Comment
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'comments')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?User $author = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'comments')]
|
||||
private ?Post $related_post = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?\DateTimeImmutable $created_at = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?\DateTimeImmutable $edited_at = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $content = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getAuthor(): ?User
|
||||
{
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
public function setAuthor(?User $author): static
|
||||
{
|
||||
$this->author = $author;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRelatedPost(): ?Post
|
||||
{
|
||||
return $this->related_post;
|
||||
}
|
||||
|
||||
public function setRelatedPost(?Post $related_post): static
|
||||
{
|
||||
$this->related_post = $related_post;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->created_at;
|
||||
}
|
||||
|
||||
public function setCreatedAt(\DateTimeImmutable $created_at): static
|
||||
{
|
||||
$this->created_at = $created_at;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEditedAt(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->edited_at;
|
||||
}
|
||||
|
||||
public function setEditedAt(\DateTimeImmutable $edited_at): static
|
||||
{
|
||||
$this->edited_at = $edited_at;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContent(): ?string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function setContent(string $content): static
|
||||
{
|
||||
$this->content = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[ORM\PrePersist]
|
||||
public function setCreatedAtDate(): void
|
||||
{
|
||||
if ($this->created_at === null) {
|
||||
$this->created_at = new \DateTimeImmutable();
|
||||
$this->edited_at = $this->created_at;
|
||||
}
|
||||
}
|
||||
|
||||
#[ORM\PreUpdate]
|
||||
public function setEditedAtDate(): void
|
||||
{
|
||||
$this->edited_at = new \DateTimeImmutable();
|
||||
}
|
||||
}
|
@@ -8,6 +8,8 @@ use ApiPlatform\Metadata\ApiProperty;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use App\Repository\PostRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
@@ -61,6 +63,17 @@ class Post
|
||||
#[Groups(['post:collection:read'])]
|
||||
private ?Species $species = null;
|
||||
|
||||
/**
|
||||
* @var Collection<int, Comment>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: Comment::class, mappedBy: 'related_post', fetch: 'EXTRA_LAZY')]
|
||||
private Collection $comments;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->comments = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
@@ -158,4 +171,34 @@ class Post
|
||||
$this->publicationDate = new \DateTimeImmutable();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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->setRelatedPost($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->getRelatedPost() === $this) {
|
||||
$comment->setRelatedPost(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,8 @@ use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Put;
|
||||
use App\Repository\UserRepository;
|
||||
use App\State\UserPasswordHasher;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
@@ -60,6 +62,17 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
#[Groups(['user:create', 'user:update'])]
|
||||
private ?string $plainPassword = null;
|
||||
|
||||
/**
|
||||
* @var Collection<int, Comment>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: Comment::class, mappedBy: 'author')]
|
||||
private Collection $comments;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->comments = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
@@ -146,4 +159,34 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
// If you store any temporary, sensitive data on the user, clear it here
|
||||
// $this->plainPassword = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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->setAuthor($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->getAuthor() === $this) {
|
||||
$comment->setAuthor(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
29
src/Form/CommentType.php
Normal file
29
src/Form/CommentType.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\Comment;
|
||||
use App\Entity\Post;
|
||||
use App\Entity\User;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class CommentType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('content', TextareaType::class)
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => Comment::class,
|
||||
]);
|
||||
}
|
||||
}
|
43
src/Repository/CommentRepository.php
Normal file
43
src/Repository/CommentRepository.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Comment;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Comment>
|
||||
*/
|
||||
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()
|
||||
// ;
|
||||
// }
|
||||
}
|
@@ -25,6 +25,8 @@ class PostRepository extends ServiceEntityRepository
|
||||
public function findPaginatedPosts(int $page, int $limit): Paginator
|
||||
{
|
||||
$query = $this->createQueryBuilder('p')
|
||||
->addSelect('species')
|
||||
->leftJoin('p.species', 'species')
|
||||
->setFirstResult(($page - 1) * $limit)
|
||||
->setMaxResults($limit);
|
||||
|
||||
|
36
src/Security/Voter/CommentVoter.php
Normal file
36
src/Security/Voter/CommentVoter.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Security\Voter;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
class CommentVoter extends Voter
|
||||
{
|
||||
public const EDIT = 'COMMENT_EDIT';
|
||||
|
||||
protected function supports(string $attribute, mixed $subject): bool
|
||||
{
|
||||
// replace with your own logic
|
||||
// https://symfony.com/doc/current/security/voters.html
|
||||
return $attribute === self::EDIT
|
||||
&& $subject instanceof \App\Entity\Comment;
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
||||
{
|
||||
$user = $token->getUser();
|
||||
|
||||
// if the user is anonymous, do not grant access
|
||||
if (!$user instanceof UserInterface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($subject->getAuthor() === $user) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_array('ROLE_ADMIN', $user->getRoles(), true);
|
||||
}
|
||||
}
|
@@ -12,7 +12,7 @@
|
||||
{% block importmap %}{{ importmap('app') }}{% endblock %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<body data-turbo="false">
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="{{ path('app_species_index') }}">Herbarium</a>
|
||||
|
4
templates/comment/_delete_form.html.twig
Normal file
4
templates/comment/_delete_form.html.twig
Normal file
@@ -0,0 +1,4 @@
|
||||
<form method="post" action="{{ path('app_post_comment_delete', {'id': comment.id}) }}" onsubmit="return confirm('Are you sure you want to delete this comment?');">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ comment.id) }}">
|
||||
<button class="btn btn-danger">Delete</button>
|
||||
</form>
|
17
templates/comment/comment.html.twig
Normal file
17
templates/comment/comment.html.twig
Normal file
@@ -0,0 +1,17 @@
|
||||
<turbo-frame id="comment_{{ comment.id }}">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
{{ comment.author.email }} le {{ comment.createdAt | date }}
|
||||
{% if comment.createdAt != comment.editedAt %}
|
||||
(modifié le {{ comment.editedAt | date }})
|
||||
{% endif %}
|
||||
</h5>
|
||||
<p class="card-text">{{ comment.content }}</p>
|
||||
{% if is_granted('COMMENT_EDIT', comment) %}
|
||||
<a href="{{ path('app_post_comment_edit', {'id': comment.id}) }}" class="btn btn-primary">Modifier</a>
|
||||
{{ include('comment/_delete_form.html.twig') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</turbo-frame>
|
3
templates/comment/deleted.stream.html.twig
Normal file
3
templates/comment/deleted.stream.html.twig
Normal file
@@ -0,0 +1,3 @@
|
||||
{% block success_stream %}
|
||||
<turbo-stream action="remove" target="comment_{{ comment }}"></turbo-stream>
|
||||
{% endblock %}
|
11
templates/comment/edit.html.twig
Normal file
11
templates/comment/edit.html.twig
Normal file
@@ -0,0 +1,11 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Edit Comment{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>Edit Comment</h1>
|
||||
|
||||
<turbo-frame id="comment_{{ comment.id }}">
|
||||
{{ include('post/_form.html.twig', {'button_label': 'Update'}) }}
|
||||
</turbo-frame>
|
||||
{% endblock %}
|
7
templates/comment/new.stream.html.twig
Normal file
7
templates/comment/new.stream.html.twig
Normal file
@@ -0,0 +1,7 @@
|
||||
{% block success_stream %}
|
||||
<turbo-stream action="append" targets="#comments">
|
||||
<template>
|
||||
{{ include('comment/comment.html.twig') }}
|
||||
</template>
|
||||
</turbo-stream>
|
||||
{% endblock %}
|
@@ -1,4 +1,4 @@
|
||||
{{ form_start(form) }}
|
||||
{{ form_widget(form) }}
|
||||
<button class="btn">{{ button_label|default('Save') }}</button>
|
||||
<button class="btn btn-primary">{{ button_label|default('Save') }}</button>
|
||||
{{ form_end(form) }}
|
||||
|
@@ -4,16 +4,16 @@
|
||||
|
||||
{% block body %}
|
||||
{% for post in posts.iterator %}
|
||||
<div class="card" style="width: 42rem; margin: 20px 0 50px 100px;">
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ post.species ? post.species.vernacularName : 'Post' }}</h5>
|
||||
<h5 class="card-title"><a href="{{ path('app_post_show', {id: post.id}) }}">{{ post.species ? post.species.vernacularName : 'Post' }}</a></h5>
|
||||
<h6 class="card-subtitle mb-2 text-muted">{{ post.foundDate | date("d/m/Y \\à H \\h") }}</h6>
|
||||
<p class="card-subtitle mb-2 text-muted">{{ post.latitude }}, {{ post.longitude }}, {{ post.altitude }}m</p>
|
||||
<p class="card-text">{{ post.commentary }}</p>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
28 ❤️
|
||||
128 💬
|
||||
{{ post.comments.count() }} 💬
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
@@ -43,4 +43,17 @@
|
||||
<a href="{{ path('app_post_edit', {'id': post.id}) }}">edit</a>
|
||||
|
||||
{{ include('post/_delete_form.html.twig') }}
|
||||
|
||||
<div data-turbo="true">
|
||||
<div id="comments">
|
||||
{% for comment in post.comments %}
|
||||
{{ include('/comment/comment.html.twig') }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{{ form_start(form) }}
|
||||
{{ form_widget(form) }}
|
||||
<button type="submit" class="btn btn-primary">Comment</button>
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@@ -125,4 +125,26 @@ class PostControllerTest extends WebTestCase
|
||||
self::assertResponseRedirects('/');
|
||||
self::assertSame(0, $this->repository->count());
|
||||
}
|
||||
|
||||
public function testPostComment()
|
||||
{
|
||||
$fixture = new Post();
|
||||
$fixture->setFoundDate(new \DateTimeImmutable('2024-01-01 00:00:00'));
|
||||
$fixture->setCommentary('Cool stuff');
|
||||
|
||||
$this->manager->persist($fixture);
|
||||
$this->manager->flush();
|
||||
|
||||
$this->client->request('GET', sprintf('%s%s', $this->path, $fixture->getId()));
|
||||
|
||||
self::assertResponseStatusCodeSame(200);
|
||||
|
||||
$this->client->submitForm('Comment', [
|
||||
'comment[content]' => 'This is a comment',
|
||||
]);
|
||||
|
||||
self::assertResponseRedirects(sprintf('%s%s', $this->path, $fixture->getId()));
|
||||
$comments = $this->repository->find($fixture->getId())->getComments();
|
||||
self::assertSame(1, $comments->count());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user