feat(controller): add details controller

This commit is contained in:
Ondrej Vlach 2024-08-03 19:18:28 +02:00
parent a99f6244b4
commit 9889c206f0
No known key found for this signature in database
GPG Key ID: 7F141CDACEDEE2DE
6 changed files with 135 additions and 2 deletions

View File

@ -0,0 +1,12 @@
.post-detail {
& .author {
font-weight: bold;
}
& .content {
padding-top: 1em;
padding-bottom: 1em;
}
& h3 {
font-size: 1.2rem;
}
}

View File

@ -1,3 +1,4 @@
$primary: darken(#66531e, 20%);
@import "~bootstrap/scss/bootstrap";
@import "detail";

View File

@ -1,8 +1,34 @@
<?php
declare(strict_types=1);
namespace App\Controller;
class DetailPostController
{
use App\Repository\Post\PostRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Attribute\Route;
final class DetailPostController extends AbstractController
{
public function __construct(
private readonly PostRepository $postRepository,
) {
}
#[Route('/posts/detail/{postId}', name: 'detail_post', requirements: ['postId' => '\d+'])]
public function detail(int $postId): Response
{
$post = $this->postRepository->find($postId);
if ($post === null) {
throw new NotFoundHttpException('Post not found. Please check the provided ID. If the problem persists, contact the system administrator');
}
return $this->render(
'detail_post/detail.html.twig',
['post' => $post]
);
}
}

View File

@ -6,6 +6,7 @@ namespace App\Controller;
use App\Repository\Post\PostRepository;
use App\Utils\Paginator\Paginator;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@ -18,6 +19,7 @@ final class ListPostController extends AbstractController
private readonly int $maxVisiblePages,
private readonly PostRepository $postRepository,
private readonly Paginator $paginator,
private readonly LoggerInterface $logger,
) {
}
@ -38,11 +40,13 @@ final class ListPostController extends AbstractController
}
if ($page === 0) {
$this->logger->error('Page number is zero. Redirecting to the first page.');
return $this->redirectToRoute('page_list', ['page' => 1]);
}
// If page number is out of range, redirect to the last page.
if ($page > $totalPages) {
$this->logger->error(sprintf('Page number %d is out of range. Redirecting to the last page: %d', $page, $totalPages));
return $this->redirectToRoute('page_list', ['page' => $totalPages]);
}

View File

@ -0,0 +1,31 @@
{% extends 'base.html.twig' %}
{% block body %}
<div class="post-detail t-post-detail">
<h2 class="t-title">{{ post.title }}</h2>
<div class="author t-author">
by <span class="t-user-name">{{ post.user.name }}</span> (<span class="t-user-email">{{ post.user.email }}</span>), <a class="t-user-website" href="http://{{ post.user.website|url_encode }}">{{ post.user.website }}</span></a>)
work at <span class="t-user-company">{{ post.user.company.name }}</span>
</div>
<p class="content t-content">
{{ post.body }}
</p>
{% if post.comments|length > 0 %}
<h3>Comments:</h3>
<div class="t-comments">
{% for comment in post.comments %}
<div class="card t-comment">
<div class="card-header t-comment-header">
<span class="t-comment-name">{{ comment.name }}</span> (<a class="t-comment-email" href="mailto:{{ comment.email|url_encode }}">{{ comment.email }}</a>)
</div>
<div class="card-body">
<p class="card-text t-comment-body">
{{ comment.body }}
</p>
</div>
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace App\Tests\Web;
use App\Tests\Common\DatabaseTestTrait;
use App\Tests\Common\FakerTrait;
use App\Tests\Common\Generators\CommentGeneratorTrait;
use App\Tests\Common\Generators\PostGeneratorTrait;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\BrowserKit\AbstractBrowser;
class DetailPostControllerTest extends WebTestCase
{
use DatabaseTestTrait;
use FakerTrait;
use PostGeneratorTrait;
use CommentGeneratorTrait;
protected AbstractBrowser $client;
protected function setUp(): void
{
$this->client = static::createClient();
$this->bootDatabase();
$this->bootFaker();
$this->getFaker()->unique();
}
public function testPostDetail(): void
{
$post = $this->createPost(1);
$comments = [];
$comments[] = $this->createComment(1, $post);
$comments[] = $this->createComment(2, $post);
$this->client->request('GET', '/posts/detail/1');
$this->assertResponseIsSuccessful();
$this->assertSelectorTextSame('.t-title', $post->title);
$this->assertSelectorTextSame('.t-content', $post->body);
$this->assertSelectorTextSame('.t-user-name', $post->user->name);
$this->assertSelectorTextSame('.t-user-email', $post->user->email);
$this->assertSelectorTextSame('.t-user-website', $post->user->website);
$this->assertSelectorTextSame('.t-user-company', $post->user->company->name);
$this->assertSelectorCount(2, '.t-comment');
$this->assertSelectorTextSame('.t-comment:nth-child(1) .t-comment-name', $comments[0]->name);
$this->assertSelectorTextSame('.t-comment:nth-child(1) .t-comment-email', $comments[0]->email);
$this->assertSelectorTextSame('.t-comment:nth-child(1) .t-comment-body', $comments[0]->body);
}
public function testPostDetailNotFound(): void
{
$this->client->request('GET', '/posts/detail/100');
$this->assertResponseStatusCodeSame(404);
}
}