Comment fonctionnent les applications ?

Vous lisez actuellement le guide fondamental de la documentation Nette. Vous apprendrez tout le principe de fonctionnement des applications web. De A à Z, du début à la fin de l'exécution du script PHP. Après lecture, vous saurez :

  • comment tout cela fonctionne
  • ce qu'est Bootstrap, un Presenter et un conteneur DI
  • à quoi ressemble la structure des répertoires

Structure des répertoires

Ouvrez l'exemple de squelette d'application web appelé WebProject et pendant la lecture, examinez les fichiers mentionnés.

La structure des répertoires ressemble à quelque chose comme ceci :

web-project/
├── app/                      ← répertoire de l'application
│   ├── Core/                 ← classes de base nécessaires au fonctionnement
│   │   └── RouterFactory.php ← configuration des adresses URL
│   ├── Presentation/         ← presenters, templates & co.
│   │   ├── @layout.latte     ← template de layout
│   │   └── Home/             ← répertoire du presenter Home
│   │       ├── HomePresenter.php ← classe du presenter Home
│   │       └── default.latte ← template de l'action default
│   └── Bootstrap.php         ← classe de démarrage Bootstrap
├── bin/                      ← scripts exécutés depuis la ligne de commande
├── config/                   ← fichiers de configuration
│   ├── common.neon
│   └── services.neon
├── log/                      ← erreurs journalisées
├── temp/                     ← fichiers temporaires, cache, …
├── vendor/                   ← bibliothèques installées par Composer
│   ├── ...
│   └── autoload.php          ← autoloading de tous les paquets installés
├── www/                      ← répertoire public ou document-root du projet
│   ├── .htaccess             ← règles mod_rewrite
│   └── index.php             ← fichier initial par lequel l'application est lancée
└── .htaccess                 ← interdit l'accès à tous les répertoires sauf www

Vous pouvez modifier la structure des répertoires comme vous le souhaitez, renommer ou déplacer des dossiers, elle est entièrement flexible. De plus, Nette dispose d'une autodétection intelligente et reconnaît automatiquement l'emplacement de l'application, y compris sa base d'URL.

Pour les applications légèrement plus grandes, nous pouvons diviser les dossiers avec les presenters et les templates en sous-répertoires et les classes en espaces de noms, que nous appelons modules.

Le répertoire www/ représente le répertoire public ou document-root du projet. Vous pouvez le renommer sans aucune configuration supplémentaire côté application. Il suffit de configurer l'hébergement pour que le document-root pointe vers ce répertoire.

Vous pouvez également télécharger directement WebProject incluant Nette en utilisant Composer :

composer create-project nette/web-project

Sous Linux ou macOS, définissez les permissions d'écriture pour les répertoires log/ et temp/.

L'application WebProject est prête à être lancée, il n'y a absolument rien à configurer et vous pouvez l'afficher directement dans le navigateur en accédant au dossier www/.

Requête HTTP

Tout commence au moment où l'utilisateur ouvre une page dans le navigateur. C'est-à-dire lorsque le navigateur contacte le serveur avec une requête HTTP. La requête pointe vers un seul fichier PHP, qui se trouve dans le répertoire public www/, et c'est index.php. Supposons qu'il s'agisse d'une requête pour l'adresse https://example.com/product/123. Grâce à une configuration serveur appropriée, cette URL est également associée au fichier index.php, qui est ensuite exécuté.

Ses tâches sont :

  1. initialiser l'environnement
  2. obtenir la factory
  3. lancer l'application Nette, qui traitera la requête

Quelle factory ? Nous ne fabriquons pas de tracteurs, mais des sites web ! Patientez, cela va s'éclaircir.

Par “initialisation de l'environnement”, nous entendons par exemple l'activation de Tracy, qui est un outil exceptionnel pour la journalisation ou la visualisation des erreurs. Sur un serveur de production, il journalise les erreurs, sur un serveur de développement, il les affiche directement. L'initialisation comprend donc également la décision de savoir si le site fonctionne en mode production ou développement. Pour cela, Nette utilise une autodétection intelligente : si vous lancez le site sur localhost, il fonctionne en mode développement. Vous n'avez donc rien à configurer et l'application est prête à la fois pour le développement et le déploiement en production. Ces étapes sont effectuées et décrites en détail dans le chapitre sur la classe Bootstrap.

Le troisième point (oui, nous avons sauté le deuxième, mais nous y reviendrons) est le lancement de l'application. Le traitement des requêtes HTTP dans Nette est géré par la classe Nette\Application\Application (ci-après Application), donc quand nous disons lancer l'application, nous entendons spécifiquement appeler la méthode run() sur l'objet de cette classe.

