Magento 2 Tutorial: So erstellen Sie ein vollständiges Modul

Veröffentlicht: 2022-03-11

Magento ist derzeit die größte Open-Source-E-Commerce-Plattform der Welt. Aufgrund seiner funktionsreichen und erweiterbaren Codebasis haben Händler mit großen und kleinen Betrieben auf der ganzen Welt es für eine Vielzahl von Projekten verwendet.

Magento 1 gibt es seit acht Jahren und sein Nachfolger Magento 2 wurde Ende 2015 veröffentlicht und verbesserte Schwachstellen der früheren Version wie:

  • Verbesserte Leistung
  • Offizielle automatisierte Testsuite
  • Bessere Back-End-Benutzeroberfläche
  • Neue, modernere Frontend-Codebasis
  • Eine modularere Art, Module zu entwickeln, mit Dateien, die im Magento-Code enthalten sind, anstatt überall verstreut zu sein
  • Reduzierte Anzahl von Konflikten zwischen Modulen, die versuchen, dieselbe Funktionalität anzupassen

Ein stilisiertes Magento 2-Logo

Nach etwas mehr als einem Jahr ist die Verbesserung sichtbar, auch wenn noch nicht alle genannten Probleme vollständig gelöst sind. Jetzt kann man mit absoluter Sicherheit sagen, dass Magento 2 eine viel robustere Software ist als sein Vorgänger. Einige der Verbesserungen in Magento 2 sind:

  • Einheiten- und Integrationstests, einschließlich einer offiziellen und dokumentierten Möglichkeit, sie für benutzerdefinierte Module zu erstellen
  • Module, die wirklich modularisiert sind und alle ihre Dateien in einem einzigen Verzeichnis abgelegt haben
  • Ein reichhaltigeres Templating-System, das es dem Theme-Entwickler ermöglicht, eine n-Level-Template-Hierarchie zu erstellen
  • Eine Reihe von nützlichen Entwurfsmustern, die im gesamten Code verwendet werden, um die Codequalität zu verbessern und die Wahrscheinlichkeit von Fehlern zu verringern, die von Modulen verursacht werden. Dazu gehören automatische Abhängigkeitsinjektion, Serviceverträge, Repositories und Fabriken, um nur einige zu nennen.
  • Native Integration in Varnish als Ganzseiten-Caching-System sowie Redis für Sitzungs- und Cache-Handling
  • PHP 7-Unterstützung

Die Lernkurve für Magento 2 ist mit all diesen Änderungen noch steiler geworden. In diesem Leitfaden möchte ich Ihnen zeigen, wie Sie Ihr erstes Magento 2-Modul entwickeln, und Sie in die richtige Richtung weisen, um Ihr Studium fortzusetzen. Lasst uns anfangen!

Voraussetzungen für das Magento 2-Tutorial

Es ist wichtig, dass Sie die folgenden Technologien/Konzepte gut verstehen, um dem Rest dieses Artikels folgen zu können:

  • Objektorientierte Programmierung (OOP)
  • PHP
  • Namensräume
  • MySQL
  • Grundlegende Bash-Nutzung

Von all dem ist OOP wahrscheinlich das wichtigste. Magento wurde ursprünglich von einem Team erfahrener Java-Entwickler erstellt, und ihr Vermächtnis ist sicherlich in der gesamten Codebasis zu sehen. Falls Sie sich Ihrer OOP-Fähigkeiten nicht sehr sicher sind, ist es möglicherweise eine gute Idee, diese zu überprüfen, bevor Sie mit der Arbeit mit der Plattform beginnen.

Überblick über die Architektur von Magento 2

Die Architektur von Magento wurde mit der Absicht entworfen, den Quellcode so modularisiert und erweiterbar wie möglich zu machen. Das Endziel dieses Ansatzes besteht darin, eine einfache Anpassung und Anpassung an die Anforderungen jedes Projekts zu ermöglichen.

Anpassen bedeutet normalerweise, das Verhalten des Codes der Plattform zu ändern. In den meisten Systemen bedeutet dies, den „Kern“-Code zu ändern. Wenn Sie in Magento Best Practices befolgen, können Sie dies meistens vermeiden, sodass ein Geschäft zuverlässig mit den neuesten Sicherheitspatches und Feature-Releases auf dem Laufenden bleiben kann.

