Tutorial Magento 2: come costruire un modulo completo

Pubblicato: 2022-03-11

Magento è attualmente la più grande piattaforma di eCommerce open source al mondo. Grazie alla sua base di codice ricca di funzionalità ed estensibile, i commercianti con operazioni grandi e piccole in tutto il mondo lo utilizzano per un'ampia varietà di progetti.

Magento 1 è in circolazione da otto anni e il suo successore, Magento 2, è stato rilasciato alla fine del 2015, migliorando i punti deboli della versione precedente come:

  • Prestazione migliorata
  • Suite di test automatizzata ufficiale
  • Migliore interfaccia utente di back-end
  • Base di codice front-end nuova e più moderna
  • Un modo più modulare per sviluppare moduli, con file contenuti all'interno del codice Magento invece di essere sparsi ovunque
  • Numero ridotto di conflitti tra i moduli che tentano di personalizzare la stessa funzionalità

Un logo Magento 2 stilizzato

Manca poco più di un anno e il miglioramento è visibile, anche se non tutti i problemi citati sono stati completamente risolti. Ora è del tutto sicuro dire che Magento 2 è un software molto più robusto rispetto al suo predecessore. Alcuni dei miglioramenti presenti in Magento 2 sono:

  • Test unitari e di integrazione, incluso un modo ufficiale e documentato per crearli per moduli personalizzati
  • Moduli davvero modulari, con tutti i loro file posizionati in un'unica directory
  • Un sistema di modelli più ricco, che consente allo sviluppatore del tema di creare una gerarchia di modelli a n livelli
  • Una serie di modelli di progettazione utili adottati in tutto il codice, che migliorano la qualità del codice e riducono la probabilità di errori creati dai moduli, tra cui l'inserimento automatico delle dipendenze, i contratti di servizio, i repository e le fabbriche, solo per citarne alcuni.
  • Integrazione nativa con Varnish come sistema di memorizzazione nella cache a pagina intera, nonché Redis per la gestione di sessioni e cache
  • Supporto PHP 7

La curva di apprendimento per Magento 2, con tutti questi cambiamenti, è diventata ancora più ripida. In questa guida intendo mostrarti come sviluppare il tuo primo modulo Magento 2 e indicarti la giusta direzione per continuare i tuoi studi. Andiamo a farlo!

Magento 2 Tutorial Prerequisiti

È importante che tu abbia una buona comprensione delle seguenti tecnologie/concetti per seguire il resto di questo articolo:

  • Programmazione orientata agli oggetti (OOP)
  • PHP
  • Spazi dei nomi
  • MySQL
  • Utilizzo di base di bash

Di tutto quanto sopra, OOP è probabilmente il più importante. Magento è stato inizialmente creato da un team di sviluppatori Java esperti e la loro eredità può sicuramente essere vista in tutta la base di codice. Nel caso in cui non sei molto sicuro delle tue abilità OOP, potrebbe essere una buona idea rivederle prima di iniziare il tuo lavoro con la piattaforma.

Panoramica dell'architettura di Magento 2

L'architettura di Magento è stata progettata con l'intento di rendere il codice sorgente il più modulare ed estensibile possibile. L'obiettivo finale di tale approccio è di consentirne l'adattamento e la personalizzazione facilmente in base alle esigenze di ciascun progetto.

La personalizzazione di solito significa modificare il comportamento del codice della piattaforma. Nella maggior parte dei sistemi, ciò significa modificare il codice "core". In Magento, se stai seguendo le migliori pratiche, questo è qualcosa che puoi evitare la maggior parte del tempo, consentendo a un negozio di tenersi aggiornato con le ultime patch di sicurezza e versioni di funzionalità in modo affidabile.