Nette est un mentor qui vous guide vers l'écriture d'applications propres selon des méthodologies éprouvées. Et l'une de celles qui sont absolument les plus éprouvées s'appelle l'injection de dépendances, en abrégé DI. Pour le moment, nous ne voulons pas vous surcharger avec l'explication de la DI, il y a un chapitre séparé pour cela, l'important est la conséquence que les objets clés nous seront généralement créés par une factory d'objets appelée conteneur DI (abrégé en DIC). Oui, c'est la factory dont il était question il y a un instant. Et elle nous créera aussi l'objet Application, c'est pourquoi nous avons d'abord besoin du conteneur. Nous l'obtenons à l'aide de la classe Configurator et nous lui demandons de créer l'objet Application, nous appelons sa méthode run() et l'application Nette est ainsi lancée. C'est exactement ce qui se passe dans le fichier index.php.

Nette Application

La classe Application n'a qu'une seule tâche : répondre à la requête HTTP.

Les applications écrites en Nette sont divisées en de nombreux presenters (dans d'autres frameworks, vous pouvez rencontrer le terme controller, c'est la même chose), qui sont des classes, dont chacune représente une page web spécifique : par ex. la page d'accueil ; un produit dans une boutique en ligne ; un formulaire de connexion ; un flux sitemap, etc. Une application peut avoir d'un à des milliers de presenters.

Application commence par demander au routeur de décider à quel presenter transmettre la requête actuelle pour traitement. Le routeur détermine qui est responsable. Il regarde l'URL d'entrée https://example.com/product/123 et sur la base de sa configuration, décide que c'est le travail, par ex., du presenter Product, auquel il demandera comme action l'affichage (show) du produit avec id: 123. Il est de bonne pratique de noter la paire presenter + action séparée par deux-points, comme Product:show.

Le routeur a donc transformé l'URL en une paire Presenter:action + paramètres, dans notre cas Product:show + id: 123. À quoi ressemble un tel routeur, vous pouvez le voir dans le fichier app/Core/RouterFactory.php et nous le décrivons en détail dans le chapitre Routage.

Continuons. Application connaît déjà le nom du presenter et peut continuer. En créant l'objet de la classe ProductPresenter, qui est le code du presenter Product. Plus précisément, il demande au conteneur DI de créer le presenter, car c'est son rôle.

Le presenter peut ressembler à ceci :

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

	public function renderShow(int $id): void
	{
		// nous obtenons les données du modèle et les passons au template
		$this->template->product = $this->repository->getProduct($id);
	}
}

Le presenter prend en charge le traitement de la requête. Et la tâche est claire : effectuer l'action show avec id: 123. Ce qui, dans le langage des presenters, signifie que la méthode renderShow() est appelée et reçoit 123 dans le paramètre $id.

Un presenter peut gérer plusieurs actions, c'est-à-dire avoir plusieurs méthodes render<Action>(). Mais nous recommandons de concevoir des presenters avec une seule ou le moins d'actions possible.

Donc, la méthode renderShow(123) a été appelée, dont le code est un exemple fictif, mais vous pouvez y voir comment les données sont passées au template, c'est-à-dire en écrivant dans $this->template.

Ensuite, le presenter retourne une réponse. Cela peut être une page HTML, une image, un document XML, l'envoi d'un fichier depuis le disque, du JSON ou même une redirection vers une autre page. Il est important que si nous ne disons pas explicitement comment répondre (ce qui est le cas de ProductPresenter), la réponse sera le rendu d'un template avec une page HTML. Pourquoi ? Parce que dans 99% des cas, nous voulons rendre un template, donc le presenter considère ce comportement comme celui par défaut et veut nous faciliter le travail. C'est le but de Nette.

Nous n'avons même pas besoin de spécifier quel template rendre, il déduit le chemin lui-même. Dans le cas de l'action show, il essaie simplement de charger le template show.latte dans le répertoire de la classe ProductPresenter. Il essaie également de trouver le layout dans le fichier @layout.latte (plus de détails sur la recherche de templates).

Et ensuite, il rend les templates. La tâche du presenter et de toute l'application est ainsi terminée. Si le template n'existait pas, une page d'erreur 404 serait retournée. Vous en apprendrez plus sur les presenters sur la page Presenters.

Pour être sûr, essayons de récapituler tout le processus avec une URL légèrement différente :

  1. L'URL sera https://example.com
  2. nous démarrons l'application, le conteneur est créé et Application::run() est lancé
  3. le routeur décode l'URL comme la paire Home:default
  4. l'objet de la classe HomePresenter est créé
  5. la méthode renderDefault() est appelée (si elle existe)
  6. le template (par ex. default.latte) avec le layout (par ex. @layout.latte) est rendu

Vous avez peut-être rencontré de nombreux nouveaux termes maintenant, mais nous espérons qu'ils ont du sens. Créer des applications avec Nette est incroyablement simple et agréable.

Templates

Puisque nous avons parlé des templates, Nette utilise le système de templates Latte. D'une part parce que c'est le système de templates le plus sécurisé pour PHP, et d'autre part parce que c'est aussi le système le plus intuitif. Vous n'avez pas besoin d'apprendre beaucoup de nouveautés, la connaissance de PHP et de quelques balises suffit. Vous apprendrez tout dans la documentation.

