Comment fonctionnent les applications ?

Vous êtes en train de lire le document de base de la documentation Nette. Vous y apprendrez tous les principes des applications web. Nice de A à Z, du moment de la naissance jusqu'au dernier souffle du script PHP. Après la lecture, vous saurez :

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

Structure du répertoire

Ouvrez un exemple de squelette d'une application web appelée WebProject et vous pouvez observer les fichiers sur lesquels on écrit.

La structure des répertoires ressemble à ceci :

web-project/
├── app/                      ← répertoire avec application
│   ├── Presenters/           ← classes d'presenter
│   │   ├── HomePresenter.php ← Home classe des présentateurs
│   │   └── templates/        ← répertoire des modèles
│   │       ├── @layout.latte ← modèle de disposition partagée
│   │       └── Home/         ← Modèles pour le présentateur de la page d'accueil
│   │           └── default.latte  ← modèle pour l'action `default`
│   ├── Router/               ← configuration des adresses URL
│   └── Bootstrap.php         ← classe de démarrage Bootstrap
├── bin/                      ← scripts pour la ligne de commande
├── config/                   ← configuration files
│   ├── common.neon
│   └── services.neon
├── log/                      ← journaux d'erreurs
├── temp/                     ← fichiers temporaires, cache, …
├── vendor/                   ← bibliothèques installées par Composer
│   ├── ...
│   └── autoload.php          ← autoloading of libs installed by Composer
├── www/                      ← répertoire public, racine du document du projet
│   ├── .htaccess             ← règles du mod_rewrite, etc.
│   └── index.php             ← fichier initial qui lance l'application
└── .htaccess                 ← interdit l'accès à tous les répertoires sauf www

Vous pouvez modifier la structure des répertoires de n'importe quelle manière, renommer ou déplacer des dossiers, puis modifier simplement les chemins d'accès à log/ et temp/ dans le fichier Bootstrap.php et le chemin d'accès à ce fichier dans composer.json dans la section autoload. Rien de plus, pas de reconfiguration compliquée, pas de changements constants. Nette dispose d'une autodétection intelligente.

Pour les applications un peu plus importantes, nous pouvons diviser les dossiers contenant des présentateurs et des modèles en sous-répertoires (sur le disque) et en espaces de noms (dans le code), que nous appelons modules.

Le répertoire www/ est le répertoire public ou la racine du document du projet. Vous pouvez le renommer sans avoir à définir quoi que ce soit d'autre du côté de l'application. Il suffit de configurer l'hébergement pour que le document-root aille dans ce répertoire.

Vous pouvez également télécharger directement le projet Web, y compris Nette, en utilisant Composer:

composer create-project nette/web-project

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

L'application WebProject est prête à fonctionner, il n'est pas nécessaire de configurer quoi que ce soit d'autre et vous pouvez la visualiser directement dans le navigateur en accédant au dossier www/.

Demande HTTP

Tout commence lorsqu'un utilisateur ouvre la page dans un navigateur et que le navigateur frappe le serveur avec une requête HTTP. La requête est dirigée vers un fichier PHP situé dans le répertoire public www/, qui est index.php. Supposons qu'il s'agisse d'une requête vers https://example.com/product/123 et sera exécutée.

Sa tâche est la suivante :

  1. initialiser l'environnement
  2. récupérer l'usine
  3. lancer l'application Nette qui traite la demande.

Quel genre d'usine ? Nous ne produisons pas de tracteurs, mais des sites web ! Attendez, je vais vous expliquer tout de suite.

Par “initialiser l'environnement”, nous voulons dire, par exemple, que Tracy est activé, qui est un outil étonnant pour consigner ou visualiser les erreurs. Il enregistre les erreurs sur le serveur de production et les affiche directement sur le serveur de développement. Par conséquent, l'initialisation doit également décider si le site fonctionne en mode production ou en mode développement. Pour ce faire, Nette utilise l'autodétection : si vous exécutez le site sur localhost, il fonctionne en mode développeur. Vous n'avez rien à configurer et l'application est prête à être déployée aussi bien en développement qu'en production. Ces étapes sont réalisé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) consiste à démarrer l'application. Le traitement des demandes HTTP dans Nette est effectué par la classe Nette\Application\Application (ci-après dénommée Application), donc lorsque nous disons “lancer une application”, nous voulons dire appeler une méthode portant le nom run() sur un objet de cette classe.