Magento 2 è un sistema Model View ViewModel (MVVM). Pur essendo strettamente correlata al suo fratello Model View Controller (MVC), un'architettura MVVM fornisce una separazione più solida tra il modello e i livelli di visualizzazione. Di seguito è riportata una spiegazione di ciascuno dei livelli di un sistema MVVM:

  • Il modello contiene la logica aziendale dell'applicazione e dipende da una classe associata, il ResourceModel, per l'accesso al database. I modelli si basano su contratti di servizio per esporre la loro funzionalità agli altri livelli dell'applicazione.
  • La vista è la struttura e il layout di ciò che un utente vede su uno schermo: il vero HTML. Ciò si ottiene nei file PHTML distribuiti con i moduli. I file PHTML sono associati a ciascun ViewModel nei file XML di layout , che verrebbero chiamati raccoglitori nel dialetto MVVM. I file di layout potrebbero anche assegnare file JavaScript da utilizzare nella pagina finale.
  • Il ViewModel interagisce con il livello Model, esponendo solo le informazioni necessarie al livello View. In Magento 2, questo è gestito dalle classi Block del modulo. Si noti che questo di solito faceva parte del ruolo Controller di un sistema MVC. Su MVVM, il controller è responsabile solo della gestione del flusso utente, il che significa che riceve richieste e dice al sistema di eseguire il rendering di una vista o di reindirizzare l'utente a un altro percorso.

Un modulo Magento 2 è costituito da alcuni, se non tutti, gli elementi dell'architettura sopra descritta. L'architettura complessiva è descritta di seguito (fonte):

Diagramma dell'intera architettura di Magento 2

Un modulo Magento 2 può a sua volta definire dipendenze esterne utilizzando Composer, il gestore delle dipendenze di PHP. Nel diagramma sopra, puoi vedere che i moduli principali di Magento 2 dipendono da Zend Framework, Symfony e altre librerie di terze parti.

Di seguito è riportata la struttura di Magento/Cms, un modulo principale di Magento 2 responsabile della gestione della creazione di pagine e blocchi statici.

Layout della directory del modulo Magento/Cms

Ogni cartella contiene una parte dell'architettura, come segue:

  • Api: Contratti di servizio, definizione di interfacce di servizio e interfacce dati
  • Block: i ViewModel della nostra architettura MVVM
  • Controller: Controller, responsabili della gestione del flusso dell'utente durante l'interazione con il sistema
  • ecc: File XML di configurazione: il modulo definisce se stesso e le sue parti (percorsi, modelli, blocchi, osservatori e lavori cron) all'interno di questa cartella. I file etc possono essere utilizzati anche da moduli non core per sovrascrivere la funzionalità dei moduli core.
  • Helper: classi helper che contengono codice utilizzato in più di un livello di applicazione. Ad esempio, nel modulo Cms, le classi helper sono responsabili della preparazione dell'HTML per la presentazione al browser.
  • i18n: contiene file CSV di internazionalizzazione, utilizzati per la traduzione
  • Modello: per modelli e modelli di risorse
  • Osservatore: contiene osservatori o modelli che stanno "osservando" eventi di sistema. Di solito, quando un tale evento viene attivato, l'osservatore crea un'istanza di un modello per gestire la logica aziendale necessaria per tale evento.
  • Configurazione: classi di migrazione, responsabili dello schema e della creazione dei dati
  • Test: test unitari
  • Interfaccia utente: elementi dell'interfaccia utente come griglie e moduli utilizzati nell'applicazione di amministrazione
  • visualizzazione: file di layout (XML) e file di modello (PHTML) per l'applicazione front-end e di amministrazione

È anche interessante notare che, in pratica, tutti i meccanismi interni di Magento 2 vivono all'interno di un modulo. Nell'immagine sopra puoi vedere, ad esempio, Magento_Checkout , responsabile del processo di checkout, e Magento_Catalog , responsabile della gestione di prodotti e categorie. Fondamentalmente, ciò che questo ci dice è che imparare a lavorare con i moduli è la parte più importante per diventare uno sviluppatore Magento 2.

Va bene, dopo questa introduzione relativamente breve all'architettura del sistema e alla struttura dei moduli, facciamo qualcosa di più concreto, vero? Successivamente, esamineremo il tradizionale tutorial Weblog per metterti a tuo agio con Magento 2 e sulla buona strada per diventare uno sviluppatore Magento 2. Prima di ciò, dobbiamo creare un ambiente di sviluppo. Andiamo a farlo!

Configurazione dell'ambiente di sviluppo del modulo Magento 2

