feat(controller): add details controller
This commit is contained in:
parent
a99f6244b4
commit
9889c206f0
12
assets/styles/_detail.scss
Normal file
12
assets/styles/_detail.scss
Normal file
@ -0,0 +1,12 @@
|
||||
.post-detail {
|
||||
& .author {
|
||||
font-weight: bold;
|
||||
}
|
||||
& .content {
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
& h3 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
$primary: darken(#66531e, 20%);
|
||||
|
||||
@import "~bootstrap/scss/bootstrap";
|
||||
@import "detail";
|
||||
|
@ -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]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
|
31
templates/detail_post/detail.html.twig
Normal file
31
templates/detail_post/detail.html.twig
Normal 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 %}
|
59
tests/Web/DetailPostControllerTest.php
Normal file
59
tests/Web/DetailPostControllerTest.php
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user