Magento 2 ist ein Model View Model (MVVM)-System. Eine MVVM-Architektur ist zwar eng mit ihrem Geschwistermodell Model View Controller (MVC) verwandt, bietet jedoch eine robustere Trennung zwischen der Modell- und der Ansichtsebene. Nachfolgend finden Sie eine Erläuterung der einzelnen Schichten eines MVVM-Systems:

  • Das Modell enthält die Geschäftslogik der Anwendung und ist für den Datenbankzugriff von einer zugeordneten Klasse – dem ResourceModel – abhängig. Modelle verlassen sich auf Serviceverträge , um ihre Funktionalität für die anderen Schichten der Anwendung verfügbar zu machen.
  • Die Ansicht ist die Struktur und das Layout dessen, was ein Benutzer auf einem Bildschirm sieht – das eigentliche HTML. Dies wird in den mit Modulen verteilten PHTML-Dateien erreicht. PHTML-Dateien sind jedem ViewModel in den Layout-XML-Dateien zugeordnet, die im MVVM-Dialekt als Binder bezeichnet würden. Die Layoutdateien weisen möglicherweise auch JavaScript-Dateien zu, die auf der letzten Seite verwendet werden sollen.
  • Das ViewModel interagiert mit der Modellebene und stellt der Ansichtsebene nur die erforderlichen Informationen zur Verfügung. In Magento 2 wird dies von den Block -Klassen des Moduls übernommen. Beachten Sie, dass dies normalerweise Teil der Controller-Rolle eines MVC-Systems war. Auf MVVM ist der Controller nur für die Handhabung des Benutzerflusses verantwortlich, was bedeutet, dass er Anforderungen empfängt und das System entweder anweist, eine Ansicht zu rendern oder den Benutzer auf eine andere Route umzuleiten.

Ein Magento 2-Modul besteht aus einigen, wenn nicht allen Elementen der oben beschriebenen Architektur. Die Gesamtarchitektur wird im Folgenden beschrieben (Quelle):

Diagramm der vollständigen Magento 2-Architektur

Ein Magento 2-Modul kann wiederum externe Abhängigkeiten definieren, indem es Composer, den Abhängigkeitsmanager von PHP, verwendet. Im obigen Diagramm sehen Sie, dass die Kernmodule von Magento 2 vom Zend Framework, Symfony sowie anderen Bibliotheken von Drittanbietern abhängen.

Unten ist die Struktur von Magento/Cms, einem Kernmodul von Magento 2, das für die Handhabung der Erstellung von Seiten und statischen Blöcken verantwortlich ist.

Verzeichnislayout des Magento/Cms-Moduls

Jeder Ordner enthält einen Teil der Architektur, wie folgt:

  • Api: Serviceverträge, Definition von Serviceschnittstellen und Datenschnittstellen
  • Block: Die ViewModels unserer MVVM-Architektur
  • Controller: Controller, die für die Verarbeitung des Benutzerflusses während der Interaktion mit dem System verantwortlich sind
  • etc: Konfigurations-XML-Dateien – Das Modul definiert sich selbst und seine Teile (Routen, Modelle, Blöcke, Beobachter und Cron-Jobs) in diesem Ordner. Die etc -Dateien können auch von Nicht-Kernmodulen verwendet werden, um die Funktionalität von Kernmodulen zu überschreiben.
  • Helfer: Helferklassen, die Code enthalten, der in mehr als einer Anwendungsschicht verwendet wird. Beispielsweise sind im Cms-Modul Hilfsklassen dafür verantwortlich, HTML für die Präsentation im Browser vorzubereiten.
  • i18n: Enthält Internationalisierungs-CSV-Dateien, die für die Übersetzung verwendet werden
  • Modell: Für Modelle und Ressourcenmodelle
  • Beobachter: Hält Beobachter oder Modelle, die Systemereignisse „beobachten“. Wenn ein solches Ereignis ausgelöst wird, instanziiert der Beobachter normalerweise ein Modell, um die erforderliche Geschäftslogik für ein solches Ereignis zu handhaben.
  • Setup: Migrationsklassen, verantwortlich für die Schema- und Datenerstellung
  • Test: Unit-Tests
  • Ui: UI-Elemente wie Raster und Formulare, die in der Admin-Anwendung verwendet werden
  • Ansicht: Layoutdateien (XML) und Vorlagendateien (PHTML) für die Frontend- und Admin-Anwendung

Es ist auch interessant festzustellen, dass in der Praxis das gesamte Innenleben von Magento 2 in einem Modul lebt. Im obigen Bild sehen Sie zum Beispiel Magento_Checkout , verantwortlich für den Checkout-Prozess, und Magento_Catalog , verantwortlich für die Handhabung von Produkten und Kategorien. Im Grunde sagt uns dies, dass das Erlernen der Arbeit mit Modulen der wichtigste Teil ist, um ein Magento 2-Entwickler zu werden.

Okay, nach dieser relativ kurzen Einführung in die Systemarchitektur und den Modulaufbau machen wir etwas Konkreteres, oder? Als nächstes werden wir das traditionelle Weblog-Tutorial durchgehen, um Sie mit Magento 2 vertraut zu machen und Sie auf den richtigen Weg zu bringen, ein Magento 2-Entwickler zu werden. Zuvor müssen wir eine Entwicklungsumgebung einrichten. Lasst uns anfangen!

Einrichten der Entwicklungsumgebung für das Magento 2-Modul

Zum Zeitpunkt der Erstellung dieses Artikels konnten wir die offizielle Magento 2 DevBox verwenden, die ein Magento 2 Docker-Container ist. Docker unter macOS halte ich immer noch für unbrauchbar, zumindest bei einem System, das stark von schnellen Festplatten-I/Os abhängt, wie Magento 2. Also machen wir es auf die traditionelle Weise: Installieren Sie alle Pakete nativ auf unserer eigenen Maschine.