Nel momento in cui scriviamo, siamo stati in grado di utilizzare il Magento 2 DevBox ufficiale, che è un contenitore Docker Magento 2. Docker su macOS è qualcosa che considero ancora inutilizzabile, almeno con un sistema che dipende fortemente dall'I/O veloce del disco come Magento 2. Quindi, lo faremo nel modo tradizionale: installa tutti i pacchetti in modo nativo sulla nostra macchina.

Configurazione del server

Installare tutto è sicuramente un po' più noioso, ma il risultato finale sarà un ambiente di sviluppo Magento fulmineo. Credimi, risparmierai ore di lavoro non dipendendo da Docker per lo sviluppo di Magento 2.

Questo tutorial presuppone un ambiente su macOS con Brew installato su di esso. Se questo non è il tuo caso, le basi rimarranno le stesse, cambiando solo il modo in cui installi i pacchetti. Iniziamo con l'installazione di tutti i pacchetti:

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

Quindi avvia i servizi:

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

Ok, ora punteremo un dominio al nostro indirizzo di loopback. Apri il file hosts in qualsiasi editor, ma assicurati di disporre delle autorizzazioni di superutente. Farlo con Vim sarebbe:

 sudo vim /etc/hosts

Quindi aggiungi la seguente riga:

 127.0.0.1 magento2.dev

Ora creeremo un vhost in Nginx:

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

Aggiungi il seguente contenuto:

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

Se non hai mai avuto a che fare con Nginx prima, questo file potrebbe spaventarti, quindi spieghiamo i piccoli frammenti qui, poiché farà anche luce su alcuni dei meccanismi interni di Magento. Le prime righe dicono semplicemente a Nginx che stiamo usando la porta HTTP predefinita e il nostro dominio è magento2.dev :

 listen 80; server_name magento2.dev;

Quindi impostiamo alcune variabili di ambiente. Il primo $MAGE_ROOT , contiene il percorso della nostra base di codice. Nota che dovrai cambiare il percorso principale in modo che corrisponda al tuo nome utente/percorso della cartella, ovunque prevedi di posizionare l'origine:

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

La seconda variabile, $MAGE_MODE , imposta la modalità di runtime per il nostro negozio. Mentre stiamo sviluppando un modulo, utilizzeremo la modalità sviluppatore. Questo ci consente di codificare più velocemente, poiché non dovremo compilare o distribuire file statici durante lo sviluppo. Le altre modalità sono produzione e default. Il reale utilizzo di quest'ultimo non è ancora chiaro.

 set $MAGE_MODE developer;

Dopo aver impostato queste variabili, definiamo il percorso radice di vhost. Nota che aggiungiamo il suffisso alla variabile $MAGE_ROOT con la cartella /pub , rendendo disponibile al web solo una parte del nostro negozio.

 root $MAGE_ROOT/pub;

Definiamo quindi il nostro file di indice, il file nginx verrà caricato quando il file richiesto non esiste, come index.php. Questo script, $MAGE_ROOT/pub/index.php , è il punto di ingresso principale per i clienti che visitano sia il carrello degli acquisti che le applicazioni di amministrazione. Indipendentemente dall'URL richiesto, index.php verrà caricato e verrà avviato il processo di invio del router.

 index index.php;

Successivamente, disattiviamo alcune funzionalità di Nginx. Innanzitutto, disattiviamo autoindex , che visualizzerebbe un elenco di file quando richiedi una cartella, ma non specifichi un file e non è presente alcun indice. In secondo luogo, disattiviamo charset , che consentirebbe a Nginx di aggiungere automaticamente le intestazioni di Charset alla risposta.

 autoindex off; charset off;

Successivamente, definiamo alcune intestazioni di sicurezza:

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

Questa posizione, / , è puntata alla nostra cartella principale $MAGE_ROOT/pub e sostanzialmente reindirizza qualsiasi richiesta ricevuta al nostro front controller index.php, insieme agli argomenti della richiesta:

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