Dans le template, des liens sont créés vers d'autres presenters & actions comme ceci :

<a n:href="Product:show $productId">détail du produit</a>

Au lieu d'une URL réelle, écrivez simplement la paire connue Presenter:action et spécifiez d'éventuels paramètres. L'astuce réside dans n:href, qui indique que cet attribut sera traité par Nette. Et il générera :

<a href="/product/456">détail du produit</a>

La génération d'URL est gérée par le routeur mentionné précédemment. En effet, les routeurs dans Nette sont exceptionnels car ils peuvent effectuer non seulement des transformations d'URL en paire presenter:action, mais aussi l'inverse, c'est-à-dire générer une URL à partir du nom du presenter + action + paramètres. Grâce à cela, dans Nette, vous pouvez complètement changer les formes d'URL dans toute une application terminée, sans changer un seul caractère dans le template ou le presenter. Juste en modifiant le routeur. Grâce à cela fonctionne également la canonisation, qui est une autre caractéristique unique de Nette, contribuant à un meilleur SEO (optimisation pour les moteurs de recherche) en empêchant automatiquement l'existence de contenu dupliqué sur différentes URL. De nombreux programmeurs trouvent cela impressionnant.

Composants interactifs

Nous devons vous dire encore une chose sur les presenters : ils ont un système de composants intégré. Les vétérans peuvent se souvenir de quelque chose de similaire dans Delphi ou ASP.NET Web Forms ; React ou Vue.js sont basés sur quelque chose de vaguement similaire. Dans le monde des frameworks PHP, c'est une caractéristique absolument unique.

Les composants sont des unités autonomes et réutilisables que nous insérons dans les pages (c'est-à-dire les presenters). Il peut s'agir de formulaires, de datagrids, de menus, de sondages, en fait de tout ce qu'il est judicieux d'utiliser de manière répétée. Nous pouvons créer nos propres composants ou utiliser certains de la vaste offre de composants open source.

Les composants influencent fondamentalement l'approche de la création d'applications. Ils vous ouvrent de nouvelles possibilités de composition de pages à partir d'unités préfabriquées. Et en plus, ils ont quelque chose en commun avec Hollywood.

Conteneur DI et configuration

Le conteneur DI ou factory d'objets est le cœur de toute l'application.

Ne vous inquiétez pas, ce n'est pas une boîte noire magique, comme on pourrait le penser d'après les lignes précédentes. En fait, c'est une classe PHP plutôt simple, générée par Nette et stockée dans le répertoire cache. Elle a beaucoup de méthodes nommées comme createServiceAbcd() et chacune d'elles sait comment créer et retourner un objet. Oui, il y a aussi une méthode createServiceApplication(), qui crée Nette\Application\Application, dont nous avions besoin dans le fichier index.php pour lancer l'application. Et il y a des méthodes créant les presenters individuels. Et ainsi de suite.

Les objets que le conteneur DI crée sont, pour une raison quelconque, appelés services.

Ce qui est vraiment spécial à propos de cette classe, c'est que ce n'est pas vous qui la programmez, mais le framework. Il génère réellement le code PHP et le sauvegarde sur le disque. Vous donnez simplement des instructions sur les objets que le conteneur doit savoir créer et comment précisément. Et ces instructions sont écrites dans des fichiers de configuration, pour lesquels le format NEON est utilisé et qui ont donc aussi l'extension .neon.

Les fichiers de configuration servent uniquement à instruire le conteneur DI. Ainsi, par exemple, si j'indique dans la section session l'option expiration: 14 days, alors le conteneur DI, lors de la création de l'objet Nette\Http\Session représentant la session, appellera sa méthode setExpiration('14 days') et la configuration deviendra ainsi réalité.

Il y a tout un chapitre préparé pour vous décrivant tout ce qui peut être configuré et comment définir vos propres services.

Dès que vous vous familiariserez un peu avec la création de services, vous rencontrerez le mot autowiring. C'est une astuce qui vous simplifiera incroyablement la vie. Elle sait comment passer automatiquement des objets là où vous en avez besoin (par exemple dans les constructeurs de vos classes), sans que vous ayez à faire quoi que ce soit. Vous découvrirez que le conteneur DI de Nette est un petit miracle.

Où aller ensuite ?

Nous avons parcouru les principes de base des applications en Nette. Pour l'instant très superficiellement, mais vous approfondirez bientôt et créerez avec le temps de merveilleuses applications web. Où continuer ? Avez-vous déjà essayé le tutoriel Écrivons notre première application ?

En plus de ce qui a été décrit ci-dessus, Nette dispose de tout un arsenal de classes utiles, d'une couche de base de données, etc. Essayez juste de parcourir la documentation. Ou le blog. Vous découvrirez beaucoup de choses intéressantes.

Que le framework vous apporte beaucoup de joie 💙

version: 4.0