¿Cómo funcionan las aplicaciones?

Está leyendo el documento básico de la documentación de Nette. Aprenderá todo el principio de las aplicaciones web. Nette de la A a la Z, desde el momento del nacimiento hasta el último suspiro del script PHP. Después de leer usted sabrá:

  • cómo funciona todo
  • qué es Bootstrap, Presenter y DI container
  • cómo es la estructura de directorios

Estructura del directorio

Abra un esqueleto de ejemplo de una aplicación web llamada WebProject y podrá observar los archivos sobre los que se escribe.

La estructura de directorios se parece a esto

web-project/
├── app/                      ← directorio con la aplicación
│   ├── Core/                 ← clases básicas necesarias.
│   │ └── RouterFactory.php   ← configuración de direcciones URL.
│   ├── UI/                   ← presentadores, plantillas & co.
│   │   ├── @layout.latte     ← plantilla de maquetación compartida
│   │   └── Home/             ← Home directorio del presentador
│   │       ├── HomePresenter.php ← Clase del presentador de inicio
│   │       └── default.latte ← plantilla para la acción default
│   └── Bootstrap.php         ← clase de arranque Bootstrap
├── bin/                      ← scripts para la línea de comandos
├── config/                   ← archivos de configuración
│   ├── common.neon
│   └── services.neon
├── log/                      ← registros de errores
├── temp/                     ← archivos temporales, caché, ...
├── vendor/                   ← libraries installed by Composer
│   ├── ...
│   └── autoload.php          ← carga automática de librerías instaladas por Composer
├── www/                      ← directorio público, raíz documental del proyecto
│   ├── .htaccess             ← reglas mod_rewrite, etc.
│   └── index.php             ← archivo inicial que lanza la aplicación
└── .htaccess                 ← prohíbe el acceso a todos los directorios excepto www

Puedes cambiar la estructura de directorios de cualquier forma, renombrar o mover carpetas, y entonces sólo tienes que editar las rutas a log/ y temp/ en el archivo Bootstrap.php y la ruta a este archivo en composer.json en la sección autoload. Nada más, sin reconfiguraciones complicadas ni cambios constantes. Nette tiene una autodetección inteligente.

Para aplicaciones un poco más grandes, podemos dividir las carpetas con presentadores y plantillas en subdirectorios (en el disco) y en espacios de nombres (en el código), que llamamos módulos.

El directorio www/ es el directorio público o raíz documental del proyecto. Puedes renombrarlo sin tener que configurar nada más en el lado de la aplicación. Solo necesitas configurar el hosting para que el document-root vaya a este directorio.

También puede descargar el WebProject directamente, incluyendo Nette, utilizando Composer:

composer create-project nette/web-project

En Linux o macOS, establezca los permisos de escritura para los directorios log/ y temp/.

La aplicación WebProject está lista para ejecutarse, no es necesario configurar nada más y puede verla directamente en el navegador accediendo a la carpeta www/.

Petición HTTP

Todo comienza cuando un usuario abre la página en un navegador y éste llama al servidor con una petición HTTP. La petición va a un fichero PHP situado en el directorio público www/, que es index.php. Supongamos que se trata de una petición a https://example.com/product/123 y se ejecutará.

Su tarea es:

  1. inicializar el entorno
  2. obtener la fábrica
  3. lanzar la aplicación Nette que gestiona la solicitud

¿Qué tipo de fábrica? No fabricamos tractores, ¡sino páginas web! Espere, se lo explicaremos enseguida.

Por “inicializar el entorno” entendemos, por ejemplo, que se activa Tracy, que es una herramienta increíble para registrar o visualizar errores. Registra los errores en el servidor de producción y los muestra directamente en el servidor de desarrollo. Por lo tanto, la inicialización también necesita decidir si el sitio se está ejecutando en modo de producción o de desarrollo. Para ello, Nette utiliza la autodetección: si ejecuta el sitio en localhost, se ejecuta en modo desarrollador. No hay que configurar nada y la aplicación está lista tanto para el desarrollo como para el despliegue en producción. Estos pasos se realizan y describen en detalle en el capítulo sobre la clase Bootstrap.

El tercer punto (sí, nos hemos saltado el segundo, pero volveremos a él) es iniciar la aplicación. El manejo de las peticiones HTTP en Nette lo realiza la clase Nette\Application\Application (en adelante Application), por lo que cuando decimos “ejecutar una aplicación”, nos referimos a llamar a un método con el nombre run() sobre un objeto de esta clase.