La parte successiva potrebbe essere un po' confusa, ma è abbastanza semplice. Poche righe fa, abbiamo definito la nostra radice come $MAGE_ROOT/pub . Questa è la configurazione consigliata e più sicura, poiché la maggior parte del codice non è visibile dal Web. Ma non è l'unico modo per configurare il server web. In realtà, la maggior parte dei server Web condivisi ha una configurazione predefinita, che prevede che il server Web punti alla cartella Web. Per quegli utenti, il team di Magento ha preparato questo file per quei casi, quando la radice è definita come $MAGE_ROOT con il seguente snippet:

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

Tieni presente che, quando possibile, è meglio se il tuo server web punta alla cartella $MAGE_ROOT/pub . Il tuo negozio sarà più sicuro in questo modo.

Successivamente, abbiamo la posizione statica $MAGE_ROOT/pub/static . Questa cartella è inizialmente vuota e si riempie automaticamente con i file statici dei moduli e dei temi, come file immagine, CSS, JS, ecc. Qui fondamentalmente definiamo alcuni valori di cache per i file statici e, quando il file richiesto non lo fa esiste, reindirizzalo a $MAGE_ROOT/pub/static.php . Quello script, tra le altre cose, analizzerà la richiesta e copierà o collegherà simbolicamente il file specificato dal modulo o dal tema corrispondente, a seconda della modalità di runtime definita. In questo modo, i file statici del tuo modulo risiederanno all'interno della cartella dei nostri moduli, ma verranno serviti direttamente dalla cartella pubblica di 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"; }

Successivamente neghiamo l'accesso Web ad alcune cartelle e file limitati:

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

E l'ultimo bit è dove carichiamo php-fpm e gli diciamo di eseguire index.php ogni volta che l'utente lo colpisce:

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

Detto questo, salva il file, quindi abilitalo digitando i seguenti comandi:

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

Come installare Magento 2

Ok, a questo punto la tua macchina soddisfa i requisiti di Magento 2, mancando solo la bestia stessa. Vai al sito Web di Magento e crea un account se non ne hai ancora uno. Successivamente, vai alla pagina di download e scarica l'ultima versione (2.1.5, al momento della scrittura):

Pagina di download di Magento 2

Seleziona il formato .tar.bz2 e scaricalo. Quindi procedi all'estrazione e imposta la cartella e i permessi dei file corretti affinché Magento 2 possa funzionare:

 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

Ora, per installare le tabelle del database e creare i file di configurazione necessari, eseguiremo questo comando dal terminale:

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

Ricordati di cambiare il nome del database ( db-name ), l'utente ( db-user ) e la password ( db-password ) in modo che corrispondano a quella che hai usato durante l'installazione di MySQL, e il gioco è fatto! Questo comando installerà tutti i moduli di Magento 2, creando le tabelle e i file di configurazione richiesti. Al termine, apri il browser e vai su http://magento2.dev/. Dovresti vedere un'installazione pulita di Magento 2 con il tema Luma predefinito:

Pagina iniziale nel tema Luma predefinito

Se vai su http://magento2.dev/admin, dovresti vedere la pagina di accesso dell'applicazione Admin:

Pagina di accesso all'applicazione amministratore

Quindi utilizzare le credenziali di seguito per accedere:

Utente: admin Password: admin123

Siamo finalmente pronti per iniziare a scrivere il nostro codice!

Creazione del nostro primo modulo Magento 2

Per completare il nostro modulo, dovremo creare i seguenti file e ti guiderò attraverso l'intero processo. Avremo bisogno:

  • Alcuni file di registrazione standard, per far conoscere a Magento il nostro modulo Blog
  • Un file di interfaccia, per definire il nostro contratto dati per la Posta
  • Un Post Model, per rappresentare un Post in tutto il nostro codice, implementando l'interfaccia dati Post
  • Un Post Resource Model, per collegare il Post Model al database
  • Una raccolta di post, per recuperare più post contemporaneamente dal database con l'aiuto del Resource Model
  • Due classi di migrazione, per impostare lo schema e il contenuto della tabella
  • Due azioni: una per elencare tutti i post e un'altra per mostrare ogni post individualmente
  • Due file per blocchi, viste e layout ciascuno: uno per l'azione elenco e uno per la vista