Nette est un mentor qui vous guide pour écrire des applications propres grâce à des méthodologies éprouvées. Et la plus éprouvée est appelée injection de dépendance, en abrégé DI. Pour l'instant, nous ne voulons pas vous ennuyer avec l'explication de DI, car il y a un chapitre séparé, la chose importante ici est que les objets clés seront généralement créés par une usine pour les objets appelés conteneur DI (abrégé DIC). Oui, c'est la fabrique dont on a parlé il y a un moment. Et elle crée également l'objet Application pour nous, donc nous avons d'abord besoin d'un conteneur. Nous l'obtenons en utilisant la classe Configurator et la laissons produire l'objet Application, appelons la méthode run() et cela démarre l'application Nette. C'est exactement ce qui se passe dans le fichier index.php.

Application Nette

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

Les applications écrites dans Nette sont divisées en plusieurs présentateurs (dans d'autres frameworks, vous pouvez rencontrer le terme contrôleur, qui est le même), qui sont des classes représentant une page spécifique du site Web : par exemple, la page d'accueil, un produit dans une boutique en ligne, un formulaire d'inscription, un flux sitemap, etc. L'application peut avoir de un à plusieurs milliers de présentateurs.

L'application commence par demander à ce qu'on appelle le routeur de décider lequel des présentateurs doit transmettre la demande actuelle pour traitement. Le routeur décide de la responsabilité qui lui incombe. Il examine l'URL d'entrée https://example.com/product/123, qui veut show un produit avec id: 123 comme action. C'est une bonne habitude d'écrire une paire présentateur + action séparée par un deux-points comme Product:show.

Le routeur transforme donc l'URL en une paire Presenter:action + paramètres, dans notre cas Product:show + id: 123. Vous pouvez voir à quoi ressemble un routeur dans le fichier app/Router/RouterFactory.php et nous le décrirons en détail dans le chapitre Routage.

Continuons. L'application connaît déjà le nom du présentateur et peut continuer. En créant un objet ProductPresenter, qui est le code du présentateur Product. Plus précisément, elle demande au conteneur DI de créer le présentateur, car la production d'objets est son travail.

Le présentateur pourrait ressembler à ceci :

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

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

La demande est traitée par le présentateur. Et la tâche est claire : faire l'action show avec id: 123. Ce qui dans le langage des présentateurs signifie que la méthode renderShow() est appelée et que dans le paramètre $id on obtient 123.

Un présentateur peut gérer plusieurs actions, c'est-à-dire avoir plusieurs méthodes. render<Action>(). Mais nous recommandons de concevoir des présentateurs avec une ou aussi peu d'actions que possible.

Ainsi, 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 transmises au modèle, c'est-à-dire en écrivant à $this->template.

Ensuite, le présentateur renvoie la réponse. Cela peut être une page HTML, une image, un document XML, l'envoi d'un fichier depuis le disque, JSON ou la redirection vers une autre page. Il est important de noter que si nous ne disons pas explicitement comment répondre (ce qui est le cas de ProductPresenter), la réponse sera de rendre le modèle avec une page HTML. Pourquoi ? Eh bien, parce que dans 99% des cas, nous voulons dessiner un modèle, donc le présentateur prend ce comportement par défaut et veut nous faciliter le travail. C'est le point de vue de Nette.

Nous n'avons même pas besoin d'indiquer quel modèle dessiner, il dérive le chemin vers celui-ci selon une logique simple. Dans le cas du présentateur Product et de l'action show, il essaie de voir si l'un de ces fichiers modèles existe par rapport au répertoire où se trouve la classe ProductPresenter:

  • templates/Product/show.latte
  • templates/Product.show.latte

Il essaiera également de trouver la mise en page dans le fichier @layout.latte, puis il effectuera le rendu du modèle. La tâche du présentateur et de l'ensemble de l'application est maintenant terminée. Si le modèle n'existe pas, une page d'erreur 404 sera renvoyée. Vous pouvez en savoir plus sur les présentateurs sur la page Présentateurs.

Juste pour être sûr, essayons de récapituler l'ensemble du processus avec une URL légèrement différente :

  1. l'URL sera https://example.com
  2. nous démarrons l'application, nous créons un conteneur et nous l'exécutons Application::run()
  3. le routeur décode l'URL comme une paire Home:default
  4. un objet HomePresenter est créé
  5. la méthode renderDefault() est appelée (si elle existe)
  6. un modèle templates/Home/default.latte avec une mise en page templates/@layout.latte est rendu

Vous avez peut-être rencontré beaucoup de nouveaux concepts maintenant, mais nous pensons qu'ils ont un sens. Créer des applications dans Nette est un jeu d'enfant.

Modèles

En ce qui concerne les modèles, Nette utilise le système de modèles Latte. C'est pourquoi les fichiers contenant des modèles se terminent par .latte. Latte est utilisé parce que c'est le système de modèles le plus sûr pour PHP, et en même temps le système le plus intuitif. Vous n'avez pas besoin d'apprendre grand-chose de nouveau, il vous suffit de connaître PHP et quelques balises Latte. Vous trouverez tout dans la documentation.

Dans le template, nous créons un lien vers d'autres présentateurs et actions comme suit :

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

Il suffit d'écrire la paire familière Presenter:action au lieu de l'URL réelle et d'inclure tous les paramètres. L'astuce est n:href, qui indique que cet attribut sera traité par Nette. Et elle le fera :

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

Le routeur mentionné précédemment est chargé de générer l'URL. En fait, les routeurs de Nette sont uniques en ce sens qu'ils peuvent non seulement transformer une URL en une paire présentateur:action, mais aussi, à l'inverse, générer une URL à partir du nom du présentateur + action + paramètres. Grâce à cela, dans Nette, vous pouvez changer complètement la forme de l'URL dans toute l'application finie sans changer un seul caractère dans le modèle ou le présentateur, juste en modifiant le routeur. Et grâce à cela, ce que l'on appelle la canonisation fonctionne, ce qui est une autre caractéristique unique de Nette, qui améliore le SEO (optimisation de la facilité de recherche sur Internet) en empêchant automatiquement l'existence de contenu dupliqué à différentes URL. De nombreux programmeurs trouvent cela étonnant.

Composants interactifs

Nous avons encore une chose à vous dire sur les présentateurs : ils ont un système de composants intégré. Les plus anciens d'entre vous se souviennent peut-être de quelque chose de similaire dans Delphi ou ASP.NET Web Forms. React ou Vue.js sont construits sur quelque chose de très similaire. Dans le monde des frameworks PHP, il s'agit d'une fonctionnalité tout à fait unique.

Les composants sont des unités distinctes réutilisables que nous plaçons dans des pages (c'est-à-dire des présentateurs). Il peut s'agir de formulaires, de grilles de données, de menus, de sondages, en fait de tout ce qui peut être utilisé de manière répétée. Nous pouvons créer nos propres composants ou utiliser une partie de la vaste gamme de composants open source.

Les composants modifient fondamentalement l'approche du développement d'applications. Ils ouvrent de nouvelles possibilités pour composer des pages à partir d'unités prédéfinies. Et ils ont quelque chose en commun avec Hollywood.

Conteneur et configuration de DI

Le conteneur DI (fabrique d'objets) est le cœur de toute l'application.

Ne vous inquiétez pas, il ne s'agit pas d'une boîte noire magique, comme cela pourrait sembler dans les mots précédents. En fait, il s'agit d'une classe PHP assez ennuyeuse générée par Nette et stockée dans un répertoire de cache. Elle possède un grand nombre de méthodes nommées createServiceAbcd() et chacune d'entre elles crée et renvoie un objet. Oui, il y a aussi une méthode createServiceApplication() qui produit Nette\Application\Application, dont nous avons besoin dans le fichier index.php pour exécuter l'application. Et il y a des méthodes pour produire des présentateurs individuels. Et ainsi de suite.

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

Ce qui est vraiment spécial à propos de cette classe est qu'elle n'est pas programmée par vous, mais par le framework. Il génère en fait le code PHP et l'enregistre sur le disque. Vous donnez simplement des instructions sur les objets que le conteneur doit être capable de produire et comment exactement. Ces instructions sont écrites dans des fichiers de configuration au format NEON et portent donc l'extension .neon.

Les fichiers de configuration sont utilisés uniquement pour donner des instructions au conteneur DI. Ainsi, par exemple, si je spécifie l'option expiration: 14 days dans la section session, le conteneur DI, lorsqu'il créera l'objet Nette\Http\Session représentant la session, appellera sa méthode setExpiration('14 days'), et la configuration deviendra ainsi une réalité.

Un chapitre entier est prêt pour vous, décrivant ce qui peut être configuré et comment définir vos propres services.

Lorsque vous entrerez dans la création de services, vous rencontrerez le mot autowiring. Il s'agit d'un gadget qui vous rendra la vie incroyablement plus facile. Il permet de passer automatiquement des objets là où vous en avez besoin (dans les constructeurs de vos classes, par exemple) sans avoir à faire quoi que ce soit. Vous constaterez que le conteneur DI de Nette est un petit miracle.

Et maintenant ?

Nous avons passé en revue les principes de base des applications dans Nette. Jusqu'ici, très superficiellement, mais vous allez bientôt plonger dans les profondeurs et finalement créer de merveilleuses applications web. Où continuer ? Avez-vous essayé le tutoriel Créer votre première application?

En plus de ce qui précède, Nette dispose de tout un arsenal de classes utiles, d'une couche de base de données, etc. Essayez volontairement de cliquer sur la documentation. Ou visitez le blog. Vous découvrirez beaucoup de choses intéressantes.

Laissez le framework vous apporter beaucoup de joie 💙

version: 4.0