Comentarios
El blog se ha puesto en marcha, hemos escrito algunas entradas muy buenas y las hemos publicado a través de Adminer. La gente está leyendo el blog y les apasionan nuestras ideas. Estamos recibiendo muchos correos electrónicos con elogios cada día. Pero, ¿para qué sirven todos esos elogios si sólo los recibimos en el correo electrónico, de modo que nadie más puede leerlos? ¿No sería mejor que la gente pudiera comentar directamente en el blog para que todo el mundo pudiera leer lo increíbles que somos?
Hagamos que todos los artículos se puedan comentar.
Crear una nueva tabla
Inicie Adminer de nuevo y cree una nueva tabla llamada comments
con estas columnas:
id
int, comprobar autoincremento (AI)post_id
, una clave externa que hace referencia a la tablaposts
name
varchar, longitud 255email
varchar, longitud 255content
textocreated_at
timestamp
Debería tener este aspecto:
No olvides usar el almacenamiento de tablas InnoDB y pulsa Guardar.
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 de comentarios
Primero, necesitamos crear un formulario, que permitirá a los usuarios comentar en nuestra página. Nette Framework tiene un soporte impresionante para formularios. Pueden ser configurados en un presentador y renderizados en una plantilla.
Nette Framework tiene un concepto de componentes. Un componente es una clase reutilizable o una pieza de
código, que puede ser adjuntada a otro componente. Incluso un presentador es un componente. Cada componente se crea utilizando la
fábrica de componentes. Así que vamos a definir la fábrica de formularios de comentarios en PostPresenter
.
protected function createComponentCommentForm(): Form
{
$form = new Form; // means 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;
}
Vamos a explicarlo un poco. La primera línea crea una nueva instancia del componente Form
. Los siguientes
métodos están adjuntando entradas HTML en la definición del formulario. ->addText
se renderizará como
<input type=text name=name>
con <label>Your name:</label>
. Como ya habrás adivinado en
este momento, el ->addTextArea
adjunta un <textarea>
y ->addSubmit
añade un
<input type=submit>
. Hay más métodos como este, pero esto es todo lo que tienes que saber por ahora. Puedes
aprender más en la documentación.
Una vez definido el componente formulario en un presentador, podemos renderizarlo (mostrarlo) en una plantilla. Para ello,
coloque la etiqueta {control}
al final de la plantilla de detalles de la entrada, en Post/show.latte
.
Dado que el nombre del componente es commentForm
(se deriva del nombre del método
createComponentCommentForm
), la etiqueta tendrá el siguiente aspecto
...
<h2>Post new comment</h2>
{control commentForm}
Ahora, si revisas el detalle de algún post, habrá un nuevo formulario para publicar comentarios.
Guardar en base de datos
¿Ha intentado enviar algunos datos? Te habrás dado cuenta de que el formulario no realiza ninguna acción. Simplemente está ahí, con un aspecto chulo y sin hacer nada. Tenemos que adjuntarle un método callback, que guardará los datos enviados.
Coloca la siguiente línea antes de la línea return
en la fábrica del componente commentForm
:
$form->onSuccess[] = $this->commentFormSucceeded(...);
Significa “después de que el formulario se envíe correctamente, llama al método commentFormSucceeded
del
presentador actual”. Este método no existe todavía, así que 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('Thank you for your comment', 'success');
$this->redirect('this');
}
Debes colocarlo justo después de la fábrica del componente commentForm
.
El nuevo método tiene un argumento que es la instancia del formulario que se está enviando, creada por la fábrica de
componentes. Recibimos los valores enviados en $data
. Y luego insertamos los datos en la tabla de la base de datos
comments
.
Hay otras dos llamadas a métodos que explicar. La redirección redirige literalmente a la página actual. Deberías hacer eso
cada vez que el formulario es enviado, válido, y la operación callback hizo lo que debería haber hecho. Además, cuando
redirija la página después de enviar el formulario, no verá el conocido mensaje
Would you like to submit the post data again?
que a veces puede ver en el navegador. (En general, después de enviar
un formulario por el método POST
, siempre debes redirigir al usuario a una acción GET
).
El flashMessage
sirve para informar al usuario del resultado de alguna operación. Debido a que estamos
redirigiendo, el mensaje no puede ser pasado directamente a la plantilla y renderizado. Así que existe este método, que lo
almacenará y lo hará disponible en la siguiente carga de página. Los mensajes flash son renderizados en el archivo por defecto
app/UI/@layout.latte
, y se ve así:
<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">
{$flash->message}
</div>
Como ya sabemos, se pasan automáticamente a la plantilla, así que no tienes que pensar mucho en ello, simplemente funciona. Para más detalles consulta la documentación.
Renderizando los Comentarios
Esta es una de las cosas que te encantará. La Base de Datos Nette tiene esta característica genial llamada Explorador. ¿Recuerdas que hemos creado las tablas como InnoDB? Adminer ha creado las llamadas claves foráneas que nos ahorrarán un montón de trabajo.
Nette Database Explorer utiliza las claves externas para resolver las relaciones entre las tablas, y conociendo las relaciones, puede crear automáticamente consultas para usted.
Como recordará, hemos pasado la variable $post
a la plantilla en PostPresenter::renderShow()
y ahora
queremos iterar por todos los comentarios que tengan la columna post_id
igual a nuestra $post->id
.
Puedes hacerlo llamando a $post->related('comments')
. Así de sencillo. Mira 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>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íjate en el atributo especial n:tag-if
. Ya sabes cómo funciona n: attributes
. Pues bien, si
antepones al atributo tag-
, sólo envolverá las etiquetas, no su contenido. Esto le permite convertir el nombre del
comentarista en un enlace si ha proporcionado su dirección de correo electrónico. Los resultados de estas dos líneas son
idénticos:
<strong n:tag-if="$important"> Hello there! </strong>
{if $important}<strong>{/if} Hello there! {if $important}</strong>{/if}