Einrichten des Servers

Alles zu installieren ist sicherlich etwas mühsamer, aber das Endergebnis wird eine blitzschnelle Magento-Entwicklungsumgebung sein. Glauben Sie mir, Sie sparen Stunden an Arbeit, wenn Sie für Ihre Magento 2-Entwicklung nicht auf Docker angewiesen sind.

Dieses Tutorial geht von einer Umgebung auf macOS aus, auf der Brew installiert ist. Wenn das bei Ihnen nicht der Fall ist, bleiben die Grundlagen gleich und ändern sich nur die Art und Weise, wie Sie die Pakete installieren. Beginnen wir mit der Installation aller Pakete:

 brew install mysql nginxb php70 php70-imagick php70-intl php70-mcrypt

Starten Sie dann die Dienste:

 brew services start mysql brew services start php70 sudo brew services start nginx

Ok, jetzt verweisen wir eine Domain auf unsere Loopback-Adresse. Öffnen Sie die Hosts-Datei in einem beliebigen Editor, aber stellen Sie sicher, dass Sie über Superuser-Berechtigungen verfügen. Das mit Vim zu tun wäre:

 sudo vim /etc/hosts

Fügen Sie dann die folgende Zeile hinzu:

 127.0.0.1 magento2.dev

Jetzt erstellen wir einen vhost in Nginx:

 vim /usr/local/etc/nginx/sites-available/magento2dev.conf

Fügen Sie den folgenden Inhalt hinzu:

 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; }

Wenn Sie sich noch nie mit Nginx befasst haben, könnte Ihnen diese Datei Angst machen, also lassen Sie uns die kleinen Details hier erklären, da sie auch etwas Licht auf einige der inneren Abläufe von Magento werfen wird. Die ersten Zeilen teilen Nginx einfach mit, dass wir den Standard-HTTP-Port verwenden und unsere Domain magento2.dev ist:

 listen 80; server_name magento2.dev;

Dann setzen wir einige Umgebungsvariablen. Der erste $MAGE_ROOT – enthält den Pfad zu unserer Codebasis. Beachten Sie, dass Sie den Stammpfad ändern müssen, damit er mit Ihrem Benutzernamen/Ordnerpfad übereinstimmt, wo immer Sie die Quelle platzieren möchten:

 set $MAGE_ROOT /Users/yourusername/www/magento2dev;

Die zweite Variable $MAGE_MODE – legt den Laufzeitmodus für unseren Shop fest. Da wir ein Modul entwickeln, verwenden wir den Entwicklermodus. Dadurch können wir schneller codieren, da wir während der Entwicklung keine statischen Dateien kompilieren oder bereitstellen müssen. Die anderen Modi sind Produktion und Standard. Der eigentliche Nutzen für Letzteres ist noch nicht klar.

 set $MAGE_MODE developer;

Nachdem diese Variablen festgelegt wurden, definieren wir den vhost-Root-Pfad. Beachten Sie, dass wir der Variable $MAGE_ROOT den Ordner /pub anhängen, wodurch nur ein Teil unseres Shops für das Web verfügbar ist.

 root $MAGE_ROOT/pub;

Dann definieren wir unsere Indexdatei – die Datei, die nginx lädt, wenn die angeforderte Datei nicht existiert – als index.php. Dieses Skript, $MAGE_ROOT/pub/index.php , ist der Haupteinstiegspunkt für Kunden, die sowohl den Warenkorb als auch die Verwaltungsanwendungen besuchen. Unabhängig von der angeforderten URL wird index.php geladen und der Router-Dispatching-Prozess gestartet.

 index index.php;

Als nächstes schalten wir einige Nginx-Funktionen aus. Zuerst deaktivieren wir autoindex , wodurch eine Dateiliste angezeigt wird, wenn Sie einen Ordner anfordern, aber keine Datei angeben und kein Index vorhanden ist. Zweitens deaktivieren wir charset , was es Nginx ermöglichen würde, der Antwort automatisch Charset-Header hinzuzufügen.

 autoindex off; charset off;

Als Nächstes definieren wir einige Sicherheitsheader:

 add_header 'X-Content-Type-Options' 'nosniff'; add_header 'X-XSS-Protection' '1; mode=block';

Dieser Speicherort, / , verweist auf unseren Stammordner $MAGE_ROOT/pub und leitet im Grunde jede empfangene Anfrage zusammen mit den Anfrageargumenten an unseren Front-Controller index.php weiter:

 location / { try_files $uri $uri/ /index.php?$args; }