Per prima cosa, diamo una rapida occhiata alla struttura della cartella del codice sorgente principale, in modo da poter definire dove posizionare il nostro codice. Il modo in cui abbiamo installato ha tutto il codice principale di Magento 2, insieme a tutte le sue dipendenze, che vivono all'interno della cartella del vendor del compositore.

Layout della directory del codice principale di Magento 2

Registrazione del nostro modulo

Conserveremo il nostro codice in una cartella separata, app/code . Il nome di ogni modulo è nel formato Namespace_ModuleName e la sua posizione nel filesystem deve riflettere quel nome, app/code/Namespace/ModuleName per questo esempio. Seguendo questo schema, chiameremo il nostro modulo Toptal_Blog e collocheremo i nostri file in app/code/Toptal/Blog . Vai avanti e crea quella struttura di cartelle.

Layout della directory del nostro modulo Toptal_Blog

Ora, dobbiamo creare alcuni file standard per avere il nostro modulo registrato con Magento. Innanzitutto, crea app/code/Toptal/Blog/composer.json :

 {}

Questo file verrà caricato da Composer ogni volta che lo esegui. Anche se in realtà non stiamo usando Composer con il nostro modulo, dobbiamo crearlo per rendere felice Composer.

Ora registreremo il nostro modulo con Magento. Vai avanti e crea app/code/Toptal/Blog/registration.php :

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

Qui chiamiamo il metodo register della classe ComponentRegistrar , inviando due parametri: la stringa 'module' , che è il tipo di componente che stiamo registrando, e il nome del nostro modulo, 'Toptal_Blog' . Con queste informazioni, il caricatore automatico di Magento sarà a conoscenza del nostro spazio dei nomi e saprà dove cercare le nostre classi e file XML.

Una cosa interessante da notare qui è che abbiamo il tipo del componente ( MODULE ) inviato come parametro alla funzione \Magento\Framework\Component\ComponentRegistrar::register . Non solo possiamo registrare moduli, possiamo registrare altri tipi di componenti. Ad esempio, anche i temi, le librerie esterne e i Language Pack vengono registrati utilizzando lo stesso metodo.

Continuando, creiamo il nostro ultimo file di registrazione, 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>

Questo file contiene alcune informazioni molto importanti sul nostro modulo. Loro sono:

  • Il nome del modulo è di nuovo presente, esponendo il nome del nostro modulo alla configurazione di Magento.
  • La versione di installazione di Magento, che verrà utilizzata da Magento per decidere quando eseguire gli script di migrazione del database.
  • Dipendenze del nostro modulo: mentre stiamo scrivendo un modulo semplice, dipendiamo solo da due moduli principali di Magento: Magento_Directory e Magento_Config .

Ora abbiamo un modulo che dovrebbe essere riconoscibile da Magento 2. Controlliamolo usando la CLI di Magento 2.

Innanzitutto, dobbiamo disabilitare la cache di Magento. I meccanismi della cache di Magento meritano un articolo dedicato a se stessi. Per il momento, poiché stiamo sviluppando un modulo e vogliamo che le nostre modifiche vengano riconosciute da Magento istantaneamente senza la necessità di svuotare la cache in ogni momento, lo disabiliteremo semplicemente. Dalla riga di comando, esegui:

 ./bin/magento cache:disable

Quindi vediamo se Magento è già a conoscenza delle nostre modifiche osservando lo stato dei moduli. Basta eseguire il seguente comando:

 ./bin/magento module:status

Il risultato dell'ultimo dovrebbe essere simile a:

Output del comando di stato, che mostra il modulo Toptal_Blog disabilitato

Il nostro modulo è lì, ma come mostra l'output, è ancora disabilitato. Per abilitarlo, esegui:

 ./bin/magento module:enable Toptal_Blog

Avrebbe dovuto farlo. Per essere sicuro, puoi chiamare nuovamente module:status e cercare il nome del nostro modulo nell'elenco abilitato:

Output del comando di stato, che mostra l'abilitazione del modulo Toptal_Blog

Gestione dell'archiviazione dei dati

