Tutoriel Magento 2 : Comment créer un module complet
Publié: 2022-03-11Magento est actuellement la plus grande plateforme de commerce électronique open source au monde. En raison de sa base de code riche en fonctionnalités et extensible, les commerçants avec de grandes et petites opérations partout dans le monde l'utilisent pour une grande variété de projets.
Magento 1 existe depuis huit ans, et son successeur, Magento 2, est sorti fin 2015, améliorant les points faibles de la version précédente tels que :
- Performance améliorée
- Suite de tests automatisée officielle
- Meilleure interface utilisateur principale
- Nouvelle base de code frontale plus moderne
- Une façon plus modulaire de développer des modules, avec des fichiers contenus dans le code Magento au lieu d'être éparpillés un peu partout
- Réduction du nombre de conflits entre les modules essayant de personnaliser la même fonctionnalité
Un peu plus d'un an plus tard, et l'amélioration est visible, même si tous les problèmes évoqués ne sont pas totalement résolus. Maintenant, il est tout à fait sûr de dire que Magento 2 est un logiciel beaucoup plus robuste que son prédécesseur. Certaines des améliorations présentes dans Magento 2 sont :
- Tests unitaires et d'intégration, y compris un moyen officiel et documenté de les créer pour des modules personnalisés
- Des modules vraiment modularisés, ayant tous leurs fichiers placés dans un seul répertoire
- Un système de modèles plus riche, permettant au développeur de thèmes de créer une hiérarchie de modèles à n niveaux
- Une série de modèles de conception utiles adoptés dans tout le code, améliorant la qualité du code et diminuant la probabilité d'erreurs créées par les modules. Ceux-ci incluent l'injection automatique de dépendances, les contrats de service, les référentiels et les usines, pour n'en nommer que quelques-uns.
- Intégration native à Varnish en tant que système de mise en cache pleine page, ainsi qu'à Redis pour la gestion des sessions et du cache
- Prise en charge de PHP7
La courbe d'apprentissage de Magento 2, avec tous ces changements, est devenue encore plus raide. Dans ce guide, je compte vous montrer comment développer votre premier module Magento 2, et vous orienter dans la bonne direction pour poursuivre vos études. Allons-y !
Prérequis du tutoriel Magento 2
Il est important que vous ayez une bonne compréhension des technologies/concepts suivants afin de suivre la suite de cet article :
- Programmation orientée objet (POO)
- PHP
- Espaces de noms
- MySQL
- Utilisation basique de bash
De tout ce qui précède, la POO est probablement la plus importante. Magento a été initialement créé par une équipe de développeurs Java expérimentés, et leur héritage est certainement visible dans toute la base de code. Si vous n'êtes pas très sûr de vos compétences en POO, il peut être judicieux de les revoir avant de commencer votre travail avec la plateforme.
Présentation de l'architecture de Magento 2
L'architecture de Magento a été conçue dans le but de rendre le code source aussi modulaire et extensible que possible. L'objectif final de cette approche est de lui permettre d'être facilement adaptée et personnalisée en fonction des besoins de chaque projet.
La personnalisation signifie généralement modifier le comportement du code de la plateforme. Dans la majorité des systèmes, cela signifie changer le code « principal ». Dans Magento, si vous suivez les meilleures pratiques, c'est quelque chose que vous pouvez éviter la plupart du temps, ce qui permet à un magasin de se tenir au courant des derniers correctifs de sécurité et des dernières fonctionnalités de manière fiable.
Magento 2 est un système Model View ViewModel (MVVM). Tout en étant étroitement liée à son frère Model View Controller (MVC), une architecture MVVM fournit une séparation plus robuste entre les couches Model et View. Vous trouverez ci-dessous une explication de chacune des couches d'un système MVVM :
- Le modèle contient la logique métier de l'application et dépend d'une classe associée, le ResourceModel, pour l'accès à la base de données. Les modèles s'appuient sur des contrats de service pour exposer leurs fonctionnalités aux autres couches de l'application.
- La vue est la structure et la disposition de ce qu'un utilisateur voit sur un écran - le HTML réel. Ceci est réalisé dans les fichiers PHTML distribués avec les modules. Les fichiers PHTML sont associés à chaque ViewModel dans les fichiers XML de mise en page, qui seraient appelés classeurs dans le dialecte MVVM. Les fichiers de mise en page peuvent également affecter des fichiers JavaScript à utiliser dans la page finale.
- Le ViewModel interagit avec le calque Model, exposant uniquement les informations nécessaires au calque View. Dans Magento 2, cela est géré par les classes Block du module. Notez que cela faisait généralement partie du rôle de contrôleur d'un système MVC. Sur MVVM, le contrôleur est uniquement responsable de la gestion du flux d'utilisateurs, ce qui signifie qu'il reçoit les demandes et indique au système de rendre une vue ou de rediriger l'utilisateur vers une autre route.
Un module Magento 2 se compose de certains, sinon tous, éléments de l'architecture décrite ci-dessus. L'architecture globale est décrite ci-dessous (source):
Un module Magento 2 peut à son tour définir des dépendances externes en utilisant Composer, le gestionnaire de dépendances de PHP. Dans le diagramme ci-dessus, vous voyez que les modules principaux de Magento 2 dépendent de Zend Framework, Symfony ainsi que d'autres bibliothèques tierces.
Vous trouverez ci-dessous la structure de Magento/Cms, un module central de Magento 2 chargé de gérer la création de pages et de blocs statiques.
Chaque dossier contient une partie de l'architecture, comme suit :
- Api : contrats de service, définition des interfaces de service et des interfaces de données
- Block : Les ViewModels de notre architecture MVVM
- Contrôleur : contrôleurs, chargés de gérer le flux de l'utilisateur tout en interagissant avec le système
- etc : Fichiers XML de configuration—Le module se définit lui-même ainsi que ses parties (routes, modèles, blocs, observateurs et tâches cron) dans ce dossier. Les fichiers etc peuvent également être utilisés par des modules non essentiels pour remplacer la fonctionnalité des modules principaux.
- Helper : classes d'assistance contenant du code utilisé dans plusieurs couches d'application. Par exemple, dans le module Cms, les classes d'assistance sont responsables de la préparation du HTML pour la présentation au navigateur.
- i18n : Contient les fichiers CSV d'internationalisation, utilisés pour la traduction
- Modèle : pour les modèles et les modèles de ressources
- Observateur : contient des observateurs ou des modèles qui « observent » les événements système. Habituellement, lorsqu'un tel événement est déclenché, l'observateur instancie un modèle pour gérer la logique métier nécessaire pour un tel événement.
- Configuration : classes de migration, responsables de la création de schémas et de données
- Test : Tests unitaires
- Ui : éléments de l'interface utilisateur tels que les grilles et les formulaires utilisés dans l'application d'administration
- vue : fichiers de mise en page (XML) et fichiers de modèle (PHTML) pour l'application frontale et d'administration
Il est également intéressant de noter qu'en pratique, tout le fonctionnement interne de Magento 2 vit à l'intérieur d'un module. Dans l'image ci-dessus, vous pouvez voir, par exemple, Magento_Checkout
, responsable du processus de paiement, et Magento_Catalog
, responsable de la gestion des produits et des catégories. Fondamentalement, ce que cela nous dit, c'est qu'apprendre à travailler avec des modules est la partie la plus importante pour devenir un développeur Magento 2.
D'accord, après cette introduction relativement brève à l'architecture du système et à la structure des modules, faisons quelque chose de plus concret, d'accord ? Ensuite, nous passerons par le didacticiel traditionnel du blog Web afin de vous familiariser avec Magento 2 et sur la bonne voie pour devenir un développeur Magento 2. Avant cela, nous devons mettre en place un environnement de développement. Allons-y !
Configuration de l'environnement de développement du module Magento 2
Au moment d'écrire ces lignes, nous pouvions utiliser la DevBox officielle de Magento 2, qui est un conteneur Docker de Magento 2. Docker sur macOS est quelque chose que je considère toujours comme inutilisable, du moins avec un système qui dépend fortement d'E/S disque rapides comme Magento 2. Nous allons donc le faire de manière traditionnelle : installer tous les packages de manière native sur notre propre machine.
Configuration du serveur
Tout installer est sûrement un peu plus fastidieux, mais le résultat final sera un environnement de développement Magento ultra-rapide. Croyez-moi, vous économiserez des heures de travail en ne dépendant pas de Docker pour votre développement Magento 2.
Ce didacticiel suppose un environnement sur macOS avec Brew installé dessus. Si ce n'est pas le cas pour vous, les bases resteront les mêmes, ne changeant que la façon dont vous installez les packages. Commençons par installer tous les packages :
brew install mysql nginxb php70 php70-imagick php70-intl php70-mcrypt
Démarrez ensuite les services :
brew services start mysql brew services start php70 sudo brew services start nginx
Ok, maintenant nous allons faire pointer un domaine vers notre adresse de bouclage. Ouvrez le fichier hosts dans n'importe quel éditeur, mais assurez-vous que vous disposez des autorisations de superutilisateur. Faire cela avec Vim serait :
sudo vim /etc/hosts
Ajoutez ensuite la ligne suivante :
127.0.0.1 magento2.dev
Nous allons maintenant créer un vhost dans Nginx :
vim /usr/local/etc/nginx/sites-available/magento2dev.conf
Ajoutez le contenu suivant :
server { listen 80; server_name magento2.dev; set $MAGE_ROOT /Users/yourusername/www/magento2dev; set $MAGE_MODE developer; # Default magento Nginx config starts below root $MAGE_ROOT/pub; index index.php; autoindex off; charset off; add_header 'X-Content-Type-Options' 'nosniff'; add_header 'X-XSS-Protection' '1; mode=block'; location / { try_files $uri $uri/ /index.php?$args; } location /pub { location ~ ^/pub/media/(downloadable|customer|import|theme_customization/.*\.xml) { deny all; } alias $MAGE_ROOT/pub; add_header X-Frame-Options "SAMEORIGIN"; } location /static/ { if ($MAGE_MODE = "production") { expires max; } location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { add_header Cache-Control "public"; add_header X-Frame-Options "SAMEORIGIN"; expires +1y; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { add_header Cache-Control "no-store"; add_header X-Frame-Options "SAMEORIGIN"; expires off; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } add_header X-Frame-Options "SAMEORIGIN"; } location /media/ { try_files $uri $uri/ /get.php?$args; location ~ ^/media/theme_customization/.*\.xml { deny all; } location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { add_header Cache-Control "public"; add_header X-Frame-Options "SAMEORIGIN"; expires +1y; try_files $uri $uri/ /get.php?$args; } location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { add_header Cache-Control "no-store"; add_header X-Frame-Options "SAMEORIGIN"; expires off; try_files $uri $uri/ /get.php?$args; } add_header X-Frame-Options "SAMEORIGIN"; } location /media/customer/ { deny all; } location /media/downloadable/ { deny all; } location /media/import/ { deny all; } location ~ /media/theme_customization/.*\.xml$ { deny all; } location /errors/ { try_files $uri =404; } location ~ ^/errors/.*\.(xml|phtml)$ { deny all; } location ~ cron\.php { deny all; } location ~ (index|get|static|report|404|503)\.php$ { try_files $uri =404; fastcgi_pass 127.0.0.1:9000; fastcgi_param PHP_FLAG "session.auto_start=off \n suhosin.session.cryptua=off"; fastcgi_param PHP_VALUE "memory_limit=768M \n max_execution_time=60"; fastcgi_read_timeout 60s; fastcgi_connect_timeout 60s; fastcgi_param MAGE_MODE $MAGE_MODE; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } # Default magento Nginx config finishes below client_max_body_size 20M; }
Si vous n'avez jamais traité avec Nginx auparavant, ce fichier pourrait vous effrayer, alors laissez-nous vous expliquer les petits détails ici, car il éclairera également certains des rouages de Magento. Les premières lignes indiquent simplement à Nginx que nous utilisons le port HTTP par défaut, et que notre domaine est magento2.dev
:
listen 80; server_name magento2.dev;
Ensuite, nous définissons des variables d'environnement. Le premier $MAGE_ROOT
— contient le chemin vers notre base de code. Notez que vous devrez modifier le chemin racine pour qu'il corresponde à votre nom d'utilisateur/chemin de dossier, quel que soit l'endroit où vous prévoyez de placer la source :
set $MAGE_ROOT /Users/yourusername/www/magento2dev;
La deuxième variable $MAGE_MODE
- définit le mode d'exécution de notre boutique. Comme nous développons un module, nous utiliserons le mode développeur. Cela nous permet de coder plus rapidement, car nous n'aurons pas à compiler ou à déployer des fichiers statiques pendant le développement. Les autres modes sont production et défaut. L'utilisation réelle de ce dernier n'est pas encore claire.
set $MAGE_MODE developer;
Une fois ces variables définies, nous définissons le chemin racine du vhost. Notez que nous suffixons la variable $MAGE_ROOT
avec le dossier /pub
, ne rendant qu'une partie de notre boutique disponible sur le Web.
root $MAGE_ROOT/pub;
Nous définissons ensuite notre fichier d'index — le fichier que nginx chargera lorsque le fichier demandé n'existe pas — comme index.php. Ce script, $MAGE_ROOT/pub/index.php
, est le principal point d'entrée pour les clients visitant à la fois le panier et les applications d'administration. Quelle que soit l'URL demandée, index.php sera chargé et le processus de répartition du routeur lancé.
index index.php;
Ensuite, nous désactivons certaines fonctionnalités de Nginx. Tout d'abord, nous désactivons autoindex
, qui afficherait une liste de fichiers lorsque vous demandez un dossier, mais ne spécifiez pas de fichier et aucun index n'est présent. Deuxièmement, nous désactivons charset
, ce qui permettrait à Nginx d'ajouter automatiquement des en-têtes Charset à la réponse.
autoindex off; charset off;
Ensuite, nous définissons quelques en-têtes de sécurité :
add_header 'X-Content-Type-Options' 'nosniff'; add_header 'X-XSS-Protection' '1; mode=block';
Cet emplacement, /
, pointe vers notre dossier racine $MAGE_ROOT/pub
, et redirige essentiellement toute requête reçue vers notre contrôleur frontal index.php, ainsi que les arguments de la requête :
location / { try_files $uri $uri/ /index.php?$args; }
La partie suivante peut être un peu déroutante, mais elle est assez simple. Il y a quelques lignes, nous avons défini notre racine comme $MAGE_ROOT/pub
. Il s'agit de la configuration recommandée et la plus sécurisée, car la majeure partie du code n'est pas visible sur le Web. Mais ce n'est pas la seule façon de configurer le serveur Web. En fait, la plupart des serveurs Web partagés ont une configuration par défaut, qui consiste à faire pointer votre serveur Web vers votre dossier Web. Pour ces utilisateurs, l'équipe Magento a préparé ce fichier pour ces cas, lorsque la racine est définie comme $MAGE_ROOT
avec l'extrait suivant :
location /pub { location ~ ^/pub/media/(downloadable|customer|import|theme_customization/.*\.xml) { deny all; } alias $MAGE_ROOT/pub; add_header X-Frame-Options "SAMEORIGIN"; }
Gardez à l'esprit que, dans la mesure du possible, il est préférable que votre serveur Web pointe vers le $MAGE_ROOT/pub
. Votre boutique sera ainsi plus sécurisée.
Ensuite, nous avons l'emplacement statique $MAGE_ROOT/pub/static
. Ce dossier est initialement vide et rempli automatiquement avec les fichiers statiques des modules et des thèmes, tels que les fichiers image, CSS, JS, etc. Ici, nous définissons essentiellement des valeurs de cache pour les fichiers statiques et, lorsque le fichier demandé ne existe, redirigez-le vers $MAGE_ROOT/pub/static.php
. Ce script analysera, entre autres, la requête et copiera ou créera un lien symbolique vers le fichier spécifié à partir du module ou du thème correspondant, selon le mode d'exécution défini. De cette façon, les fichiers statiques de votre module résideront dans le dossier de nos modules, mais seront servis directement à partir du dossier public vhost :
location /static/ { if ($MAGE_MODE = "production") { expires max; } location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { add_header Cache-Control "public"; add_header X-Frame-Options "SAMEORIGIN"; expires +1y; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { add_header Cache-Control "no-store"; add_header X-Frame-Options "SAMEORIGIN"; expires off; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } add_header X-Frame-Options "SAMEORIGIN"; }
Ensuite, nous refusons l'accès Web à certains dossiers et fichiers restreints :
location /media/customer/ { deny all; } location /media/downloadable/ { deny all; } location /media/import/ { deny all; } location ~ /media/theme_customization/.*\.xml$ { deny all; } location /errors/ { try_files $uri =404; } location ~ ^/errors/.*\.(xml|phtml)$ { deny all; } location ~ cron\.php { deny all; }
Et le dernier bit est l'endroit où nous chargeons php-fpm et lui disons d'exécuter index.php chaque fois que l'utilisateur le frappe :
location ~ (index|get|static|report|404|503)\.php$ { try_files $uri =404; fastcgi_pass 127.0.0.1:9000; fastcgi_param PHP_FLAG "session.auto_start=off \n suhosin.session.cryptua=off"; fastcgi_param PHP_VALUE "memory_limit=768M \n max_execution_time=60"; fastcgi_read_timeout 60s; fastcgi_connect_timeout 60s; fastcgi_param MAGE_MODE $MAGE_MODE; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }
Avec cela hors de notre chemin, enregistrez le fichier, puis activez-le en tapant les commandes suivantes :
ln -s /usr/local/etc/nginx/sites-available/magento2dev.conf \ /usr/local/etc/nginx/sites-enabled/magento2dev.conf sudo brew services restart nginx
Comment installer Magento 2
D'accord, à ce stade, votre machine répond aux exigences de Magento 2, il ne manque que la bête elle-même. Rendez-vous sur le site Magento et créez un compte si vous n'en avez pas encore. Après cela, rendez-vous sur la page de téléchargement et téléchargez la dernière version (2.1.5, au moment de la rédaction) :
Sélectionnez le format .tar.bz2 et téléchargez-le. Procédez ensuite à son extraction et définissez les bonnes autorisations de dossier et de fichier pour que Magento 2 puisse fonctionner :
mkdir ~/www/magento2dev cd ~/www/magento2dev tar -xjf ~/Downloads/Magento-CE-2.1.5-2017-02-20-05-39-14.tar.bz2 find var vendor pub/static pub/media app/etc -type f -exec chmod u+w {} \; find var vendor pub/static pub/media app/etc -type d -exec chmod u+w {} \; chmod u+x bin/magento
Maintenant, pour installer les tables de la base de données et créer les fichiers de configuration nécessaires, nous allons exécuter cette commande depuis le terminal :
./bin/magento setup:install --base-url=http://magento2.dev/ \ --db-host=127.0.0.1 --db-name=magento2 --db-user=root \ --db-password=123 --admin-firstname=Magento --admin-lastname=User \ [email protected] --admin-user=admin \ --admin-password=admin123 --language=en_US --currency=USD \ --timezone=America/Chicago --use-rewrites=1 --backend-frontname=admin
N'oubliez pas de changer le nom de la base de données ( db-name
), l'utilisateur ( db-user
) et le mot de passe ( db-password
) pour qu'ils correspondent à ceux que vous avez utilisés lors de l'installation de MySQL, et c'est tout ! Cette commande installera tous les modules de Magento 2, en créant les tables et les fichiers de configuration requis. Une fois terminé, ouvrez votre navigateur et rendez-vous sur http://magento2.dev/. Vous devriez voir une installation propre de Magento 2 avec le thème Luma par défaut :
Si vous vous dirigez vers http://magento2.dev/admin, vous devriez voir la page de connexion de l'application Admin :
Utilisez ensuite les identifiants ci-dessous pour vous connecter :
Utilisateur : admin Mot de passe : admin123
Nous sommes enfin prêts à commencer à écrire notre code !
Création de notre premier module Magento 2
Pour terminer notre module, nous devrons créer les fichiers suivants, et je vous guiderai tout au long du processus. Nous aurons besoin:
- Quelques fichiers d'enregistrement passe-partout, pour faire connaître à Magento notre module Blog
- Un fichier d'interface, pour définir notre contrat de données pour la Poste
- Un Post Model, pour représenter un Post dans notre code, implémentant l'interface de données Post
- Un Post Resource Model, pour lier le Post Model à la base de données
- Une collection de publications, pour récupérer plusieurs publications à la fois dans la base de données à l'aide du modèle de ressources
- Deux classes de migration, pour configurer le schéma et le contenu de notre table
- Deux actions : une pour répertorier tous les messages et une autre pour afficher chaque message individuellement
- Deux fichiers de chaque bloc, vue et mise en page : un de chaque pour l'action de liste et un de chaque pour la vue
Tout d'abord, jetons un coup d'œil à la structure du dossier du code source principal, afin que nous puissions définir où placer notre code. La façon dont nous avons installé tout le code principal de Magento 2, ainsi que toutes ses dépendances, vit dans le dossier du vendor
du compositeur.
Enregistrement de notre module
Nous conserverons notre code dans un dossier séparé, app/code
. Le nom de chaque module est sous la forme Namespace_ModuleName
, et son emplacement sur le système de fichiers doit refléter ce nom, app/code/Namespace/ModuleName
pour cet exemple. En suivant ce modèle, nous nommerons notre module Toptal_Blog
et placerons nos fichiers sous app/code/Toptal/Blog
. Allez-y et créez cette structure de dossiers.
Maintenant, nous devons créer quelques fichiers passe-partout afin d'enregistrer notre module auprès de Magento. Tout d'abord, créez app/code/Toptal/Blog/composer.json
:
{}
Ce fichier sera chargé par Composer à chaque fois que vous l'exécuterez. Même si nous n'utilisons pas réellement Composer avec notre module, nous devons le créer pour que Composer soit heureux.
Nous allons maintenant enregistrer notre module auprès de Magento. Allez-y et créez app/code/Toptal/Blog/registration.php
:
<?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Toptal_Blog', __DIR__ );
Ici, nous appelons la méthode register
de la classe ComponentRegistrar
, en envoyant deux paramètres : la chaîne 'module'
, qui est le type de composant que nous enregistrons, et le nom de notre module, 'Toptal_Blog'
. Avec ces informations, le chargeur automatique de Magento sera conscient de notre espace de noms et saura où chercher nos classes et nos fichiers XML.
Une chose intéressante à noter ici est que nous avons le type du composant ( MODULE
) envoyé en tant que paramètre à la fonction \Magento\Framework\Component\ComponentRegistrar::register
. Non seulement pouvons-nous enregistrer des modules, mais nous pouvons également enregistrer d'autres types de composants. Par exemple, les thèmes, les bibliothèques externes et les modules linguistiques sont également enregistrés à l'aide de cette même méthode.
Continuons, créons notre dernier fichier d'inscription, app/code/Toptal/Blog/etc/module.xml
:
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd"> <module name="Toptal_Blog" setup_version="0.1.0"> <sequence> <module name="Magento_Directory" /> <module name="Magento_Config" /> </sequence> </module> </config>
Ce fichier contient des informations très importantes sur notre module. Elles sont:
- Le nom du module est à nouveau présent, exposant notre nom de module à la configuration de Magento.
- La version d'installation de Magento, qui sera utilisée par Magento pour décider quand exécuter les scripts de migration de base de données.
- Les dépendances de notre module—Comme nous écrivons un module simple, nous ne dépendons que de deux modules principaux de Magento :
Magento_Directory
etMagento_Config
.
Maintenant, nous avons un module qui devrait être reconnaissable par Magento 2. Vérifions-le en utilisant la CLI de Magento 2.
Tout d'abord, nous devons désactiver le cache de Magento. Les mécanismes de cache de Magento méritent un article qui leur est dédié. Pour l'instant, comme nous développons un module et que nous souhaitons que nos modifications soient reconnues par Magento instantanément sans avoir besoin de vider le cache à tout moment, nous allons simplement le désactiver. Depuis la ligne de commande, exécutez :
./bin/magento cache:disable
Voyons ensuite si Magento est déjà au courant de nos modifications en regardant le statut des modules. Exécutez simplement la commande suivante :
./bin/magento module:status
Le résultat du dernier devrait ressembler à :
Notre module est là, mais comme le montre la sortie, il est toujours désactivé. Pour l'activer, exécutez :
./bin/magento module:enable Toptal_Blog
Cela aurait dû le faire. Pour être sûr, vous pouvez appeler à nouveau module:status
et rechercher le nom de notre module dans la liste activée :
Gestion du stockage des données
Maintenant que nous avons activé notre module, nous devons créer la table de base de données qui contient nos articles de blog. Voici le schéma de la table que nous voulons créer :
Champ | Taper | Nul | Clé | Défaut |
---|---|---|---|---|
post_id | int(10) non signé | NON | IRP | NUL |
Titre | texte | NON | NUL | |
contenu | texte | NON | NUL | |
créé à | horodatage | NON | CURRENT_TIMESTAMP |