Nette es un mentor que te guía para escribir aplicaciones limpias mediante metodologías probadas. Y la más probada se llama inyección de dependencia, abreviada DI. De momento no queremos agobiarte explicando DI, ya que hay un capítulo aparte, lo importante aquí es que los objetos clave normalmente serán creados por una fábrica de objetos llamada contenedor DI (abreviado DIC). Sí, esta es la fábrica que se mencionó hace un rato. Y también crea el objeto Application por nosotros, así que primero necesitamos un contenedor. Lo obtenemos usando la clase Configurator y dejamos que produzca el objeto Application, llamamos al método run() y esto inicia la aplicación Nette. Esto es exactamente lo que sucede en el archivo index.php.

Aplicación Nette

La clase Aplicación tiene una única tarea: responder a una petición HTTP.

Las aplicaciones escritas en Nette se dividen en muchos de los llamados presentadores (en otros frameworks se puede encontrar con el término controlador, que es lo mismo), que son clases que representan una página web específica: por ejemplo, página de inicio; producto en e-shop; formulario de registro; feed sitemap, etc. La aplicación puede tener de uno a miles de presentadores.

La aplicación comienza pidiendo al llamado enrutador que decida a cuál de los presentadores debe pasar la petición actual para su procesamiento. El enrutador decide de quién es la responsabilidad. Mira la URL de entrada https://example.com/product/123, que quiere show un producto con id: 123 como acción. Es una buena costumbre escribir pares de presentador + acción separados por dos puntos como Product:show.

Así que el enrutador transforma la URL en un par Presenter:action + parámetros, en nuestro caso Product:show + id: 123. Puedes ver el aspecto de un enrutador en el archivo app/Core/RouterFactory.php y lo describiremos en detalle en el capítulo Enrutamiento.

Sigamos. La aplicación ya conoce el nombre del presentador y puede continuar. Creando un objeto ProductPresenter, que es el código del presentador Product. Más concretamente, le pide al contenedor DI que cree el presentador, porque producir objetos es su trabajo.

El presentador podría tener este aspecto:

class ProductPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private ProductRepository $repository,
	) {
	}

	public function renderShow(int $id): void
	{
		// obtenemos datos del modelo y los pasamos a la plantilla
		$this->template->product = $this->repository->getProduct($id);
	}
}

El presentador gestiona la solicitud. Y la tarea está clara: hacer la acción show con id: 123. Lo que en el lenguaje de los presentadores significa que se llama al método renderShow() y en el parámetro $id se obtiene 123.

Un presentador puede manejar múltiples acciones, es decir, tener múltiples métodos render<Action>(). Pero recomendamos diseñar los presentadores con una o las menos acciones posibles.

Así, se llamó al método renderShow(123), cuyo código es ficticio ejemplo, pero se puede ver en él cómo los datos se pasan a la plantilla, es decir, escribiendo a $this->template.

Posteriormente, el presentador devuelve la respuesta. Esta puede ser una página HTML, una imagen, un documento XML, el envío de un fichero desde disco, JSON o la redirección a otra página. Es importante destacar que, si no decimos explícitamente cómo responder (que es el caso de ProductPresenter), la respuesta será renderizar la plantilla con una página HTML. ¿Por qué? Pues porque en el 99% de los casos queremos dibujar una plantilla, así que el presentador toma este comportamiento por defecto y quiere facilitarnos el trabajo. Ese es el punto de Nette.

Ni siquiera necesitamos especificar qué plantilla renderizar; el framework deducirá la ruta por sí mismo. En el caso de la acción show, simplemente intenta cargar la plantilla show.latte en el directorio con la clase ProductPresenter. También intenta encontrar el diseño en el archivo @layout.latte (más información sobre la búsqueda de plantillas).

Posteriormente, se renderizan las plantillas. Esto completa la tarea del presentador y de toda la aplicación, y el trabajo está hecho. Si la plantilla no existiera, se devolvería una página de error 404. Puede leer más sobre los presentadores en la página Presentadores.

Sólo para estar seguros, intentemos recapitular todo el proceso con una URL ligeramente diferente:

  1. la URL será https://example.com
  2. arrancamos la aplicación, creamos un contenedor y ejecutamos Application::run()
  3. el router decodifica la URL como un par Home:default
  4. se crea un objeto HomePresenter
  5. se llama al método renderDefault() (si existe)
  6. se renderiza una plantilla default.latte con un diseño @layout.latte

Puede que ahora te hayas encontrado con un montón de conceptos nuevos, pero creemos que tienen sentido. Crear aplicaciones en Nette es pan comido.

Plantillas

