Comentarios
Hemos subido el blog al servidor web y publicado algunas entradas muy interesantes usando Adminer. La gente lee nuestro blog y está muy entusiasmada con él. Recibimos muchos correos electrónicos con elogios todos los días. Pero, ¿de qué sirven todos estos elogios si solo los tenemos en el correo electrónico y nadie más puede leerlos? Sería mejor si el lector pudiera comentar directamente en el artículo, para que todos pudieran leer lo geniales que somos.
Así que vamos a programar los comentarios.
Creación de una nueva tabla
Iniciamos Adminer y creamos una tabla comments
con estas columnas:
id
int, marcamos autoincrement (AI)post_id
, clave foránea que hace referencia a la tablaposts
name
varchar, longitud 255email
varchar, longitud 255content
textcreated_at
timestamp
La tabla debería verse algo así:

No olvides usar de nuevo el almacenamiento InnoDB.
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;
Formulario para comentar
Primero, debemos crear un formulario que permita a los usuarios comentar las entradas. Nette Framework tiene un soporte increíble para formularios. Podemos configurarlos en el presenter y renderizarlos en la plantilla.
Nette Framework utiliza el concepto de componentes. Un componente es una clase reutilizable o una parte de
código que se puede adjuntar a otro componente. Incluso el presenter es un componente. Cada componente se crea a través de una
fábrica. Por lo tanto, crearemos una fábrica para el formulario de comentarios en el presenter PostPresenter
.
protected function createComponentCommentForm(): Form
{
$form = new Form; // significa Nette\Application\UI\Form
$form->addText('name', 'Nombre:')
->setRequired();
$form->addEmail('email', 'E-mail:');
$form->addTextArea('content', 'Comentario:')
->setRequired();
$form->addSubmit('send', 'Publicar comentario');
return $form;
}
Vamos a explicarlo un poco de nuevo. La primera línea crea una nueva instancia del componente Form
. Los métodos
siguientes adjuntan entradas HTML a la definición de este formulario. ->addText
se renderizará como
<input type="text" name="name">
con <label>Nombre:</label>
. Como probablemente ya
adivinas correctamente, ->addTextArea
se renderizará como <textarea>
y
->addSubmit
como <input type="submit">
. Hay muchos más métodos similares, pero estos son
suficientes para este formulario por ahora. Puedes leer más en la
documentación.
Si el formulario ya está definido en el presenter, podemos renderizarlo (mostrarlo) en la plantilla. Esto se hace colocando la
etiqueta {control}
al final de la plantilla que renderiza una entrada específica, en Post/show.latte
.
Como el componente se llama commentForm
(el nombre se deriva del nombre del método
createComponentCommentForm
), la etiqueta se verá así:
...
<h2>Añadir nuevo comentario</h2>
{control commentForm}
Si ahora ves la página con el detalle de la entrada, verás un nuevo formulario para comentarios al final.
Guardando en la base de datos
¿Ya has intentado rellenar el formulario y enviarlo? Probablemente te hayas dado cuenta de que el formulario en realidad no hace nada. Necesitamos adjuntar un método de callback que guarde los datos enviados.
En la línea antes de return
en la fábrica para el componente commentForm
, colocamos la
siguiente línea:
$form->onSuccess[] = $this->commentFormSucceeded(...);
La notación anterior significa “después de enviar el formulario con éxito, llama al método
commentFormSucceeded
del presenter actual”. Sin embargo, este método aún no existe. Vamos a crearlo:
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('Gracias por tu comentario', 'success');
$this->redirect('this');
}
Colocamos este método justo después de la fábrica del formulario commentForm
.
Este nuevo método tiene un argumento, que es una instancia del formulario que fue enviado – creado por la fábrica.
Obtenemos los valores enviados en $data
. Y luego guardamos los datos en la tabla de la base de datos
comments
.
Todavía hay otros dos métodos que merecen ser explicados. El método redirect
literalmente redirige de vuelta a
la página actual. Es conveniente hacer esto después de cada envío de formulario, si contenía datos válidos y el callback
realizó la operación como debía. Y también, si redirigimos la página después de enviar el formulario, no veremos el conocido
mensaje ¿Quieres volver a enviar los datos del formulario?
, que a veces podemos ver en el navegador. (En general,
después de enviar un formulario con el método POST
, siempre debe seguir una redirección a una acción
GET
).
El método flashMessage
es para informar al usuario sobre el resultado de alguna operación. Como estamos
redirigiendo, el mensaje no puede simplemente pasarse a la plantilla y renderizarse. Por eso existe este método, que guarda este
mensaje y lo hace accesible en la próxima carga de la página. Los mensajes flash se renderizan en la plantilla principal
app/Presentation/@layout.latte
y se ve así:
<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 información, visita la documentación.
Renderizando comentarios
Esta es una de esas cosas que simplemente te encantarán. Nette Database tiene una función genial llamada Explorer. ¿Recuerdas que creamos intencionalmente las tablas en la base de datos usando el almacenamiento InnoDB? Adminer creó así algo llamado claves foráneas, que nos ahorrarán mucho trabajo.
Nette Database Explorer utiliza claves foráneas para resolver la relación mutua entre tablas y, a partir del conocimiento de estas relaciones, puede crear automáticamente consultas a la base de datos.
Como seguramente recordarás, pasamos la variable $post
a la plantilla usando el método
PostPresenter::renderShow()
y ahora queremos iterar sobre todos los comentarios que tienen el valor de la columna
post_id
igual a $post->id
. Podemos lograr esto llamando a $post->related('comments')
.
Sí, así de simple. Veamos el código resultante:
public function renderShow(int $id): void
{
...
$this->template->post = $post;
$this->template->comments = $post->related('comments')->order('created_at');
}
Y la plantilla:
...
<h2>Comentarios</h2>
<div class="comments">
{foreach $comments as $comment}
<p><b><a href="mailto:{$comment->email}" n:tag-if="$comment->email">
{$comment->name}
</a></b> escribió:</p>
<div>{$comment->content}</div>
{/foreach}
</div>
...
Observa el atributo especial n:tag-if
. Ya sabes cómo funcionan los n:atributos
. Si adjuntas el
prefijo tag-
al atributo, la funcionalidad se aplica solo a la etiqueta HTML, no a su contenido. Esto nos permite
convertir el nombre del comentarista en un enlace solo si proporcionó su correo electrónico. Estas dos líneas son
idénticas:
<strong n:tag-if="$important"> ¡Hola! </strong>
{if $important}<strong>{/if} ¡Hola! {if $important}</strong>{/if}