Ora che abbiamo abilitato il nostro modulo, dobbiamo creare la tabella del database che contiene i nostri post sul blog. Questo è lo schema per la tabella che vogliamo creare:

Campo Tipo Nullo Chiave Predefinito
post_id int(10) senza segno NO PRI NULLO
titolo testo NO NULLO
contenuto testo NO NULLO
creato_a timestamp NO CURRENT_TIMESTAMP

Raggiungiamo questo obiettivo creando la classe InstallSchema , che è responsabile della gestione dell'installazione della nostra migrazione dello schema. Il file si trova in app/code/Toptal/Blog/Setup/InstallSchema.php e ha il seguente contenuto:

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

Se analizzi il metodo di install , noterai che crea semplicemente la nostra tabella e aggiunge le sue colonne una per una.

Per determinare quando eseguire una migrazione dello schema, Magento mantiene una tabella con tutte le versioni di installazione correnti per ciascun modulo e ogni volta che una versione del modulo cambia, le sue classi di migrazione vengono inizializzate. Questa tabella è setup_module e se dai un'occhiata al contenuto di quella tabella, vedrai che finora non c'è alcun riferimento al nostro modulo. Quindi, cambiamo questo. Da un terminale, lancia il seguente comando:

 ./bin/magento setup:upgrade

Questo ti mostrerà un elenco di tutti i moduli e i suoi script di migrazione che sono stati eseguiti, incluso il nostro:

Output del comando di aggiornamento, che mostra l'esecuzione della nostra migrazione

Ora, dal tuo client MySQL preferito, puoi verificare se la tabella è stata davvero creata:

Dimostrazione della nostra tabella nel client MySQL

E nella tabella setup_module , ora c'è un riferimento al nostro modulo, al suo schema e alla versione dei dati:

Contenuto della tabella setup_module

Ok, e per quanto riguarda gli aggiornamenti dello schema? Aggiungiamo alcuni post a quella tabella tramite un aggiornamento per mostrarti come farlo. Per prima cosa, setup_version sul nostro etc/module.xml :

Evidenzia il valore modificato nel nostro file module.xml

Ora creiamo il nostro file app/code/Toptal/Blog/Setup/UpgradeData.php , che è responsabile delle migrazioni dei dati (non dello schema):

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

Puoi vedere che è molto simile alla nostra classe Install. L'unica differenza è che implementa un UpgradeDataInterface invece di InstallSchemaInterface e il metodo principale è chiamato upgrade . Con questo metodo, controlli la versione installata del modulo corrente e, quando è più piccola della tua, esegui le modifiche necessarie per completare. Nel nostro esempio, stiamo verificando se la versione corrente è inferiore a 0.1.1 nella riga seguente utilizzando la funzione version_compare :

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

La chiamata $context->getVersion() restituirà 0.1.0 quando il comando CLI setup:upgrade viene chiamato per la prima volta. Quindi i dati di esempio vengono caricati nel database e la nostra versione viene spostata a 0.1.1. Per farlo funzionare, vai avanti e avvia un setup:upgrade :

 ./bin/magento setup:upgrade

E poi controlla i risultati nella tabella dei post:

Contenuto della nostra tavola

E alla tabella setup_module :

Contenuto aggiornato della tabella setup_module

Si noti che, anche se abbiamo aggiunto i dati alla nostra tabella utilizzando il processo di migrazione, sarebbe stato possibile modificare anche lo schema. Il processo è lo stesso; useresti solo UpgradeSchemaInterface invece di UpgradeDataInterface .

Definizione del modello per i post

Andando avanti, se ricordi la nostra panoramica sull'architettura, il nostro prossimo elemento costitutivo sarebbe il post sul blog ResourceModel. Il modello delle risorse è molto semplice e indica semplicemente la tabella a cui il modello si "connetterà", insieme a qual è la sua chiave primaria. Creeremo il nostro ResourceModel su app/code/Toptal/Blog/Model/ResourceModel/Post.php con i seguenti contenuti:

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

Tutte le operazioni ResourceModel, a meno che non sia necessario qualcosa di diverso dalle normali operazioni CRUD, sono gestite dalla classe padre AbstractDb .

