¿Cómo funcionan las aplicaciones?
Está leyendo el documento fundamental de la documentación de Nette. Aprenderá todo el principio de funcionamiento de las aplicaciones web. De la A a la Z, desde el momento del nacimiento hasta el último suspiro del script PHP. Después de leerlo, sabrá:
- cómo funciona todo
- qué son Bootstrap, Presenter y el contenedor DI
- cómo es la estructura de directorios
Estructura de directorios
Abra el ejemplo del esqueleto de una aplicación web llamado WebProject y mientras lee, puede mirar los archivos de los que se habla.
La estructura de directorios tiene este aspecto:
web-project/ ├── app/ ← directorio con la aplicación │ ├── Core/ ← clases básicas necesarias para el funcionamiento │ │ └── RouterFactory.php ← configuración de direcciones URL │ ├── Presentation/ ← presenters, plantillas, etc. │ │ ├── @layout.latte ← plantilla de layout │ │ └── Home/ ← directorio del presenter Home │ │ ├── HomePresenter.php ← clase del presenter Home │ │ └── default.latte ← plantilla de la acción default │ └── Bootstrap.php ← clase de arranque Bootstrap ├── bin/ ← scripts ejecutados desde la línea de comandos ├── config/ ← archivos de configuración │ ├── common.neon │ └── services.neon ├── log/ ← errores registrados ├── temp/ ← archivos temporales, caché, etc. ├── vendor/ ← librerías instaladas por Composer │ ├── ... │ └── autoload.php ← autoloading de todos los paquetes instalados ├── www/ ← directorio público o document-root del proyecto │ ├── .htaccess ← reglas mod_rewrite │ └── index.php ← archivo inicial con el que se inicia la aplicación └── .htaccess ← prohíbe el acceso a todos los directorios excepto www
Puede cambiar la estructura de directorios como desee, renombrar o mover carpetas, es completamente flexible. Nette, además, dispone de una autodetección inteligente y reconoce automáticamente la ubicación de la aplicación, incluida su base de URL.
En aplicaciones un poco más grandes, podemos dividir las carpetas con presenters y plantillas en subdirectorios y las clases en espacios de nombres, que llamamos módulos.
El directorio www/
representa el llamado directorio público o document-root del proyecto. Puede renombrarlo sin
necesidad de configurar nada más en el lado de la aplicación. Solo es necesario configurar el hosting
para que el document-root apunte a este directorio.
También puede descargar WebProject directamente incluyendo Nette usando Composer:
composer create-project nette/web-project
En Linux o macOS, establezca permisos de escritura para los
directorios log/
y temp/
.
La aplicación WebProject está lista para ejecutarse, no es necesario configurar absolutamente nada y puede mostrarla
directamente en el navegador accediendo a la carpeta www/
.
Petición HTTP
Todo comienza en el momento en que el usuario abre una página en el navegador. Es decir, cuando el navegador llama al servidor
con una petición HTTP. La petición apunta a un único archivo PHP, que se encuentra en el directorio público www/
,
y este es index.php
. Supongamos que se trata de una petición a la dirección
https://example.com/product/123
. Gracias a la configuración adecuada del
servidor, incluso esta URL se mapea al archivo index.php
y este se ejecuta.
Su tarea es:
- inicializar el entorno
- obtener la fábrica
- iniciar la aplicación Nette, que gestionará la petición
¿Qué fábrica? ¡No fabricamos tractores, sino páginas web! Espere, se explicará de inmediato.
Con las palabras “inicialización del entorno” nos referimos, por ejemplo, a que se activa Tracy, que es una herramienta increíble para el registro o la visualización de errores. En el servidor de producción, registra los errores, en el de desarrollo los muestra directamente. Por lo tanto, la inicialización también incluye la decisión de si el sitio web se ejecuta en modo de producción o de desarrollo. Para esto, Nette utiliza una autodetección inteligente: si ejecuta el sitio web en localhost, se ejecuta en modo de desarrollo. Por lo tanto, no necesita configurar nada y la aplicación está lista tanto para el desarrollo como para la implementación en producción. Estos pasos se realizan y se describen detalladamente en el capítulo sobre la clase Bootstrap.
El tercer punto (sí, saltamos el segundo, pero volveremos a él) es el inicio de la aplicación. La gestión de las peticiones
HTTP en Nette está a cargo de la clase Nette\Application\Application
(en adelante Application
), por lo
que cuando decimos iniciar la aplicación, nos referimos específicamente a llamar al método con el nombre apropiado
run()
en el objeto de esta clase.
Nette es un mentor que le guía para escribir aplicaciones limpias según metodologías probadas. Y una de las más probadas se
llama inyección de dependencias, abreviada como DI. En este momento no queremos cargarle con la explicación de DI, para
eso está un capítulo aparte, lo importante es la
consecuencia de que los objetos clave generalmente nos los creará una fábrica de objetos, que se llama contenedor DI
(abreviado como DIC). Sí, esa es la fábrica de la que hablamos hace un momento. Y también nos fabricará el objeto
Application
, por eso necesitamos primero el contenedor. Lo obtenemos usando la clase Configurator
y le
pedimos que fabrique el objeto Application
, llamamos al método run()
en él y así se inicia la
aplicación Nette. Exactamente esto sucede en el archivo index.php.
Nette Application
La clase Application tiene una única tarea: responder a la petición HTTP.
Las aplicaciones escritas en Nette se dividen en muchos llamados presenters (en otros frameworks puede encontrar el término controller, es lo mismo), que son clases, cada una de las cuales representa alguna página específica del sitio web: p. ej., la página de inicio; un producto en una tienda electrónica; un formulario de inicio de sesión; un feed sitemap, etc. Una aplicación puede tener desde uno hasta miles de presenters.
Application comienza pidiendo al llamado router que decida a cuál de los presenters pasar la petición actual para su
gestión. El router decide de quién es la responsabilidad. Mira la URL de entrada https://example.com/product/123
y,
basándose en cómo está configurado, decide que este es trabajo, por ejemplo, para el presenter Product
, al
que le pedirá como acción la visualización (show
) del producto con id: 123
. Es una buena
costumbre escribir el par presenter + acción separado por dos puntos como Product:show
.
Por lo tanto, el router transformó la URL en el par Presenter:action
+ parámetros, en nuestro caso
Product:show
+ id: 123
. Puede ver cómo es un router de este tipo en el archivo
app/Core/RouterFactory.php
y lo describimos detalladamente en el capítulo Enrutamiento.
Sigamos. Application ya conoce el nombre del presenter y puede continuar. Creando el objeto de la clase
ProductPresenter
, que es el código del presenter Product
. Más precisamente, pide al contenedor DI que
fabrique el presenter, porque para eso está él.
El presenter puede verse así:
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);
}
}
La gestión de la petición la asume el presenter. Y la tarea es clara: realiza la acción show
con
id: 123
. Lo que en el lenguaje de los presenters significa que se llama al método renderShow()
y en el
parámetro $id
recibe 123
.
Un presenter puede manejar múltiples acciones, es decir, tener múltiples métodos render<Action>()
. Pero
recomendamos diseñar presenters con una o la menor cantidad posible de acciones.
Entonces, se llamó al método renderShow(123)
, cuyo código es un ejemplo ficticio, pero puede ver en él cómo
se pasan los datos a la plantilla, es decir, escribiendo en $this->template
.
Posteriormente, el presenter devuelve una respuesta. Esta puede ser una página HTML, una imagen, un documento XML, el envío
de un archivo desde el disco, JSON o incluso una redirección a otra página. Es importante que si no decimos explícitamente
cómo debe responder (que es el caso de ProductPresenter
), la respuesta será la renderización de una plantilla con
una página HTML. ¿Por qué? Porque en el 99% de los casos queremos renderizar una plantilla, por lo que el presenter toma este
comportamiento como predeterminado y quiere facilitarnos el trabajo. Ese es el propósito de Nette.
Ni siquiera tenemos que indicar qué plantilla renderizar, él mismo deduce la ruta hacia ella. En el caso de la acción
show
, simplemente intenta cargar la plantilla show.latte
en el directorio con la clase
ProductPresenter
. También intentará localizar el layout en el archivo @layout.latte
(más detalles
sobre la búsqueda de plantillas).
Y posteriormente renderiza las plantillas. Con esto, la tarea del presenter y de toda la aplicación está completa y la obra está terminada. Si la plantilla no existiera, se devolvería una página con error 404. Puede leer más sobre los presenters en la página Presenters.
Por si acaso, intentemos recapitular todo el proceso con una URL ligeramente diferente:
- La URL será
https://example.com
- Arrancamos la aplicación, se crea el contenedor y se ejecuta
Application::run()
- El router decodifica la URL como el par
Home:default
- Se crea el objeto de la clase
HomePresenter
- Se llama al método
renderDefault()
(si existe) - Se renderiza la plantilla, p. ej.,
default.latte
con el layout, p. ej.,@layout.latte
Puede que ahora se haya encontrado con muchos conceptos nuevos, pero creemos que tienen sentido. Crear aplicaciones en Nette es increíblemente fácil.
Plantillas
Ya que hablamos de plantillas, en Nette se utiliza el sistema de plantillas Latte.
Por eso esas extensiones .latte
en las plantillas. Latte se utiliza, por un lado, porque es el sistema de plantillas
más seguro para PHP y, al mismo tiempo, el sistema más intuitivo. No necesita aprender mucho nuevo, le basta con conocer PHP y
algunas etiquetas. Todo lo aprenderá en la documentación.
En la plantilla, se crean enlaces a otros presenters y acciones de esta manera:
<a n:href="Product:show $productId">detalle del producto</a>
Simplemente, en lugar de la URL real, escribe el par conocido Presenter:action
e indica los parámetros
necesarios. El truco está en n:href
, que indica que este atributo será procesado por Nette. Y generará:
<a href="/product/456">detalle del producto</a>
La generación de URL está a cargo del router mencionado anteriormente. Es decir, los routers en Nette son excepcionales porque pueden realizar no solo transformaciones de URL al par presenter:action, sino también al revés, es decir, generar una URL a partir del nombre del presenter + acción + parámetros. Gracias a esto, en Nette puede cambiar completamente las formas de las URL en toda la aplicación terminada, sin cambiar un solo carácter en la plantilla o el presenter. Simplemente modificando el router. También gracias a esto funciona la llamada canonización, que es otra característica única de Nette que contribuye a un mejor SEO (optimización para motores de búsqueda) al evitar automáticamente la existencia de contenido duplicado en diferentes URL. Muchos programadores lo consideran asombroso.
Componentes interactivos
Sobre los presenters debemos revelarle una cosa más: tienen incorporado un sistema de componentes. Algo similar pueden recordar los veteranos de Delphi o ASP.NET Web Forms, algo remotamente similar es la base de React o Vue.js. En el mundo de los frameworks PHP, es una característica absolutamente única.
Los componentes son unidades reutilizables independientes que insertamos en las páginas (es decir, presenters). Pueden ser formularios, datagrids, menús, encuestas de votación, en realidad cualquier cosa que tenga sentido usar repetidamente. Podemos crear nuestros propios componentes o usar algunos de la enorme oferta de componentes de código abierto.
Los componentes influyen fundamentalmente en el enfoque para la creación de aplicaciones. Le abrirán nuevas posibilidades de componer páginas a partir de unidades prefabricadas. Y además tienen algo en común con Hollywood.
Contenedor DI y configuración
El contenedor DI o fábrica de objetos es el corazón de toda la aplicación.
No se preocupe, no es ninguna caja negra mágica, como podría parecer por las líneas anteriores. En realidad, es una clase
PHP bastante aburrida, que Nette genera y guarda en el directorio de caché. Tiene muchos métodos llamados como
createServiceAbcd()
y cada uno de ellos sabe cómo fabricar y devolver algún objeto. Sí, también está el método
createServiceApplication()
, que fabrica Nette\Application\Application
, que necesitábamos en el archivo
index.php
para iniciar la aplicación. Y hay métodos que fabrican los presenters individuales. Y así
sucesivamente.
A los objetos que crea el contenedor DI, por alguna razón, se les llama servicios.
Lo que es realmente especial de esta clase es que no la programa usted, sino el framework. Él realmente genera el código PHP
y lo guarda en el disco. Usted solo da instrucciones sobre qué objetos debe saber fabricar el contenedor y cómo exactamente. Y
estas instrucciones están escritas en archivos de configuración, para los
cuales se utiliza el formato NEON y, por lo tanto, también tienen la
extensión .neon
.
Los archivos de configuración sirven puramente para instruir al contenedor DI. Así que, por ejemplo, si indico en la sección
session la opción expiration: 14 days
, 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 hará realidad.
Hay un capítulo completo preparado para usted que describe qué se puede configurar y cómo definir servicios propios.
Una vez que se adentre un poco en la creación de servicios, se encontrará con la palabra autowiring. Esta es una característica que le simplificará la vida de manera increíble. Sabe cómo pasar automáticamente objetos donde los necesita (por ejemplo, en los constructores de sus clases), sin que tenga que hacer nada. Descubrirá que el contenedor DI en Nette es un pequeño milagro.
¿A dónde ir ahora?
Hemos repasado los principios básicos de las aplicaciones en Nette. Hasta ahora muy superficialmente, pero pronto profundizará y con el tiempo creará maravillosas aplicaciones web. ¿A dónde continuar ahora? ¿Ya ha probado el tutorial Escribiendo la primera aplicación?
Además de lo descrito anteriormente, Nette dispone de todo un arsenal de clases útiles, capa de base de datos, etc. Intente simplemente hacer clic en la documentación. O en el blog. Descubrirá muchas cosas interesantes.
Que el framework le traiga mucha alegría 💙