Comentarios
El blog está desplegado, hemos escrito unos artículos interesantísimos y los hemos publicado con Adminer. La gente lee nuestro blog y está entusiasmada. Recibimos cada día muchos correos con elogios. ¿Pero de qué sirven todos esos elogios si están solo en nuestro correo y nadie más puede leerlos? ¿No sería mejor que los lectores pudieran comentar directamente en los artículos, para que todo el mundo viera lo estupendos que somos?
Implementemos los comentarios.
Crear una tabla nueva
Arranquemos de nuevo Adminer y creemos una tabla comments con estas columnas:
idint, marque autoincrement (AI)post_id, clave foránea que referencia a la tablapostsnamevarchar, longitud 255emailvarchar, longitud 255contenttextcreated_attimestamp
La tabla debería quedar más o menos así:

Recuerde usar de nuevo el motor de almacenamiento InnoDB.
CREATE TABLE `comments` (
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`post_id` int NOT NULL,
`name` varchar(255) NOT NULL,
`email` varchar(255) 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;
Formulario para comentar
Primero tenemos que crear un formulario que permita a los usuarios comentar los artículos. Nette Framework tiene un soporte excelente para formularios. Podemos configurarlos en el presenter y renderizarlos en la plantilla.
Nette Framework usa el concepto de componentes. Un componente es una clase o un trozo de código reutilizable
que se puede adjuntar a otro componente. Incluso un presenter es un componente. Cada componente se crea con un método fábrica.
Creemos una fábrica para el formulario de comentarios en PostPresenter.
protected function createComponentCommentForm(): Form
{
$form = new Form; // significa Nette\Application\UI\Form
$form->addText('name', 'Your name:')
->setRequired();
$form->addEmail('email', 'Email:');
$form->addTextArea('content', 'Comment:')
->setRequired();
$form->addSubmit('send', 'Publish comment');
return $form;
}
Expliquémoslo brevemente. La primera línea crea una nueva instancia del componente Form. Los métodos siguientes
adjuntan campos HTML a la definición del formulario. ->addText se renderiza como
<input type="text" name="name"> con una <label>Your name:</label>. Como puede
imaginar, ->addTextArea adjunta un <textarea> y ->addSubmit añade un
<input type="submit">. Hay muchos métodos parecidos, pero por ahora estos bastan. Puede saber más en la documentación.
Una vez definido el componente del formulario en el presenter, podemos renderizarlo (mostrarlo) en la plantilla. Para ello,
coloque la etiqueta {control} al final de la plantilla que muestra un artículo concreto, en
Post/show.latte. Como el componente se llama commentForm (derivado del nombre del método
createComponentCommentForm), la etiqueta quedará así:
...
<h2>Post new comment</h2>
{control commentForm}
Ahora, si mira la página de detalle de un artículo, verá al final el nuevo formulario de comentarios.
Guardar en la base de datos
¿Ha probado a rellenar y enviar el formulario? Puede que se haya dado cuenta de que el formulario todavía no hace nada. Tenemos que adjuntarle un método callback que guarde los datos enviados.
Añada la siguiente línea antes de la sentencia return del método fábrica del componente
commentForm:
$form->onSuccess[] = $this->commentFormSucceeded(...);
Esta línea significa “después de enviar el formulario correctamente, llama al método commentFormSucceeded del
presenter actual”. Ese método todavía no existe, así que creémoslo:
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('Thank you for your comment', 'success');
$this->redirect('this');
}
Debería colocarlo justo después de la fábrica del componente commentForm.
El método nuevo tiene un argumento, que es la instancia del formulario que se envía, creada por la fábrica del componente.
Los valores enviados los recibimos en $data. Y después insertamos los datos en la tabla comments de la
base de datos.
Quedan por explicar dos llamadas a métodos. El método redirect('this') redirige literalmente de vuelta a la
página actual. Debería hacerlo cada vez que se envía un formulario correctamente y la operación del callback ha terminado.
Redirigir tras el envío del formulario también evita el conocido mensaje del navegador “¿Quiere volver a enviar los datos del
formulario?”. (En general, tras enviar un formulario con el método POST debería redirigir siempre a una acción
GET.)
El método flashMessage sirve para informar al usuario del resultado de una operación. Como estamos redirigiendo,
el mensaje no se puede pasar sin más a la plantilla y renderizarlo directamente. Este método guarda el mensaje y lo pone a
disposición en la siguiente carga de la página. Los mensajes flash se renderizan en la plantilla principal del layout
app/Presentation/@layout.latte de esta manera:
<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">
{$flash->message}
</div>
Como ya sabemos, los mensajes flash se pasan automáticamente a la plantilla, así que no tenemos que pensar mucho en ello; simplemente funciona. Para más detalles, visite la documentación.
Renderizar los comentarios
Esta es una de esas funcionalidades que sencillamente le encantarán. Nette Database tiene una gran característica llamada Explorer. ¿Recuerda que creamos a propósito las tablas de la base de datos con el motor InnoDB? Adminer creó las llamadas claves foráneas, que nos ahorran mucho trabajo.
Nette Database Explorer usa esas claves foráneas para resolver las relaciones entre tablas. Al conocer esas relaciones, puede crear automáticamente las consultas a la base de datos por usted.
Como quizá recuerde, pasamos la variable $post a la plantilla en PostPresenter::renderShow(). Ahora
queremos recorrer todos los comentarios cuya columna post_id coincida con nuestro $post->id. Lo
conseguimos llamando a $post->related('comments'). Sí, es así de simple. Veamos el código final:
public function renderShow(int $id): void
{
// ...
$this->template->post = $post;
$this->template->comments = $post->related('comments')->order('created_at');
}
Y la plantilla:
...
<h2>Comments</h2>
<div class="comments">
{foreach $comments as $comment}
<p><b><a href="mailto:{$comment->email}" n:tag-if="$comment->email">
{$comment->name}
</a></b> said:</p>
<div>{$comment->content}</div>
{/foreach}
</div>
...
Fíjese en el atributo especial n:tag-if. Ya sabe cómo funcionan los n:atributos. Si le pone al
atributo el prefijo tag-, la funcionalidad se aplica solo a la propia etiqueta HTML, no a su contenido. Eso nos
permite convertir el nombre de quien comenta en un enlace solo si indicó su correo. Estas dos líneas producen resultados
idénticos:
<strong n:tag-if="$important"> Hello there! </strong>
{if $important}<strong>{/if} Hello there! {if $important}</strong>{/if}