Nous y parvenons en créant la classe InstallSchema
, qui est responsable de la gestion de l'installation de notre migration de schéma. Le fichier se trouve dans app/code/Toptal/Blog/Setup/InstallSchema.php
et a le contenu suivant :
<?php namespace Toptal\Blog\Setup; use \Magento\Framework\Setup\InstallSchemaInterface; use \Magento\Framework\Setup\ModuleContextInterface; use \Magento\Framework\Setup\SchemaSetupInterface; use \Magento\Framework\DB\Ddl\Table; /** * Class InstallSchema * * @package Toptal\Blog\Setup */ class InstallSchema implements InstallSchemaInterface { /** * Install Blog Posts table * * @param SchemaSetupInterface $setup * @param ModuleContextInterface $context */ public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) { $setup->startSetup(); $tableName = $setup->getTable('toptal_blog_post'); if ($setup->getConnection()->isTableExists($tableName) != true) { $table = $setup->getConnection() ->newTable($tableName) ->addColumn( 'post_id', Table::TYPE_INTEGER, null, [ 'identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true ], 'ID' ) ->addColumn( 'title', Table::TYPE_TEXT, null, ['nullable' => false], 'Title' ) ->addColumn( 'content', Table::TYPE_TEXT, null, ['nullable' => false], 'Content' ) ->addColumn( 'created_at', Table::TYPE_TIMESTAMP, null, ['nullable' => false, 'default' => Table::TIMESTAMP_INIT], 'Created At' ) ->setComment('Toptal Blog - Posts'); $setup->getConnection()->createTable($table); } $setup->endSetup(); } }
Si vous analysez la méthode d' install
, vous remarquerez qu'elle crée simplement notre table et ajoute ses colonnes une par une.
Pour déterminer quand exécuter une migration de schéma, Magento conserve une table avec toutes les versions de configuration actuelles pour chaque module, et chaque fois qu'une version de module change, ses classes de migration sont initialisées. Cette table est setup_module
, et si vous regardez le contenu de cette table, vous verrez qu'il n'y a aucune référence à notre module jusqu'à présent. Alors, changeons cela. Depuis un terminal, lancez la commande suivante :
./bin/magento setup:upgrade
Cela vous montrera une liste de tous les modules et de ses scripts de migration qui ont été exécutés, y compris le nôtre :
Maintenant, depuis votre client MySQL de préférence, vous pouvez vérifier si la table a bien été créée :
Et à la table setup_module
, il y a maintenant une référence à notre module, son schéma et sa version de données :
Ok, et qu'en est-il des mises à jour de schéma ? Ajoutons quelques publications à ce tableau via une mise à niveau pour vous montrer comment procéder. Tout d'abord, placez le setup_version
sur notre fichier etc/module.xml
:
Nous créons maintenant notre fichier app/code/Toptal/Blog/Setup/UpgradeData.php
, qui est responsable des migrations de données (et non de schéma) :
<?php namespace Toptal\Blog\Setup; use \Magento\Framework\Setup\UpgradeDataInterface; use \Magento\Framework\Setup\ModuleContextInterface; use \Magento\Framework\Setup\ModuleDataSetupInterface; /** * Class UpgradeData * * @package Toptal\Blog\Setup */ class UpgradeData implements UpgradeDataInterface { /** * Creates sample blog posts * * @param ModuleDataSetupInterface $setup * @param ModuleContextInterface $context * @return void */ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) { $setup->startSetup(); if ($context->getVersion() && version_compare($context->getVersion(), '0.1.1') < 0 ) { $tableName = $setup->getTable('toptal_blog_post'); $data = [ [ 'title' => 'Post 1 Title', 'content' => 'Content of the first post.', ], [ 'title' => 'Post 2 Title', 'content' => 'Content of the second post.', ], ]; $setup ->getConnection() ->insertMultiple($tableName, $data); } $setup->endSetup(); } }
Vous pouvez voir qu'elle est très similaire à notre classe Install. La seule différence est qu'il implémente une UpgradeDataInterface
au lieu de InstallSchemaInterface
, et la méthode principale est appelée upgrade
. Avec cette méthode, vous vérifiez la version installée du module actuel et, lorsqu'elle est plus petite que la vôtre, lancez les modifications nécessaires. Dans notre exemple, nous vérifions si la version actuelle est inférieure à 0.1.1 dans la ligne suivante à l'aide de la fonction version_compare
:
if ($context->getVersion() && version_compare($context->getVersion(), '0.1.1') < 0 ) {
L'appel $context->getVersion()
renverra 0.1.0 lorsque la commande CLI setup:upgrade
est appelée pour la première fois. Ensuite, les exemples de données sont chargés dans la base de données et notre version passe à la 0.1.1. Pour que cela fonctionne, allez-y et lancez un setup:upgrade
:
./bin/magento setup:upgrade
Et puis vérifiez les résultats dans le tableau des messages :
Et à la table setup_module
:
Notez que, même si nous avons ajouté des données à notre table à l'aide du processus de migration, il aurait également été possible de modifier le schéma. Le processus est le même; vous n'utiliseriez que UpgradeSchemaInterface
au lieu de UpgradeDataInterface
.
Définition du modèle pour les publications
Passant à autre chose, si vous vous souvenez de notre aperçu de l'architecture, notre prochain bloc de construction serait le billet de blog ResourceModel. Le modèle de ressource est très simple et indique simplement la table à laquelle le modèle se "connectera", ainsi que sa clé primaire. Nous allons créer notre ResourceModel sur app/code/Toptal/Blog/Model/ResourceModel/Post.php
avec le contenu suivant :
<?php namespace Toptal\Blog\Model\ResourceModel; use \Magento\Framework\Model\ResourceModel\Db\AbstractDb; class Post extends AbstractDb { /** * Post Abstract Resource Constructor * @return void */ protected function _construct() { $this->_init('toptal_blog_post', 'post_id'); } }
Toutes les opérations ResourceModel, sauf si vous avez besoin de quelque chose de différent des opérations CRUD habituelles, sont gérées par la classe parent AbstractDb
.
Nous aurons également besoin d'un autre ResourceModel, une Collection. La collection sera chargée d'interroger la base de données pour plusieurs publications à l'aide de notre ResourceModel et de renvoyer une série de modèles instanciés et remplis d'informations. Nous créons le fichier app/code/Toptal/Blog/Model/ResourceModel/Post/Collection.php
avec le contenu suivant :
<?php namespace Toptal\Blog\Model\ResourceModel\Post; use \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; class Collection extends AbstractCollection { /** * Remittance File Collection Constructor * @return void */ protected function _construct() { $this->_init('Toptal\Blog\Model\Post', 'Toptal\Blog\Model\ResourceModel\Post'); } }
Notez que dans le constructeur, nous mentionnons simplement le modèle, qui représentera l'entité de publication tout au long de notre code, et le ResourceModel, qui récupérera les informations dans la base de données.
La pièce manquante pour cette couche est le Post Model lui-même. Le modèle doit contenir tous les attributs que nous avons définis dans notre schéma, ainsi que toute logique métier dont vous pourriez avoir besoin. Suivant le modèle de Magento 2, nous devons créer une interface de données à partir de laquelle notre modèle s'étendra. Nous plaçons l'interface à app/code/Toptal/Blog/Api/Data/PostInterface.php
, et elle devrait contenir les noms des champs de la table, ainsi que les méthodes pour y accéder :
<?php namespace Toptal\Blog\Api\Data; interface PostInterface { /**#@+ * Constants for keys of data array. Identical to the name of the getter in snake case */ const POST_; const TITLE = 'title'; const CONTENT = 'content'; const CREATED_AT = 'created_at'; /**#@-*/ /** * Get Title * * @return string|null */ public function getTitle(); /** * Get Content * * @return string|null */ public function getContent(); /** * Get Created At * * @return string|null */ public function getCreatedAt(); /** * Get ID * * @return int|null */ public function getId(); /** * Set Title * * @param string $title * @return $this */ public function setTitle($title); /** * Set Content * * @param string $content * @return $this */ public function setContent($content); /** * Set Crated At * * @param int $createdAt * @return $this */ public function setCreatedAt($createdAt); /** * Set ID * * @param int $id * @return $this */ public function setId($id); }
Passons maintenant à l'implémentation du modèle, dans app/code/Toptal/Blog/Model/Post.php
. Nous allons créer les méthodes définies à l'interface. Nous spécifierons également une balise de cache via la constante CACHE_TAG
et, au niveau du constructeur, nous spécifierons le ResourceModel qui sera responsable de l'accès à la base de données pour notre modèle.
<?php namespace Toptal\Blog\Model; use \Magento\Framework\Model\AbstractModel; use \Magento\Framework\DataObject\IdentityInterface; use \Toptal\Blog\Api\Data\PostInterface; /** * Class File * @package Toptal\Blog\Model * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Post extends AbstractModel implements PostInterface, IdentityInterface { /** * Cache tag */ const CACHE_TAG = 'toptal_blog_post'; /** * Post Initialization * @return void */ protected function _construct() { $this->_init('Toptal\Blog\Model\ResourceModel\Post'); } /** * Get Title * * @return string|null */ public function getTitle() { return $this->getData(self::TITLE); } /** * Get Content * * @return string|null */ public function getContent() { return $this->getData(self::CONTENT); } /** * Get Created At * * @return string|null */ public function getCreatedAt() { return $this->getData(self::CREATED_AT); } /** * Get ID * * @return int|null */ public function getId() { return $this->getData(self::POST_ID); } /** * Return identities * @return string[] */ public function getIdentities() { return [self::CACHE_TAG . '_' . $this->getId()]; } /** * Set Title * * @param string $title * @return $this */ public function setTitle($title) { return $this->setData(self::TITLE, $title); } /** * Set Content * * @param string $content * @return $this */ public function setContent($content) { return $this->setData(self::CONTENT, $content); } /** * Set Created At * * @param string $createdAt * @return $this */ public function setCreatedAt($createdAt) { return $this->setData(self::CREATED_AT, $createdAt); } /** * Set ID * * @param int $id * @return $this */ public function setId($id) { return $this->setData(self::POST_ID, $id); } }
Creating Views
Now we are moving one layer up, and will start the implementation of our ViewModel and Controller. To define a route in the front-end (shopping cart) application, we need to create the file app/code/Toptal/Blog/etc/frontend/routes.xml
with the following contents:
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router> <route frontName="blog"> <module name="Toptal_Blog"/> </route> </router> </config>
List of Posts at the Index Page
Here, we are basically telling Magento that our module, Toptal_Blog
, will be responsible for responding to routes under http://magento2.dev/blog (notice the frontName attribute of the route). Next up is the action, at app/code/Toptal/Blog/Controller/Index/Index.php
:
<?php namespace Toptal\Blog\Controller\Index; use \Magento\Framework\App\Action\Action; use \Magento\Framework\View\Result\PageFactory; use \Magento\Framework\View\Result\Page; use \Magento\Framework\App\Action\Context; use \Magento\Framework\Exception\LocalizedException; class Index extends Action { /** * @var PageFactory */ protected $resultPageFactory; /** * @param Context $context * @param PageFactory $resultPageFactory * * @codeCoverageIgnore * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Context $context, PageFactory $resultPageFactory ) { parent::__construct( $context ); $this->resultPageFactory = $resultPageFactory; } /** * Prints the blog from informed order id * @return Page * @throws LocalizedException */ public function execute() { $resultPage = $this->resultPageFactory->create(); return $resultPage; } }
Our action is defining two methods. Let us take a closer look at them:
The constructor method simply sends the
$context
parameter to its parent method, and sets the$resultPageFactory
parameter to an attribute for later use. At this point it is useful to know the Dependency Injection design pattern, as that is what is happening here. In Magento 2's case we have automatic dependency injection. This means that whenever a class instantiation occurs, Magento will automatically try to instantiate all of the class constructor parameters (dependencies) and inject it for you as constructor parameters. It identifies which classes to instantiate for each parameter by inspecting the type hints, in this caseContext
andPageFactory
.The
execute
method is responsible for the action execution itself. In our case, we are simply telling Magento to render its layout by returning aMagento\Framework\View\Result\Page
object. This will trigger the layout rendering process, which we will create in a bit.
Now you should see a blank page at the url http://magento2.dev/blog/index/index. We still need to define the layout structure for that route, and its corresponding Block (our ViewModel) and the template file which will present the data to our user.
The layout structure for the front-end application is defined under view/frontend/layout
, and the file name must reflect our route. As our route is blog/index/index
, the layout file for that route will be app/code/Toptal/Blog/view/frontend/layout/blog_index_index.xml
:
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="content"> <block class="Toptal\Blog\Block\Posts" name="posts.list" template="Toptal_Blog::post/list.phtml" /> </referenceContainer> </body> </page>
Here, we must define three very important structures in the Magento layout structure: Blocks, Containers, and Templates.
Blocks are the ViewModel part of our MVVM architecture, which was explained in earlier sections. They are the building blocks of our template structure.
Containers contain and output Blocks. They hold blocks together in nice hierarchical structures, and help in making things make sense when the layout for a page is being processed.
Templates are PHMTL (mixed HTML and PHP) files used by a special type of block in Magento. You can make calls to methods of a
$block
variable from within a template. The variable is always defined in the template context. You will be invoking your Block's methods by doing so, and thus allowing you to pull information from the ViewModel layer to the actual presentation.
With that extra information at hand, we can analyze the XML layout structure above. This layout structure is basically telling Magento that, when a request is made to the blog/index/index
route, a Block of the type Toptal\Blog\Block\Posts
is to be added to the content
container, and the template which will be used to render it is Toptal_blog::post/list.phtml
.
This leads us to the creation of our two remaining files. Our Block, located at app/code/Toptal/Blog/Block/Posts.php
:
<?php namespace Toptal\Blog\Block; use \Magento\Framework\View\Element\Template; use \Magento\Framework\View\Element\Template\Context; use \Toptal\Blog\Model\ResourceModel\Post\Collection as PostCollection; use \Toptal\Blog\Model\ResourceModel\Post\CollectionFactory as PostCollectionFactory; use \Toptal\Blog\Model\Post; class Posts extends Template { /** * CollectionFactory * @var null|CollectionFactory */ protected $_postCollectionFactory = null; /** * Constructor * * @param Context $context * @param PostCollectionFactory $postCollectionFactory * @param array $data */ public function __construct( Context $context, PostCollectionFactory $postCollectionFactory, array $data = [] ) { $this->_postCollectionFactory = $postCollectionFactory; parent::__construct($context, $data); } /** * @return Post[] */ public function getPosts() { /** @var PostCollection $postCollection */ $postCollection = $this->_postCollectionFactory->create(); $postCollection->addFieldToSelect('*')->load(); return $postCollection->getItems(); } /** * For a given post, returns its url * @param Post $post * @return string */ public function getPostUrl( Post $post ) { return '/blog/post/view/id/' . $post->getId(); } }
Cette classe est plutôt simple, et son objectif est uniquement de charger les articles à afficher, et de fournir une méthode getPostUrl
au modèle. Il y a cependant certaines choses à remarquer.
Si vous vous souvenez, nous n'avons pas défini de Toptal\Blog\Model\ResourceModel\Post\CollectionFactory
. Nous avons uniquement défini le Toptal\Blog\Model\ResourceModel\Post\Collection
. Alors, comment cela fonctionne-t-il même? Pour chaque classe que vous définissez dans votre module, Magento 2 créera automatiquement une Factory pour vous. Les usines ont deux méthodes : create
, qui renverra une nouvelle instance pour chaque appel, et get
, qui renverra toujours la même instance chaque fois qu'elle est appelée, utilisée pour implémenter le modèle Singleton.
Le troisième paramètre de notre Block, $data
, est un tableau optionnel. Comme il est facultatif et n'a pas d'indice de type, il ne sera pas injecté par le système d'injection automatique. Il est important de noter que les paramètres de constructeur optionnels doivent toujours être positionnés en dernier dans les paramètres. Par exemple, le constructeur de Magento\Framework\View\Element\Template
, notre classe parent, a ces paramètres :
public function __construct( Template\Context $context, array $data = [] ) { ...
Comme nous voulions ajouter notre CollectionFactory
aux paramètres du constructeur après avoir étendu la classe Template, nous devions le faire avant le paramètre optionnel, sinon l'injection ne fonctionnerait pas :
public function __construct( Context $context, PostCollectionFactory $postCollectionFactory, array $data = [] ) { ...
Au niveau de la méthode getPosts
, à laquelle notre modèle accédera plus tard, nous appelons simplement la méthode create
de la PostCollectionFactory
, qui nous renverra une nouvelle PostCollection
et nous permettra de récupérer nos publications dans la base de données et de l'envoyer à notre réponse.
Et pour finir le layout de cette route, voici notre template PHTML, app/code/Toptal/Blog/view/frontend/templates/post/list.phtml
:
<?php /** @var Toptal\Blog\Block\Posts $block */ ?> <h1>Toptal Posts</h1> <?php foreach($block->getPosts() as $post): ?> <?php /** @var Toptal\Blog\Model\Post */ ?> <h2><a href="<?php echo $block->getPostUrl($post);?>"><?php echo $post->getTitle(); ?></a></h2> <p><?php echo $post->getContent(); ?></p> <?php endforeach; ?>
Notez qu'ici, nous pouvons voir la couche View accéder à notre ModelView ( $block->getPosts()
) qui à son tour utilise un ResourceModel (la collection) pour récupérer nos modèles ( Toptal\Blog\Model\Post
) de la base de données. Dans chaque modèle, chaque fois que vous souhaitez accéder aux méthodes de son bloc, une variable $block
sera définie et attendra vos appels.
Vous devriez maintenant pouvoir voir la liste des messages en appuyant simplement à nouveau sur notre itinéraire.
Affichage des messages individuels
Maintenant, si vous cliquez sur un titre de publication, vous obtiendrez un 404, alors réglons cela. Avec toute notre structure en place, cela devient assez simple. Nous n'aurons qu'à créer les éléments suivants :
- Une nouvelle action, responsable du traitement des requêtes vers la route
blog/post/view
- Un bloc pour afficher le message
- Un modèle PHTML, responsable de la vue elle-même
- Un fichier de mise en page pour la route blog/post/view, rassemblant ces derniers éléments.
Notre nouvelle action est assez simple. Il recevra simplement l' id
du paramètre de la demande et l'enregistrera dans le registre principal de Magento, un référentiel central d'informations disponibles tout au long d'un cycle de demande unique. En faisant cela, nous rendrons l'ID disponible pour le bloc plus tard. Le fichier doit être situé dans app/code/Toptal/Blog/Controller/Post/View.php
et voici son contenu :
<?php namespace Toptal\Blog\Controller\Post; use \Magento\Framework\App\Action\Action; use \Magento\Framework\View\Result\PageFactory; use \Magento\Framework\View\Result\Page; use \Magento\Framework\App\Action\Context; use \Magento\Framework\Exception\LocalizedException; use \Magento\Framework\Registry; class View extends Action { const REGISTRY_KEY_POST_; /** * Core registry * @var Registry */ protected $_coreRegistry; /** * @var PageFactory */ protected $_resultPageFactory; /** * @param Context $context * @param Registry $coreRegistry * @param PageFactory $resultPageFactory * * @codeCoverageIgnore * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Context $context, Registry $coreRegistry, PageFactory $resultPageFactory ) { parent::__construct( $context ); $this->_coreRegistry = $coreRegistry; $this->_resultPageFactory = $resultPageFactory; } /** * Saves the blog id to the register and renders the page * @return Page * @throws LocalizedException */ public function execute() { $this->_coreRegistry->register(self::REGISTRY_KEY_POST_ID, (int) $this->_request->getParam('id')); $resultPage = $this->_resultPageFactory->create(); return $resultPage; } }
Notez que nous avons ajouté le paramètre $coreRegistry
à notre __construct
et que nous l'avons enregistré en tant qu'attribut pour une utilisation ultérieure. Au niveau de la méthode execute
, nous récupérons le paramètre id
de la requête et l'enregistrons. Nous utilisons également une constante de classe, self::REGISTRY_KEY_POST_ID
comme clé du registre, et nous utiliserons cette même constante dans notre bloc pour faire référence à l'identifiant dans le registre.
Créons le bloc, à app/code/Toptal/Blog/Block/View.php
avec le contenu suivant :
<?php namespace Toptal\Blog\Block; use \Magento\Framework\Exception\LocalizedException; use \Magento\Framework\View\Element\Template; use \Magento\Framework\View\Element\Template\Context; use \Magento\Framework\Registry; use \Toptal\Blog\Model\Post; use \Toptal\Blog\Model\PostFactory; use \Toptal\Blog\Controller\Post\View as ViewAction; class View extends Template { /** * Core registry * @var Registry */ protected $_coreRegistry; /** * Post * @var null|Post */ protected $_post = null; /** * PostFactory * @var null|PostFactory */ protected $_postFactory = null; /** * Constructor * @param Context $context * @param Registry $coreRegistry * @param PostFactory $postCollectionFactory * @param array $data */ public function __construct( Context $context, Registry $coreRegistry, PostFactory $postFactory, array $data = [] ) { $this->_postFactory = $postFactory; $this->_coreRegistry = $coreRegistry; parent::__construct($context, $data); } /** * Lazy loads the requested post * @return Post * @throws LocalizedException */ public function getPost() { if ($this->_post === null) { /** @var Post $post */ $post = $this->_postFactory->create(); $post->load($this->_getPostId()); if (!$post->getId()) { throw new LocalizedException(__('Post not found')); } $this->_post = $post; } return $this->_post; } /** * Retrieves the post id from the registry * @return int */ protected function _getPostId() { return (int) $this->_coreRegistry->registry( ViewAction::REGISTRY_KEY_POST_ID ); } }
Au niveau du bloc de vue, nous définissons une méthode protégée _getPostId
, qui récupérera simplement l'ID de publication du registre principal. La méthode publique getPost
chargera à son tour la publication et lancera une exception si la publication n'existe pas. Lancer une exception ici fera que Magento affichera son écran d'erreur par défaut, ce qui n'est peut-être pas la meilleure solution dans un tel cas, mais nous le garderons ainsi par souci de simplicité.
Passons à notre modèle PHTML. Ajoutez app/code/Toptal/Blog/view/frontend/templates/post/view.phtml
avec le contenu suivant :
<?php /** @var Toptal\Blog\Block\View $block */ ?> <h1><?php echo $block->getPost()->getTitle(); ?></h1> <p><?php echo $block->getPost()->getContent(); ?></p>
Agréable et simple, en accédant simplement à la méthode getPost
du bloc View que nous avons créée précédemment.
Et, pour tout mettre ensemble, nous créons un fichier de mise en page pour notre nouvelle route sur app/code/Toptal/Blog/view/frontend/layout/blog_post_view.xml
avec le contenu suivant :
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="content"> <block class="Toptal\Blog\Block\View" name="post.view" template="Toptal_Blog::post/view.phtml" /> </referenceContainer> </body> </page>
Cela fait la même chose que nous faisions auparavant. Il ajoute simplement Toptal\Blog\Block\View
au conteneur de content
, avec Toptal_Blog::post/view.phtml
comme modèle associé.
Pour le voir en action, dirigez simplement votre navigateur vers http://magento2.dev/blog/post/view/id/1 pour charger avec succès un message. Vous devriez voir un écran tel que celui ci-dessous :
Et comme vous pouvez le voir, après avoir créé notre structure initiale, il est très simple d'ajouter des fonctionnalités à la plate-forme, et la plupart de notre code initial est réutilisé dans le processus.
Au cas où vous voudriez tester rapidement le module, voici le résultat total de notre travail.
Où aller en partant d'ici
Si vous m'avez suivi jusqu'ici, félicitations ! Je suis certain que vous êtes sur le point de devenir un développeur Magento 2. Nous avons développé un module personnalisé Magento 2 assez avancé, et même s'il est simple dans ses fonctionnalités, beaucoup de terrain a été couvert.
Certaines choses ont été omises de cet article, par souci de simplicité. Pour n'en nommer que quelques-uns :
- L'administrateur modifie les formulaires et les grilles pour gérer le contenu de notre blog
- Catégories de blogs, tags et commentaires
- Des référentiels et quelques contrats de service que nous aurions pu établir
- Modules d'empaquetage en tant qu'extensions Magento 2
Dans tous les cas, voici quelques liens utiles où vous pourrez approfondir encore plus vos connaissances :
- Alan Storm Blog sur Magento 2 - Alan Storm a probablement le contenu le plus didactique en matière d'apprentissage de Magento.
- Blog d'Alan Kent
- Documentation Magento : les documents de développement Magento 2
Je vous ai fourni une introduction complète à tous les aspects pertinents de la création d'un module dans Magento 2, et quelques ressources supplémentaires si vous en avez besoin. C'est maintenant à vous de vous lancer dans le codage ou de vous diriger vers les commentaires si vous souhaitez intervenir.