Cuando se trata de las plantillas, Nette utiliza el sistema de plantillas Latte. Es por eso que los archivos con plantillas terminan con .latte. Se usa Latte porque es el sistema de plantillas más seguro para PHP, y al mismo tiempo el sistema más intuitivo. No tienes que aprender mucho nuevo, sólo necesitas saber PHP y algunas etiquetas Latte. Encontrarás todo en la documentación.

En la plantilla creamos enlaces a otros presentadores y acciones de la siguiente manera:

<a n:href="Product:show $productId">product detail</a>

Basta con escribir el conocido par Presenter:action en lugar de la URL real e incluir cualquier parámetro. El truco es n:href, que dice que este atributo será procesado por Nette. Y lo generará:

<a href="/product/456">product detail</a>

El enrutador antes mencionado es el encargado de generar la URL. De hecho, los routers en Nette son únicos en el sentido de que pueden realizar no sólo transformaciones de una URL a un par presentador:acción, sino también viceversa generar una URL a partir del nombre del presentador + acción + parámetros. Gracias a esto, en Nette se puede cambiar completamente la forma de la URL en toda la aplicación terminada sin cambiar ni un solo carácter de la plantilla o presentador sólo modificando el enrutador. Y gracias a esto, funciona la llamada canonización, que es otra característica única de Nette, que mejora el SEO (optimización de la buscabilidad en internet) evitando automáticamente la existencia de contenido duplicado en diferentes URLs. A muchos programadores esto les parece asombroso.

Componentes interactivos

Tenemos una cosa más que contarte sobre los presentadores: tienen un sistema de componentes incorporado. Los más veteranos recordaréis algo parecido de Delphi o ASP.NET Web Forms. React o Vue.js están construidos sobre algo remotamente similar. En el mundo de los frameworks PHP, esta es una característica completamente única.

Los componentes son unidades separadas reutilizables que colocamos en páginas (es decir, presentadores). Pueden ser formularios, datagrids, menús, encuestas, de hecho cualquier cosa que tenga sentido usar repetidamente. Podemos crear nuestros propios componentes o utilizar algunos de la enorme gama de componentes de código abierto.

Los componentes cambian radicalmente el enfoque del desarrollo de aplicaciones. Abrirán nuevas posibilidades para componer páginas a partir de unidades predefinidas. Y tienen algo en común con Hollywood.

Contenedor DI y configuración

El contenedor DI (fábrica de objetos) es el corazón de toda la aplicación.

No te preocupes, no es una caja negra mágica, como podría parecer por las palabras anteriores. En realidad, es una clase PHP bastante aburrida generada por Nette y almacenada en un directorio caché. Tiene un montón de métodos llamados como createServiceAbcd() y cada uno de ellos crea y devuelve un objeto. Sí, también hay un método createServiceApplication() que producirá Nette\Application\Application, que necesitamos en el archivo index.php para ejecutar la aplicación. Y hay métodos para producir presentadores individuales. Y así sucesivamente.

Los objetos que crea el contenedor DI se llaman servicios por alguna razón.

Lo realmente especial de esta clase es que no es programada por ti, sino por el framework. En realidad genera el código PHP y lo guarda en el disco. Tú sólo das instrucciones sobre qué objetos debe ser capaz de producir el contenedor y cómo exactamente. Y estas instrucciones están escritas en archivos de configuración en el formato NEON y por lo tanto tienen la extensión .neon.

Los archivos de configuración se utilizan únicamente para dar instrucciones al contenedor DI. Así, por ejemplo, si especifico la opción expiration: 14 days en la sección de sesión, el contenedor DI al crear el objeto Nette\Http\Session que representa la sesión llamará a su método setExpiration('14 days'), y así la configuración se hace realidad.

Hay todo un capítulo preparado para ti, describiendo qué se puede configurar y cómo definir tus propios servicios.

Una vez que te adentres en la creación de servicios, te encontrarás con la palabra autocableado. Este es un gadget que te hará la vida increíblemente más fácil. Puede pasar objetos automáticamente donde los necesites (en los constructores de tus clases, por ejemplo) sin que tengas que hacer nada. Usted encontrará que el contenedor DI en Nette es un pequeño milagro.

¿Y ahora qué?

Hemos repasado los principios básicos de las aplicaciones en Nette. Hasta aquí, muy superficialmente, pero pronto ahondarás en las profundidades y acabarás creando maravillosas aplicaciones web. ¿Dónde continuar? ¿Has probado el tutorial Crea tu primera aplicación?

Además de lo anterior, Nette tiene todo un arsenal de clases útiles, capa de base de datos, etc. Prueba a hacer clic a propósito en la documentación. O visita el blog. Descubrirás un montón de cosas interesantes.

Deje que el marco de traer un montón de alegría 💙

versión: 4.0