Der nächste Abschnitt mag etwas verwirrend sein, ist aber ganz einfach. Vor ein paar Zeilen haben wir unsere Wurzel als $MAGE_ROOT/pub definiert. Dies ist die empfohlene und sicherere Einrichtung, da der größte Teil des Codes aus dem Internet nicht sichtbar ist. Aber es ist nicht die einzige Möglichkeit, den Webserver einzurichten. Tatsächlich haben die meisten gemeinsam genutzten Webserver eine Standardeinstellung, bei der Ihr Webserver auf Ihren Webordner zeigt. Für diese Benutzer hat das Magento-Team diese Datei für die Fälle vorbereitet, in denen das Stammverzeichnis als $MAGE_ROOT mit dem folgenden Ausschnitt definiert ist:

 location /pub { location ~ ^/pub/media/(downloadable|customer|import|theme_customization/.*\.xml) { deny all; } alias $MAGE_ROOT/pub; add_header X-Frame-Options "SAMEORIGIN"; }

Denken Sie daran, dass es nach Möglichkeit am besten ist, wenn Ihr Webserver auf den Ordner $MAGE_ROOT/pub zeigt. Auf diese Weise wird Ihr Geschäft sicherer.

Als nächstes haben wir den statischen Speicherort $MAGE_ROOT/pub/static . Dieser Ordner ist zunächst leer und wird automatisch mit den statischen Dateien der Module und Themes wie Bilddateien, CSS, JS usw. gefüllt. Hier definieren wir im Wesentlichen einige Cache-Werte für die statischen Dateien und, wenn die angeforderte Datei dies nicht tut vorhanden ist, leite sie nach $MAGE_ROOT/pub/static.php . Dieses Skript analysiert unter anderem die Anfrage und kopiert oder symbolisiert die angegebene Datei aus dem entsprechenden Modul oder Thema, je nach definiertem Laufzeitmodus. Auf diese Weise befinden sich die statischen Dateien Ihres Moduls im Ordner unserer Module, werden aber direkt aus dem öffentlichen vhost-Ordner bereitgestellt:

 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"; }

Als nächstes verweigern wir den Webzugriff auf einige eingeschränkte Ordner und Dateien:

 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; }

Und im letzten Bit laden wir php-fpm hoch und weisen es an, index.php auszuführen, wenn der Benutzer darauf trifft:

 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; }

Speichern Sie die Datei und aktivieren Sie sie dann, indem Sie die folgenden Befehle eingeben:

 ln -s /usr/local/etc/nginx/sites-available/magento2dev.conf \ /usr/local/etc/nginx/sites-enabled/magento2dev.conf sudo brew services restart nginx

So installieren Sie Magento 2

Okay, an diesem Punkt erfüllt Ihr Computer die Magento 2-Anforderungen und es fehlt nur das Biest selbst. Gehen Sie zur Magento-Website und erstellen Sie ein Konto, falls Sie noch keines haben. Gehen Sie danach zur Download-Seite und laden Sie die neueste Version herunter (2.1.5, zum Zeitpunkt des Schreibens):

Magento 2-Downloadseite

Wählen Sie das .tar.bz2-Format und laden Sie es herunter. Fahren Sie dann mit dem Extrahieren fort und legen Sie die richtigen Ordner- und Dateiberechtigungen fest, damit Magento 2 arbeiten kann:

 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

Um nun die Datenbanktabellen zu installieren und die erforderlichen Konfigurationsdateien zu erstellen, führen wir diesen Befehl vom Terminal aus aus:

 ./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

Denken Sie daran, den Datenbanknamen ( db-name ), den Benutzer ( db-user ) und das Passwort ( db-password ) so zu ändern, dass sie mit denen übereinstimmen, die Sie während der Installation von MySQL verwendet haben, und das war's! Dieser Befehl installiert alle Module von Magento 2 und erstellt die erforderlichen Tabellen und Konfigurationsdateien. Nachdem es fertig ist, öffnen Sie Ihren Browser und gehen Sie zu http://magento2.dev/. Sie sollten eine Neuinstallation von Magento 2 mit dem standardmäßigen Luma-Design sehen:

Startseite im standardmäßigen Luma-Design

Wenn Sie zu http://magento2.dev/admin gehen, sollten Sie die Anmeldeseite der Admin-Anwendung sehen:

Anmeldeseite der Admin-Anwendung

Verwenden Sie dann die folgenden Anmeldeinformationen, um sich anzumelden:

Benutzer: admin Passwort: admin123

Wir sind endlich bereit, unseren Code zu schreiben!

Erstellen unseres ersten Magento 2-Moduls

Um unser Modul abzuschließen, müssen wir die folgenden Dateien erstellen, und ich werde Sie durch den gesamten Prozess führen. Wir brauchen:

  • Ein paar vorgefertigte Registrierungsdateien, um Magento auf unser Blog-Modul aufmerksam zu machen
  • Eine Schnittstellendatei, um unseren Datenvertrag für die Post zu definieren
  • Ein Post-Modell, um einen Post in unserem gesamten Code darzustellen und die Post-Datenschnittstelle zu implementieren
  • Ein Post-Ressourcenmodell, um das Post-Modell mit der Datenbank zu verknüpfen
  • Eine Beitragssammlung, um mit Hilfe des Ressourcenmodells mehrere Beiträge auf einmal aus der Datenbank abzurufen
  • Zwei Migrationsklassen zum Einrichten unseres Tabellenschemas und Inhalts
  • Zwei Aktionen: eine, um alle Beiträge aufzulisten, und eine andere, um jeden Beitrag einzeln anzuzeigen
  • Jeweils zwei Blöcke, Ansichten und Layoutdateien: Je eine für die Listenaktion und je eine für die Ansicht

