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:
idint, marque autoincremento (AI)post_id, chave estrangeira que referencia a tabelapostsnamevarchar, comprimento 255emailvarchar, comprimento 255contenttextcreated_attimestamp
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}