Avremo anche bisogno di un altro ResourceModel, una Collection. La raccolta sarà responsabile di interrogare il database per più post utilizzando il nostro ResourceModel e di restituire una serie di modelli istanziati e pieni di informazioni. Creiamo il file app/code/Toptal/Blog/Model/ResourceModel/Post/Collection.php con il seguente contenuto:

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

Si noti che nel costruttore menzioniamo semplicemente il Model, che rappresenterà l'entità post in tutto il nostro codice, e il ResourceModel, che recupererà le informazioni nel database.

Il pezzo mancante per questo livello è il Post Model stesso. Il modello dovrebbe contenere tutti gli attributi che abbiamo definito nel nostro schema, insieme a qualsiasi logica aziendale di cui potresti aver bisogno. Seguendo lo schema di Magento 2, dobbiamo creare un'interfaccia dati da cui si estenderà il nostro modello. Posizioniamo l'interfaccia in app/code/Toptal/Blog/Api/Data/PostInterface.php e dovrebbe contenere i nomi dei campi della tabella, insieme ai metodi per accedervi:

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

Passiamo ora all'implementazione del modello, su app/code/Toptal/Blog/Model/Post.php . Creeremo i metodi definiti nell'interfaccia. Inoltre, specificheremo un tag di cache tramite la costante CACHE_TAG e, al costruttore, specificheremo il ResourceModel che sarà responsabile dell'accesso al database per il nostro modello.

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

Questa classe è piuttosto semplice e il suo obiettivo è esclusivamente quello di caricare i post da mostrare e fornire un metodo getPostUrl al modello. Ci sono alcune cose da notare però.

Se ricordi, non abbiamo definito una Toptal\Blog\Model\ResourceModel\Post\CollectionFactory . Abbiamo definito solo Toptal\Blog\Model\ResourceModel\Post\Collection . Quindi come funziona? Per ogni classe che definisci nel tuo modulo, Magento 2 creerà automaticamente una Factory per te. Le fabbriche hanno due metodi: create , che restituirà una nuova istanza per ogni chiamata, e get , che restituirà sempre la stessa istanza ogni volta che viene chiamato, utilizzato per implementare il modello Singleton.

Il terzo parametro del nostro Block, $data , è un array opzionale. Poiché è opzionale e non ha un suggerimento sul tipo, non verrà iniettato dal sistema di iniezione automatica. È importante notare che i parametri del costruttore facoltativo devono sempre essere posizionati per ultimi nei parametri. Ad esempio, il costruttore di Magento\Framework\View\Element\Template , la nostra classe genitore, ha questi parametri:

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

Poiché volevamo aggiungere la nostra CollectionFactory ai parametri del costruttore dopo aver esteso la classe Template, dovevamo farlo prima del parametro opzionale, altrimenti l'iniezione non avrebbe funzionato:

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

Al metodo getPosts , a cui si accederà in seguito dal nostro modello, chiamiamo semplicemente il metodo create da PostCollectionFactory , che ci restituirà una nuova PostCollection e ci consentirà di recuperare i nostri post dal database e inviarlo alla nostra risposta.

E per completare il layout di questo percorso, ecco il nostro modello 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; ?>

Si noti che qui possiamo vedere il livello View che accede al nostro ModelView ( $block->getPosts() ) che a sua volta utilizza un ResourceModel (la raccolta) per recuperare i nostri modelli ( Toptal\Blog\Model\Post ) dal database. In ogni template, ogni volta che vorrai accedere ai suoi metodi di blocco, ci sarà una variabile $block definita e in attesa delle tue chiamate.

Ora dovresti essere in grado di vedere l'elenco dei post semplicemente colpendo di nuovo il nostro percorso.

La nostra pagina indice, che mostra l'elenco dei post

Visualizzazione dei singoli post

Ora, se fai clic sul titolo di un post, otterrai un 404, quindi risolviamolo. Con tutta la nostra struttura in atto, questo diventa abbastanza semplice. Dovremo solo creare quanto segue:

  • Una nuova azione, responsabile della gestione delle richieste al percorso blog/post/view
  • Un blocco per rendere il post
  • Un modello PHTML, responsabile della vista stessa
  • Un file di layout per il percorso blog/post/visualizzazione, mettendo insieme questi ultimi pezzi.