Lassen Sie uns zunächst einen kurzen Blick auf die Ordnerstruktur des Kernquellcodes werfen, damit wir definieren können, wo unser Code platziert werden soll. Die Art und Weise, wie wir installiert haben, enthält den gesamten Kerncode von Magento 2 zusammen mit all seinen Abhängigkeiten im vendor -Ordner des Komponisten.

Verzeichnislayout des Kerncodes von Magento 2

Registrierung unseres Moduls

Wir werden unseren Code in einem separaten Ordner app/code . Der Name jedes Moduls hat die Form Namespace_ModuleName , und seine Position im Dateisystem muss diesen Namen widerspiegeln, app/code/Namespace/ModuleName für dieses Beispiel. Nach diesem Muster werden wir unser Modul Toptal_Blog und unsere Dateien unter app/code/Toptal/Blog . Fahren Sie fort und erstellen Sie diese Ordnerstruktur.

Verzeichnislayout unseres Moduls Toptal_Blog

Jetzt müssen wir ein paar Boilerplate-Dateien erstellen, damit unser Modul bei Magento registriert wird. Erstellen Sie zuerst app/code/Toptal/Blog/composer.json :

 {}

Diese Datei wird von Composer bei jeder Ausführung geladen. Obwohl wir Composer eigentlich nicht mit unserem Modul verwenden, müssen wir es erstellen, um Composer zufrieden zu stellen.

Jetzt werden wir unser Modul bei Magento registrieren. Machen Sie weiter und erstellen Sie app/code/Toptal/Blog/registration.php :

 <?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Toptal_Blog', __DIR__ );

Hier rufen wir die Methode register der Klasse ComponentRegistrar auf und senden zwei Parameter: die Zeichenfolge 'module' , die den Typ der Komponente darstellt, die wir registrieren, und den Namen unseres Moduls 'Toptal_Blog' . Mit diesen Informationen kennt der Autoloader von Magento unseren Namensraum und weiß, wo er nach unseren Klassen und XML-Dateien suchen muss.

Interessant ist hier, dass der Typ der Komponente ( MODULE ) als Parameter an die Funktion \Magento\Framework\Component\ComponentRegistrar::register gesendet wird. Wir können nicht nur Module registrieren, sondern auch andere Arten von Komponenten. Mit derselben Methode werden beispielsweise auch Themen, externe Bibliotheken und Sprachpakete registriert.

Lassen Sie uns fortfahren und unsere letzte Registrierungsdatei 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>

Diese Datei enthält einige sehr wichtige Informationen über unser Modul. Sie sind:

  • Der Modulname ist wieder vorhanden und macht unseren Modulnamen für die Magento-Konfiguration verfügbar.
  • Die Magento-Setup-Version, die von Magento verwendet wird, um zu entscheiden, wann Datenbankmigrationsskripte ausgeführt werden.
  • Die Abhängigkeiten unseres Moduls – Da wir ein einfaches Modul schreiben, sind wir nur von zwei Kernmodulen von Magento abhängig: Magento_Directory und Magento_Config .

Jetzt haben wir ein Modul, das von Magento 2 erkannt werden sollte. Lassen Sie es uns mit der Magento 2-CLI überprüfen.

Zuerst müssen wir den Cache von Magento deaktivieren. Die Cache-Mechanismen von Magento verdienen einen eigenen Artikel. Da wir ein Modul entwickeln und möchten, dass unsere Änderungen sofort von Magento erkannt werden, ohne dass der Cache jederzeit gelöscht werden muss, werden wir es vorerst einfach deaktivieren. Führen Sie in der Befehlszeile Folgendes aus:

 ./bin/magento cache:disable

Dann sehen wir uns an, ob Magento unsere Änderungen bereits kennt, indem wir uns den Status der Module ansehen. Führen Sie einfach den folgenden Befehl aus:

 ./bin/magento module:status

Das Ergebnis des letzten sollte ähnlich sein wie:

Ausgabe des Statusbefehls, die zeigt, dass das Toptal_Blog-Modul deaktiviert ist

Unser Modul ist da, aber wie die Ausgabe zeigt, ist es immer noch deaktiviert. Um es zu aktivieren, führen Sie Folgendes aus:

 ./bin/magento module:enable Toptal_Blog

Das hätte es tun sollen. Zur Sicherheit können Sie module:status noch einmal aufrufen und in der aktivierten Liste nach dem Namen unseres Moduls suchen:

Ausgabe des Statusbefehls, die zeigt, dass das Toptal_Blog-Modul aktiviert ist

Handhabung der Datenspeicherung

Nachdem wir unser Modul aktiviert haben, müssen wir die Datenbanktabelle erstellen, die unsere Blogbeiträge enthält. Dies ist das Schema für die Tabelle, die wir erstellen möchten:

