Página con la entrada

Ahora crearemos otra página del blog que mostrará una entrada específica.

Necesitamos crear un nuevo método de renderizado que obtenga un artículo específico y lo pase a la plantilla. Tener este método en HomePresenter no es muy elegante, porque estamos hablando de un artículo y no de la página de inicio. Por lo tanto, creemos PostPresenter en app/Presentation/Post/. Este presentador también necesita conectarse a la base de datos, por lo que aquí nuevamente escribiremos un constructor que requerirá una conexión a la base de datos.

PostPresenter podría verse así:

<?php
namespace App\Presentation\Post;

use Nette;
use Nette\Application\UI\Form;

final class PostPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}

	public function renderShow(int $id): void
	{
		$this->template->post = $this->database
			->table('posts')
			->get($id);
	}
}

No debemos olvidar indicar el namespace correcto App\Presentation\Post, que está sujeto a la configuración del mapeo de presentadores.

El método renderShow requiere un argumento: el ID de un artículo específico que debe mostrarse. Luego, carga este artículo de la base de datos y lo pasa a la plantilla.

En la plantilla Home/default.latte insertamos un enlace a la acción Post:show.

...
<h2><a href="{link Post:show $post->id}">{$post->title}</a></h2>
...

La etiqueta {link} genera una dirección URL que apunta a la acción Post:show. También pasa el ID de la entrada como argumento.

Podemos escribir lo mismo de forma abreviada usando un n:atributo:

...
<h2><a n:href="Post:show $post->id">{$post->title}</a></h2>
...

El atributo n:href es análogo a la etiqueta {link}.

Sin embargo, para la acción Post:show aún no existe una plantilla. Podemos intentar abrir el enlace a esta entrada. Tracy mostrará un error porque la plantilla Post/show.latte aún no existe. Si ves otro mensaje de error, probablemente necesitarás habilitar mod_rewrite en el servidor web.

Por lo tanto, creamos la plantilla Post/show.latte con este contenido:

{block content}

<p><a n:href="Home:default">← volver a la lista de entradas</a></p>

<div class="date">{$post->created_at|date:'F j, Y'}</div>

<h1 n:block="title">{$post->title}</h1>

<div class="post">{$post->content}</div>

Ahora repasemos las partes individuales de la plantilla.

La primera línea comienza la definición de un bloque llamado “content”, igual que en la página de inicio. Este bloque se mostrará nuevamente en la plantilla principal. Como puedes ver, falta la etiqueta de cierre {/block}. Es opcional.

En la siguiente línea hay un enlace de vuelta a la lista de artículos del blog, para que el usuario pueda moverse fácilmente entre la lista de artículos y uno específico. Como usamos el atributo n:href, Nette se encarga automáticamente de generar los enlaces. El enlace apunta a la acción default del presentador Home (también podemos escribir n:href="Home:", porque la acción llamada default puede omitirse, se completa automáticamente).

La tercera línea formatea la visualización de la fecha usando un filtro que ya conocemos.

La cuarta línea muestra el título del blog en la etiqueta HTML <h1>. Esta etiqueta contiene un atributo que quizás no conozcas (n:block="title"). ¿Adivinas qué hace? Si leíste la parte anterior con atención, ya sabes que se trata de un n:atributo. Este es otro ejemplo de ellos, que es equivalente a:

{block title}<h1>{$post->title}</h1>{/block}

En pocas palabras, este bloque redefine el bloque llamado title. Este bloque ya está definido en la plantilla principal layout (/app/Presentation/@layout.latte:11) y, al igual que con la sobrescritura de métodos en OOP, este bloque en la plantilla principal se sobrescribe exactamente igual. Así que el <title> de la página ahora contiene el título de la entrada mostrada y solo necesitamos usar un simple atributo n:block="title". Genial, ¿verdad?

La quinta y última línea de la plantilla muestra todo el contenido de una entrada específica.

Comprobación del ID de la entrada

¿Qué sucede si alguien cambia el ID en la URL e inserta un id inexistente? Deberíamos ofrecer al usuario un error agradable del tipo “página no encontrada”. Modificaremos un poco el método de renderizado en PostPresenter:

public function renderShow(int $id): void
{
	$post = $this->database
		->table('posts')
		->get($id);
	if (!$post) {
		$this->error('Página no encontrada');
	}

	$this->template->post = $post;
}

Si no se puede encontrar la entrada, al llamar a $this->error(...) mostraremos una página de error 404 con un mensaje comprensible. Ten en cuenta que en el modo de desarrollo (localhost) no verás esta página de error. En su lugar, se mostrará Tracy con detalles sobre la excepción, lo cual es bastante conveniente para el desarrollo. Si queremos que se muestren ambos modos, simplemente cambia el argumento del método setDebugMode en el archivo Bootstrap.php.

Resumen

Tenemos una base de datos con entradas y una aplicación web que tiene dos vistas: la primera muestra un resumen de todas las entradas y la segunda muestra una entrada específica.