Comentários
Nós carregamos o blog para o servidor web e publicamos algumas postagens muito interessantes usando o Adminer. As pessoas estão lendo nosso blog e estão muito entusiasmadas com ele. Recebemos muitos e-mails com elogios todos os dias. Mas de que adianta todo esse elogio se o temos apenas em nosso e-mail e ninguém pode lê-lo? Seria melhor se o leitor pudesse comentar diretamente no artigo, para que todos pudessem ler como somos incríveis.
Então, vamos programar os comentários.
Criação de uma nova tabela
Vamos iniciar o Adminer e criar uma tabela comments
com as seguintes colunas:
id
int, marque autoincremento (AI)post_id
, chave estrangeira que referencia a tabelaposts
name
varchar, comprimento 255email
varchar, comprimento 255content
textcreated_at
timestamp
A tabela deve, portanto, ter a seguinte aparência:

Não se esqueça de usar o armazenamento InnoDB novamente.
CREATE TABLE `comments` (
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`post_id` int(11) NOT NULL,
`name` varchar(250) NOT NULL,
`email` varchar(250) NOT NULL,
`content` text NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`)
) ENGINE=InnoDB CHARSET=utf8;
Formulário para comentar
Primeiro, precisamos criar um formulário que permita aos usuários comentar nas postagens. O Nette Framework tem um suporte incrível para formulários. Podemos configurá-los no presenter e renderizá-los no template.
O Nette Framework utiliza o conceito de componentes. Um componente é uma classe reutilizável ou parte de
código que pode ser anexada a outro componente. Até mesmo o presenter é um componente. Cada componente é criado através de
uma fábrica. Portanto, criaremos uma fábrica para o formulário de comentários no presenter PostPresenter
.
protected function createComponentCommentForm(): Form
{
$form = new Form; // significa Nette\Application\UI\Form
$form->addText('name', 'Nome:')
->setRequired();
$form->addEmail('email', 'E-mail:');
$form->addTextArea('content', 'Comentário:')
->setRequired();
$form->addSubmit('send', 'Publicar comentário');
return $form;
}
Vamos explicar isso um pouco mais. A primeira linha cria uma nova instância do componente Form
. Os métodos
seguintes anexam inputs HTML à definição deste formulário. ->addText()
será renderizado como
<input type="text" name="name">
com <label>Nome:</label>
. Como você provavelmente já
adivinhou corretamente, ->addTextArea()
será renderizado como <textarea>
e
->addSubmit()
como <input type="submit">
. Existem muitos outros métodos semelhantes, mas estes
são suficientes para este formulário por enquanto. Você pode ler mais na
documentação.
Se o formulário já estiver definido no presenter, podemos renderizá-lo (exibi-lo) no template. Faremos isso colocando a tag
{control}
no final do template que renderiza uma postagem específica, em Post/show.latte
. Como
o componente se chama commentForm
(o nome é derivado do nome do método createComponentCommentForm
),
a tag ficará assim:
...
<h2>Insira uma nova postagem</h2>
{control commentForm}
Agora, se você visualizar a página com os detalhes da postagem, verá um novo formulário de comentários no final.
Salvando no banco de dados
Você já tentou preencher e enviar o formulário? Você provavelmente notou que o formulário na verdade não faz nada. Precisamos anexar um método de callback que salvará os dados enviados.
O callback commentFormSucceeded
já foi adicionado ao código do presenter PostPresenter
acima.
$form->onSuccess[] = $this->commentFormSucceeded(...);
A escrita anterior significa “após o envio bem-sucedido do formulário, chame o método commentFormSucceeded
do presenter atual”.
private function commentFormSucceeded(\stdClass $data): void
{
$id = $this->getParameter('id');
$this->database->table('comments')->insert([
'post_id' => $id,
'name' => $data->name,
'email' => $data->email,
'content' => $data->content,
]);
$this->flashMessage('Obrigado pelo comentário', 'success');
$this->redirect('this');
}
Colocaremos este método logo após a fábrica do formulário commentForm
.
Este novo método tem um argumento, que é uma instância do formulário que foi enviado – criado pela fábrica. Obtemos os
valores enviados em $data
. E, em seguida, salvamos os dados na tabela do banco de dados comments
.
Existem ainda outros dois métodos que merecem explicação. O método redirect literalmente redireciona de volta para a
página atual. Isso é apropriado fazer após cada envio de formulário, se ele continha dados válidos e o callback realizou a
operação como deveria. E também, se redirecionarmos a página após o envio do formulário, não veremos a conhecida mensagem
Deseja reenviar os dados do formulário?
, que às vezes podemos ver no navegador. (Geralmente, após o envio de um
formulário pelo método POST
, deve sempre seguir um redirecionamento para uma ação GET
.)
O método flashMessage()
serve para informar o usuário sobre o resultado de alguma operação. Como estamos
redirecionando, a mensagem não pode ser simplesmente passada para o template e renderizada diretamente. Por isso, existe este
método, que armazena esta mensagem na sessão e a disponibiliza na próxima carga da página. As mensagens flash são
renderizadas no template principal app/Presentation/@layout.latte
e têm a seguinte aparência:
<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">
{$flash->message}
</div>
Como já sabemos, as mensagens flash são automaticamente passadas para o template, então não precisamos pensar muito sobre isso, simplesmente funciona. Para mais informações visite a documentação.
Renderizando comentários
Esta é uma daquelas coisas que você simplesmente vai adorar. O Nette Database tem uma função incrível chamada Explorer. Lembra-se que criamos intencionalmente as tabelas no banco de dados usando o armazenamento InnoDB? O Adminer criou algo chamado chaves estrangeiras, que nos poupará muito trabalho.
O Nette Database Explorer usa chaves estrangeiras para resolver o relacionamento mútuo entre as tabelas e, com base no conhecimento dessas relações, pode criar automaticamente consultas ao banco de dados.
Como você certamente se lembra, passamos a variável $post
para o template usando o método
PostPresenter::renderShow()
e agora queremos iterar sobre todos os comentários que têm o valor da coluna
post_id
igual a $post->id
. Podemos conseguir isso chamando
$post->related('comments')
. Sim, é simples assim. Vejamos o código resultante:
public function renderShow(int $id): void
{
...
$this->template->post = $post;
$this->template->comments = $post->related('comments')->order('created_at');
}
E o template:
...
<h2>Comentários</h2>
<div class="comments">
{foreach $comments as $comment}
<p><b><a href="mailto:{$comment->email}" n:tag-if="$comment->email">
{$comment->name}
</a></b> escreveu:</p>
<div>{$comment->content}</div>
{/foreach}
</div>
...
Observe o atributo especial n:tag-if
. Você já sabe como os n:atributos
funcionam. Se você anexar
o prefixo tag-
ao atributo, a funcionalidade será aplicada apenas à tag HTML, não ao seu conteúdo. Isso nos
permite transformar o nome do comentarista em um link apenas se ele forneceu seu e-mail. Estas duas linhas são idênticas:
<strong n:tag-if="$important"> Olá! </strong>
{if $important}<strong>{/if} Olá! {if $important}</strong>{/if}