Bereich Art Null Taste Standard
post_id int(10) ohne Vorzeichen NEIN PRI NULL
Titel Text NEIN NULL
Inhalt Text NEIN NULL
hergestellt in Zeitstempel NEIN AKTUELLER ZEITSTEMPEL

Wir erreichen dies, indem wir die InstallSchema -Klasse erstellen, die für die Verwaltung der Installation unserer Schemamigration verantwortlich ist. Die Datei befindet sich unter app/code/Toptal/Blog/Setup/InstallSchema.php und hat folgenden Inhalt:

 <?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(); } }

Wenn Sie die install analysieren, werden Sie feststellen, dass sie einfach unsere Tabelle erstellt und ihre Spalten eine nach der anderen hinzufügt.

Um zu bestimmen, wann eine Schemamigration durchgeführt werden soll, führt Magento eine Tabelle mit allen aktuellen Setup-Versionen für jedes Modul, und wenn sich eine Modulversion ändert, werden ihre Migrationsklassen initialisiert. Diese Tabelle ist setup_module , und wenn Sie sich den Inhalt dieser Tabelle ansehen, werden Sie feststellen, dass es bisher keinen Verweis auf unser Modul gibt. Also, lasst uns das ändern. Lösen Sie von einem Terminal aus den folgenden Befehl aus:

 ./bin/magento setup:upgrade

Das zeigt Ihnen eine Liste aller Module und ihrer Migrationsskripte, die ausgeführt wurden, einschließlich unseres:

Ausgabe des Upgrade-Befehls, die zeigt, dass unsere Migration durchgeführt wird

Jetzt können Sie von Ihrem bevorzugten MySQL-Client aus überprüfen, ob die Tabelle wirklich erstellt wurde:

Demonstration unserer Tabelle im MySQL-Client

Und in der Tabelle setup_module gibt es jetzt einen Verweis auf unser Modul, sein Schema und seine Datenversion:

Inhalt der Tabelle setup_module

Ok, und was ist mit Schema-Upgrades? Lassen Sie uns dieser Tabelle durch ein Upgrade einige Posts hinzufügen, um Ihnen zu zeigen, wie das geht. Erhöhen Sie zunächst die setup_version in unserer Datei etc/module.xml :

Hervorhebung des geänderten Werts in unserer Datei module.xml

Jetzt erstellen wir unsere Datei app/code/Toptal/Blog/Setup/UpgradeData.php , die für die Migration der Daten (nicht Schema) verantwortlich ist:

 <?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(); } }

Sie können sehen, dass sie unserer Install-Klasse sehr ähnlich ist. Der einzige Unterschied besteht darin, dass anstelle von InstallSchemaInterface ein UpgradeDataInterface implementiert wird und die Hauptmethode upgrade heißt. Mit dieser Methode suchen Sie nach der installierten Version des aktuellen Moduls und starten, wenn es kleiner als Ihre ist, die Änderungen, die Sie erledigen müssen. Ob die aktuelle Version kleiner als 0.1.1 ist, prüfen wir in unserem Beispiel in der folgenden Zeile mit der Funktion version_compare :

 if ($context->getVersion() && version_compare($context->getVersion(), '0.1.1') < 0 ) {

Der $context->getVersion() gibt 0.1.0 zurück, wenn der CLI-Befehl setup:upgrade zum ersten Mal aufgerufen wird. Dann werden die Beispieldaten in die Datenbank geladen und unsere Version auf 0.1.1 erhöht. Um dies zum Laufen zu bringen, starten Sie ein setup:upgrade :

 ./bin/magento setup:upgrade

Und dann überprüfen Sie die Ergebnisse in der Posts-Tabelle:

Inhalt unserer Tabelle

Und in der setup_module Tabelle:

Aktualisierter Inhalt der setup_module-Tabelle

Beachten Sie, dass es auch möglich gewesen wäre, das Schema zu ändern, obwohl wir mit dem Migrationsprozess Daten zu unserer Tabelle hinzugefügt haben. Der Prozess ist derselbe; Sie würden nur das UpgradeSchemaInterface anstelle des UpgradeDataInterface .

Definition des Modells für Beiträge

Wenn Sie sich an unsere Architekturübersicht erinnern, wäre unser nächster Baustein der Blog-Beitrag ResourceModel. Das Ressourcenmodell ist sehr einfach und gibt einfach die Tabelle an, mit der sich das Modell „verbindet“, zusammen mit dem Primärschlüssel. Wir erstellen unser ResourceModel unter app/code/Toptal/Blog/Model/ResourceModel/Post.php mit folgendem Inhalt:

 <?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'); } }

Alle ResourceModel-Vorgänge werden von der übergeordneten Klasse AbstractDb verarbeitet, es sei denn, Sie benötigen etwas anderes als die üblichen CRUD-Vorgänge.

Außerdem benötigen wir ein weiteres ResourceModel, eine Collection. Die Sammlung wird dafür verantwortlich sein, die Datenbank nach mehreren Beiträgen mit unserem Ressourcenmodell abzufragen und eine Reihe von Modellen zurückzugeben, die instanziiert und mit Informationen gefüllt sind. Wir erstellen die Datei app/code/Toptal/Blog/Model/ResourceModel/Post/Collection.php mit folgendem Inhalt:

 <?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'); } }

Beachten Sie, dass wir im Konstruktor einfach das Modell erwähnen, das die Post-Entität in unserem gesamten Code darstellt, und das Ressourcenmodell, das die Informationen aus der Datenbank abruft.

Das fehlende Teil für diese Schicht ist das Post Model selbst. Das Modell sollte alle Attribute enthalten, die wir in unserem Schema definiert haben, zusammen mit jeder Geschäftslogik, die Sie möglicherweise benötigen. Nach dem Muster von Magento 2 müssen wir eine Datenschnittstelle erstellen, von der unser Modell ausgeht. Wir platzieren die Schnittstelle unter app/code/Toptal/Blog/Api/Data/PostInterface.php , und sie sollte die Feldnamen der Tabelle zusammen mit den Methoden für den Zugriff darauf enthalten:

 <?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); }