La nostra nuova azione è abbastanza semplice. Riceverà semplicemente l' id del parametro dalla richiesta e lo registrerà nel registro principale di Magento, un repository centrale per le informazioni disponibili durante un singolo ciclo di richiesta. In questo modo, renderemo disponibile l'ID al blocco in un secondo momento. Il file dovrebbe trovarsi in app/code/Toptal/Blog/Controller/Post/View.php e questi sono i suoi contenuti:

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

Si noti che abbiamo aggiunto il parametro $coreRegistry al nostro __construct e lo abbiamo salvato come attributo per un uso successivo. Al metodo execute , recuperiamo il parametro id dalla richiesta e lo registriamo. Usiamo anche una costante di classe, self::REGISTRY_KEY_POST_ID come chiave per il registro, e useremo questa stessa costante nel nostro blocco per fare riferimento all'id nel registro.

Creiamo il blocco, in app/code/Toptal/Blog/Block/View.php con i seguenti contenuti:

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

Nel blocco di visualizzazione, definiamo un metodo protetto _getPostId , che recupererà semplicemente l'ID del post dal registro principale. Il metodo public getPost , a sua volta, caricherà in modo pigro il post e genererà un'eccezione se il post non esiste. Lanciare un'eccezione qui farà sì che Magento mostri la sua schermata di errore predefinita, che potrebbe non essere la soluzione migliore in questo caso, ma la terremo così per semplicità.

Al nostro modello PHTML. Aggiungi app/code/Toptal/Blog/view/frontend/templates/post/view.phtml con i seguenti contenuti:

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

Bello e semplice, accedendo semplicemente al metodo getPost del blocco Visualizza che abbiamo creato in precedenza.

E, per mettere insieme il tutto, creiamo un file di layout per il nostro nuovo percorso in app/code/Toptal/Blog/view/frontend/layout/blog_post_view.xml con il seguente contenuto:

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

Questo fa la stessa cosa che abbiamo fatto prima. Aggiunge semplicemente Toptal\Blog\Block\View al contenitore content , con Toptal_Blog::post/view.phtml come modello associato.

Per vederlo in azione, indirizza semplicemente il tuo browser su http://magento2.dev/blog/post/view/id/1 per caricare correttamente un post. Dovresti vedere una schermata come quella qui sotto:

Pagina per la visualizzazione dei singoli post

E come puoi vedere, dopo aver creato la nostra struttura iniziale, è davvero semplice aggiungere funzionalità alla piattaforma e la maggior parte del nostro codice iniziale viene riutilizzato nel processo.

Nel caso volessi testare velocemente il modulo, ecco il risultato totale del nostro lavoro.

Dove andare da qui

Se mi avete seguito fino a qui, congratulazioni! Sono sicuro che sei abbastanza vicino a diventare uno sviluppatore Magento 2. Abbiamo sviluppato un modulo personalizzato Magento 2 piuttosto avanzato e, anche se è semplice nelle sue caratteristiche, è stato coperto molto terreno.

Alcune cose sono state tralasciate da questo articolo, per semplicità. Per dirne alcuni:

  • L'amministratore modifica moduli e griglie per gestire i contenuti del nostro blog
  • Categorie blog, tag e commenti
  • Repository e alcuni contratti di servizio che avremmo potuto stabilire
  • Moduli di confezionamento come estensioni Magento 2

In ogni caso, ecco alcuni link utili dove approfondire ancora di più le tue conoscenze:

  • Blog di Alan Storm su Magento 2 — Alan Storm ha probabilmente il contenuto più didattico quando si tratta di imparare Magento.
  • Il blog di Alan Kent
  • Documentazione Magento: The Magento 2 Dev Docs

Ti ho fornito un'introduzione completa a tutti gli aspetti rilevanti su come creare un modulo in Magento 2 e alcune risorse aggiuntive se ne avessi bisogno. Ora tocca a te ottenere la codifica o vai ai commenti se desideri valutare.