Nun zur Implementierung des Modells unter app/code/Toptal/Blog/Model/Post.php . Wir erstellen die an der Schnittstelle definierten Methoden. Wir werden auch ein Cache-Tag über die Konstante CACHE_TAG angeben und beim Konstruktor das ResourceModel angeben, das für den Datenbankzugriff für unser Modell verantwortlich sein wird.

 <?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 case Context and PageFactory .

  • The execute method is responsible for the action execution itself. In our case, we are simply telling Magento to render its layout by returning a Magento\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(); } }

Diese Klasse ist ziemlich einfach und ihr Ziel besteht lediglich darin, die anzuzeigenden Posts zu laden und der Vorlage eine getPostUrl Methode bereitzustellen. Es gibt jedoch einige Dinge zu beachten.

Wenn Sie sich erinnern, haben wir keine Toptal\Blog\Model\ResourceModel\Post\CollectionFactory -Klasse definiert. Wir haben nur Toptal\Blog\Model\ResourceModel\Post\Collection definiert. Wie funktioniert das überhaupt? Für jede Klasse, die Sie in Ihrem Modul definieren, erstellt Magento 2 automatisch eine Factory für Sie. Factories haben zwei Methoden: create , die bei jedem Aufruf eine neue Instanz zurückgibt, und get , die bei jedem Aufruf immer dieselbe Instanz zurückgibt – wird verwendet, um das Singleton-Muster zu implementieren.

Der dritte Parameter unseres Blocks, $data , ist ein optionales Array. Da es optional ist und keinen Typhinweis hat, wird es nicht vom automatischen Injektionssystem injiziert. Es ist wichtig zu beachten, dass optionale Konstruktorparameter immer an letzter Stelle in den Parametern stehen müssen. Beispielsweise hat der Konstruktor von Magento\Framework\View\Element\Template , unserer übergeordneten Klasse, diese Parameter:

 public function __construct( Template\Context $context, array $data = [] ) { ...

Da wir unsere CollectionFactory nach dem Erweitern der Template-Klasse zu den Konstruktorparametern hinzufügen wollten, mussten wir dies vor dem optionalen Parameter tun, sonst würde die Injektion nicht funktionieren:

 public function __construct( Context $context, PostCollectionFactory $postCollectionFactory, array $data = [] ) { ...

Bei der getPosts Methode, auf die später von unserer Vorlage zugegriffen wird, rufen wir einfach die create -Methode aus der PostCollectionFactory , die uns eine neue PostCollection und es uns ermöglicht, unsere Posts aus der Datenbank abzurufen und an unsere Antwort zu senden.

Und um das Layout dieser Route zu vervollständigen, hier ist unsere PHTML-Vorlage 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; ?>

Beachten Sie, dass wir hier sehen können, wie die View-Ebene auf unsere ModelView ( $block->getPosts() ) zugreift, die wiederum ein ResourceModel (die Sammlung) verwendet, um unsere Modelle ( Toptal\Blog\Model\Post ) aus der Datenbank abzurufen. In jeder Vorlage wird immer dann, wenn Sie auf die Methoden ihres Blocks zugreifen möchten, eine $block Variable definiert und auf Ihre Aufrufe warten.

Jetzt sollten Sie in der Lage sein, die Beitragsliste zu sehen, indem Sie einfach erneut auf unsere Route klicken.

Unsere Indexseite mit der Liste der Beiträge

Anzeigen einzelner Beiträge

Wenn Sie jetzt auf einen Beitragstitel klicken, erhalten Sie einen 404, also lassen Sie uns das beheben. Mit all unserer Struktur wird dies ganz einfach. Wir müssen nur Folgendes erstellen:

  • Eine neue Aktion, die für die Bearbeitung von Anfragen an die blog/post/view -Route verantwortlich ist
  • Ein Block zum Rendern des Beitrags
  • Eine PHTML-Vorlage, die für die Ansicht selbst verantwortlich ist
  • Eine Layoutdatei für die Blog-/Post-/View-Route, die diese letzten Teile zusammenfügt.

Unsere neue Aktion ist ganz einfach. Es erhält einfach die Parameter- id von der Anfrage und registriert sie in der Magento Core Registry, einem zentralen Repository für Informationen, die während eines einzigen Anfragezyklus verfügbar sind. Dadurch stellen wir die ID später dem Baustein zur Verfügung. Die Datei sollte sich unter app/code/Toptal/Blog/Controller/Post/View.php befinden und dies sind ihre Inhalte:

 <?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; } }

Beachten Sie, dass wir den Parameter $coreRegistry zu unserem __construct und als Attribut zur späteren Verwendung gespeichert haben. Bei der execute Methode rufen wir den id Parameter aus der Anfrage ab und registrieren ihn. Wir verwenden auch eine Klassenkonstante self::REGISTRY_KEY_POST_ID als Schlüssel für das Register, und wir werden dieselbe Konstante in unserem Block verwenden, um auf die ID in der Registrierung zu verweisen.

Lassen Sie uns den Block unter app/code/Toptal/Blog/Block/View.php mit folgendem Inhalt erstellen:

 <?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 ); } }

Am Ansichtsblock definieren wir eine geschützte Methode _getPostId , die einfach die Post-ID aus der Kernregistrierung abruft. Die öffentliche getPost Methode wiederum lädt den Post lazy und löst eine Ausnahme aus, wenn der Post nicht existiert. Wenn Sie hier eine Ausnahme auslösen, zeigt Magento seinen Standardfehlerbildschirm an, was in einem solchen Fall möglicherweise nicht die beste Lösung ist, aber wir werden es der Einfachheit halber so beibehalten.

Weiter zu unserer PHTML-Vorlage. Fügen Sie app/code/Toptal/Blog/view/frontend/templates/post/view.phtml mit folgendem Inhalt hinzu:

 <?php /** @var Toptal\Blog\Block\View $block */ ?> <h1><?php echo $block->getPost()->getTitle(); ?></h1> <p><?php echo $block->getPost()->getContent(); ?></p>

Schön und einfach, indem Sie einfach auf die getPost Methode des Ansichtsblocks zugreifen, die wir zuvor erstellt haben.

Und um alles zusammenzufassen, erstellen wir eine Layoutdatei für unsere neue Route unter app/code/Toptal/Blog/view/frontend/layout/blog_post_view.xml mit folgendem Inhalt:

 <?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>

Dies macht dasselbe, was wir zuvor getan haben. Es fügt einfach Toptal\Blog\Block\View zum content hinzu, mit Toptal_Blog::post/view.phtml als zugehöriger Vorlage.

Um es in Aktion zu sehen, leiten Sie einfach Ihren Browser zu http://magento2.dev/blog/post/view/id/1, um erfolgreich einen Beitrag zu laden. Sie sollten einen Bildschirm wie den folgenden sehen:

Seite zum Anzeigen einzelner Beiträge

Und wie Sie sehen können, ist es nach dem Erstellen unserer anfänglichen Struktur wirklich einfach, Funktionen zur Plattform hinzuzufügen, und der größte Teil unseres anfänglichen Codes wird dabei wiederverwendet.

Falls Sie das Modul schnell testen möchten, hier ist das Gesamtergebnis unserer Arbeit.

Wohin von hier aus

Wenn Sie mir bis hierher gefolgt sind, herzlichen Glückwunsch! Ich bin mir sicher, dass Sie kurz davor stehen, Magento 2-Entwickler zu werden. Wir haben ein ziemlich fortschrittliches benutzerdefiniertes Magento 2-Modul entwickelt, und obwohl es in seinen Funktionen einfach ist, wurde viel Boden abgedeckt.

Einige Dinge wurden in diesem Artikel der Einfachheit halber weggelassen. Um ein paar zu nennen:

  • Admin bearbeitet Formulare und Raster, um unsere Blog-Inhalte zu verwalten
  • Blogs Kategorien, Tags und Kommentare
  • Repositories und ein paar Serviceverträge, die wir hätten abschließen können
  • Paketieren von Modulen als Magento 2-Erweiterungen

Hier sind auf jeden Fall einige nützliche Links, wo Sie Ihr Wissen noch weiter vertiefen können:

  • Alan Storm Blog über Magento 2 – Alan Storm hat wahrscheinlich die didaktischsten Inhalte, wenn es um das Erlernen von Magento geht.
  • Alan Kents Blog
  • Magento-Dokumentation: Die Magento 2 Dev Docs

Ich habe Ihnen eine umfassende Einführung in alle relevanten Aspekte zum Erstellen eines Moduls in Magento 2 sowie einige zusätzliche Ressourcen bereitgestellt, falls Sie diese benötigen. Jetzt liegt es an Ihnen, zu programmieren oder zu den Kommentaren zu gehen, wenn Sie sich einbringen möchten.