Tutorial Magento 2: Cum să construiți un modul complet

Publicat: 2022-03-11

Magento este în prezent cea mai mare platformă de comerț electronic open-source din lume. Datorită bazei de cod bogate și extensibile, comercianții cu operațiuni mari și mici din întreaga lume îl folosesc pentru o mare varietate de proiecte.

Magento 1 există de opt ani, iar succesorul său, Magento 2, a fost lansat la sfârșitul anului 2015, îmbunătățind punctele slabe ale versiunii anterioare, cum ar fi:

  • Performanta imbunatatita
  • Suită oficială de teste automatizate
  • Interfață de utilizare back-end mai bună
  • Bază de cod front-end nouă, mai modernă
  • O modalitate mai modulară de a dezvolta module, cu fișiere conținute în codul Magento în loc să fie împrăștiate peste tot
  • Număr redus de conflicte între module care încearcă să personalizeze aceeași funcționalitate

Un logo stilizat Magento 2

Puțin peste un an mai departe, iar îmbunătățirea este vizibilă, deși nu toate problemele menționate au fost rezolvate în totalitate. Acum este complet sigur să spunem că Magento 2 este un program mult mai robust decât predecesorul său. Unele dintre îmbunătățirile prezente în Magento 2 sunt:

  • Teste unitare și de integrare, inclusiv o modalitate oficială și documentată de a le crea pentru module personalizate
  • Module care sunt cu adevărat modularizate, având toate fișierele plasate într-un singur director
  • Un sistem de șabloane mai bogat, permițând dezvoltatorului temei să creeze o ierarhie de șabloane pe n nivel
  • O serie de modele de design utile adoptate în întregul cod, îmbunătățind calitatea codului și scăzând probabilitatea erorilor create de module - Acestea includ injecția automată de dependență, contracte de servicii, depozite și fabrici, pentru a numi câteva.
  • Integrare nativă în Varnish ca sistem de stocare în cache a întregii pagini, precum și Redis pentru gestionarea sesiunilor și a memoriei cache
  • Suport PHP 7

Curba de învățare pentru Magento 2, cu toate aceste modificări, a devenit și mai abruptă. În acest ghid, intenționez să vă arăt cum să vă dezvoltați primul modul Magento 2 și să vă îndrept în direcția potrivită pentru a vă continua studiile. Să ajungem la asta!

Magento 2 Tutorial Cerințe preliminare

Este important să aveți o bună înțelegere a următoarelor tehnologii/concepte pentru a urma restul acestui articol:

  • Programare orientată pe obiecte (OOP)
  • PHP
  • Spații de nume
  • MySQL
  • Utilizarea de bază a bash

Dintre toate cele de mai sus, OOP este probabil cea mai importantă. Magento a fost creat inițial de o echipă de dezvoltatori Java experimentați, iar moștenirea lor poate fi văzută cu siguranță în întreaga bază de cod. În cazul în care nu sunteți foarte încrezător în abilitățile dvs. de POO, ar putea fi o idee bună să o revizuiți înainte de a începe lucrul cu platforma.

Prezentare generală asupra arhitecturii Magento 2

Arhitectura Magento a fost concepută cu intenția de a face codul sursă cât mai modularizat și extensibil posibil. Scopul final al acestei abordări este să îi permită să fie ușor adaptat și personalizat în funcție de nevoile fiecărui proiect.

Personalizarea înseamnă de obicei schimbarea comportamentului codului platformei. În majoritatea sistemelor, aceasta înseamnă schimbarea codului „de bază”. În Magento, dacă urmați cele mai bune practici, acesta este ceva pe care îl puteți evita de cele mai multe ori, făcând posibil ca un magazin să fie la curent cu cele mai recente corecții de securitate și versiuni de funcții într-un mod fiabil.

Magento 2 este un sistem Model View ViewModel (MVVM). Deși este strâns legată de modelul său model View Controller (MVC), o arhitectură MVVM oferă o separare mai solidă între straturile Model și View. Mai jos este o explicație pentru fiecare dintre straturile unui sistem MVVM:

  • Modelul deține logica de afaceri a aplicației și depinde de o clasă asociată - ResourceModel - pentru accesul la baza de date. Modelele se bazează pe contracte de servicii pentru a-și expune funcționalitatea celorlalte straturi ale aplicației.
  • Vizualizarea este structura și aspectul a ceea ce vede un utilizator pe un ecran - HTML-ul real. Acest lucru se realizează în fișierele PHTML distribuite cu module. Fișierele PHTML sunt asociate fiecărui ViewModel din fișierele XML Layout , care ar fi denumite lianți în dialectul MVVM. Fișierele de aspect pot atribui și fișiere JavaScript pentru a fi utilizate în pagina finală.
  • ViewModel interacționează cu stratul Model, expunând doar informațiile necesare layer-ului View. În Magento 2, acest lucru este gestionat de clasele Block ale modulului. Rețineți că aceasta făcea de obicei parte din rolul de controler al unui sistem MVC. Pe MVVM, controlerul este responsabil doar pentru gestionarea fluxului de utilizator, ceea ce înseamnă că primește solicitări și fie spune sistemului să arate o vizualizare, fie să redirecționeze utilizatorul către o altă rută.

Un modul Magento 2 constă din unele, dacă nu toate, elemente ale arhitecturii descrise mai sus. Arhitectura generală este descrisă mai jos (sursă):

Diagrama arhitecturii complete Magento 2

Un modul Magento 2 poate defini, la rândul său, dependențe externe folosind Composer, managerul de dependențe al PHP. În diagrama de mai sus, vedeți că modulele de bază Magento 2 depind de Zend Framework, Symfony, precum și de alte biblioteci terțe.

Mai jos este structura Magento/Cms, un modul de bază Magento 2 responsabil cu gestionarea creării de pagini și blocuri statice.

Aspectul directorului modulului Magento/Cms

Fiecare folder deține o parte a arhitecturii, după cum urmează:

  • Api: Contracte de servicii, definirea interfețelor de servicii și a interfețelor de date
  • Bloc: Modelele de vizualizare ale arhitecturii noastre MVVM
  • Controller: Controller, responsabili de gestionarea fluxului utilizatorului în timp ce interacționează cu sistemul
  • etc: Fișiere XML de configurare—Modulul se definește pe sine și părțile sale (rute, modele, blocuri, observatori și joburi cron) în acest folder. Fișierele etc pot fi folosite și de modulele non-core pentru a suprascrie funcționalitatea modulelor de bază.
  • Helper: clase de ajutor care dețin codul utilizat în mai mult de un nivel de aplicație. De exemplu, în modulul Cms, clasele de ajutor sunt responsabile pentru pregătirea HTML pentru prezentare în browser.
  • i18n: Deține fișiere CSV de internaționalizare, utilizate pentru traducere
  • Model: pentru modele și modele de resurse
  • Observator: Deține observatori sau modele care „observă” evenimente de sistem. De obicei, atunci când un astfel de eveniment este declanșat, observatorul instanțiază un Model pentru a gestiona logica de afaceri necesară pentru un astfel de eveniment.
  • Configurare: clase de migrare, responsabil pentru crearea schemei și a datelor
  • Test: teste unitare
  • Ui: elemente de UI, cum ar fi grile și formulare utilizate în aplicația de administrare
  • vizualizare: fișiere de aspect (XML) și fișiere șablon (PHTML) pentru aplicația front-end și de administrare

De asemenea, este interesant de observat că, în practică, toate funcțiile interioare ale Magento 2 trăiesc în interiorul unui modul. În imaginea de mai sus, puteți vedea, de exemplu, Magento_Checkout , responsabil pentru procesul de checkout și Magento_Catalog , responsabil pentru manipularea produselor și categoriilor. Practic, ceea ce ne spune acest lucru este că a învăța cum să lucrezi cu module este cea mai importantă parte a devenirii unui dezvoltator Magento 2.

Bine, după această introducere relativ scurtă în arhitectura sistemului și structura modulelor, hai să facem ceva mai concret, nu? În continuare, vom parcurge tutorialul tradițional Weblog pentru a vă simți confortabil cu Magento 2 și pe cale să deveniți un dezvoltator Magento 2. Înainte de asta, trebuie să creăm un mediu de dezvoltare. Să ajungem la asta!

Configurarea mediului de dezvoltare a modulelor Magento 2

La momentul scrierii acestui articol, am putut folosi oficial Magento 2 DevBox, care este un container Magento 2 Docker. Docker pe macOS este ceva pe care încă îl consider inutilizabil, cel puțin cu un sistem care depinde în mare măsură de I/O pe disc, cum ar fi Magento 2. Așadar, o vom face în mod tradițional: instalați toate pachetele nativ pe propria noastră mașină.

Configurarea serverului

Instalarea totul este cu siguranță puțin mai obositoare, dar rezultatul final va fi un mediu de dezvoltare Magento fulgerător. Crede-mă, vei economisi ore de muncă dacă nu depinzi de Docker pentru dezvoltarea Magento 2.

Acest tutorial presupune un mediu pe macOS cu Brew instalat pe el. Dacă nu este cazul dvs., elementele de bază vor rămâne aceleași, schimbând doar modul în care instalați pachetele. Să începem cu instalarea tuturor pachetelor:

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

Apoi porniți serviciile:

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

Ok, acum vom indica un domeniu către adresa noastră de loopback. Deschideți fișierul hosts în orice editor, dar asigurați-vă că aveți permisiuni de superutilizator. A face asta cu Vim ar fi:

 sudo vim /etc/hosts

Apoi adăugați următoarea linie:

 127.0.0.1 magento2.dev

Acum vom crea un vhost în Nginx:

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

Adăugați următorul conținut:

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

Dacă nu ați mai avut de-a face cu Nginx până acum, acest fișier s-ar putea să vă sperie, așa că permiteți-ne să explicăm părțile mici aici, deoarece va arunca, de asemenea, puțină lumină asupra unora dintre funcțiile interioare ale Magento. Primele linii îi spun pur și simplu lui Nginx că folosim portul HTTP implicit, iar domeniul nostru este magento2.dev :

 listen 80; server_name magento2.dev;

Apoi setăm câteva variabile de mediu. Primul - $MAGE_ROOT deține calea către baza noastră de cod. Observați că va trebui să schimbați calea rădăcină pentru a se potrivi cu calea numelui de utilizator/dosar, oriunde doriți să plasați sursa:

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

A doua variabilă $MAGE_MODE setează modul de rulare pentru magazinul nostru. Pe măsură ce dezvoltăm un modul, vom folosi modul dezvoltator. Acest lucru ne permite să codificăm mai rapid, deoarece nu va trebui să compilam sau să implementăm fișiere statice în timpul dezvoltării. Celelalte moduri sunt producție și implicite. Utilizarea reală a acestuia din urmă nu este încă clară.

 set $MAGE_MODE developer;

După ce aceste variabile sunt setate, definim calea rădăcină a vhost. Observați că sufixăm variabila $MAGE_ROOT cu folderul /pub , făcând doar o parte din magazinul nostru disponibil pe web.

 root $MAGE_ROOT/pub;

Apoi definim fișierul nostru index - fișierul nginx se va încărca atunci când fișierul solicitat nu există - ca index.php. Acest script, $MAGE_ROOT/pub/index.php , este principalul punct de intrare pentru clienții care vizitează atât coșul de cumpărături, cât și aplicațiile de administrare. Indiferent de adresa URL solicitată, index.php va fi încărcat și procesul de expediere a routerului va începe.

 index index.php;

Apoi, dezactivăm unele funcții Nginx. În primul rând, dezactivăm autoindex , care ar afișa o listă de fișiere atunci când solicitați un folder, dar nu specificăm un fișier și nu este prezent niciun index. În al doilea rând, dezactivăm charset , ceea ce i-ar permite lui Nginx să adauge automat anteturi Charset la răspuns.

 autoindex off; charset off;

În continuare, definim câteva anteturi de securitate:

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

Această locație, / , este îndreptată către folderul nostru rădăcină $MAGE_ROOT/pub și, practic, redirecționează orice solicitare primită către controlerul nostru frontal index.php, împreună cu argumentele cererii:

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

Următoarea porțiune poate fi puțin confuză, dar este destul de simplă. Acum câteva rânduri, am definit rădăcina noastră ca $MAGE_ROOT/pub . Aceasta este configurarea recomandată și mai sigură, deoarece majoritatea codului nu este vizibil de pe web. Dar nu este singura modalitate de a configura serverul web. De fapt, majoritatea serverelor web partajate au o configurație implicită, care este ca serverul dvs. web să indice folderul dvs. web. Pentru acești utilizatori, echipa Magento a pregătit acest fișier pentru acele cazuri, când rădăcina este definită ca $MAGE_ROOT cu următorul fragment:

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

Rețineți că, ori de câte ori este posibil, cel mai bine este ca serverul dvs. web să indice folderul $MAGE_ROOT/pub . Magazinul dvs. va fi mai sigur în acest fel.

În continuare, avem locația statică $MAGE_ROOT/pub/static . Acest folder este inițial gol și umplut automat cu fișierele statice ale modulelor și temelor, cum ar fi fișiere imagine, CSS, JS, etc. Aici definim practic câteva valori cache pentru fișierele statice și, atunci când fișierul solicitat nu există, redirecționați-l către $MAGE_ROOT/pub/static.php . Acest script va analiza, printre altele, cererea și va copia sau legă simbolul fișierului specificat din modulul sau tema corespondente, în funcție de modul de rulare definit. În acest fel, fișierele statice ale modulului dvs. vor locui în folderul modulelor noastre, dar vor fi servite direct din folderul public vhost:

 location /static/ { if ($MAGE_MODE = "production") { expires max; } location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { add_header Cache-Control "public"; add_header X-Frame-Options "SAMEORIGIN"; expires +1y; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { add_header Cache-Control "no-store"; add_header X-Frame-Options "SAMEORIGIN"; expires off; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } add_header X-Frame-Options "SAMEORIGIN"; }

Apoi refuzăm accesul web la unele foldere și fișiere restricționate:

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

Și ultimul bit este locul în care încărcăm php-fpm și îi spunem să execute index.php ori de câte ori utilizatorul îl lovește:

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

Cu asta în afara drumului nostru, salvați fișierul și apoi activați-l tastând următoarele comenzi:

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

Cum se instalează Magento 2

Bine, în acest moment, mașina dvs. îndeplinește cerințele Magento 2, lipsind doar fiara în sine. Mergeți pe site-ul web Magento și creați un cont dacă încă nu aveți unul. După aceea, accesați pagina de descărcare și descărcați cea mai recentă versiune (2.1.5, la momentul scrierii):

Pagina de descărcare a Magento 2

Selectați formatul .tar.bz2 și descărcați-l. Apoi continuați să îl extrageți și să setați permisiunile corecte pentru foldere și fișiere pentru ca Magento 2 să poată funcționa:

 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

Acum, pentru a instala tabelele bazei de date și a crea fișierele de configurare necesare, vom rula această comandă din terminal:

 ./bin/magento setup:install --base-url=http://magento2.dev/ \ --db-host=127.0.0.1 --db-name=magento2 --db-user=root \ --db-password=123 --admin-firstname=Magento --admin-lastname=User \ [email protected] --admin-user=admin \ --admin-password=admin123 --language=en_US --currency=USD \ --timezone=America/Chicago --use-rewrites=1 --backend-frontname=admin

Nu uitați să schimbați numele bazei de date ( db-name ), utilizator ( db-user ) și parola ( db-password ) pentru a se potrivi cu cea pe care ați folosit-o în timpul instalării MySQL și asta este tot! Această comandă va instala toate modulele Magento 2, creând tabelele și fișierele de configurare necesare. După ce ați terminat, deschideți browserul și mergeți la http://magento2.dev/. Ar trebui să vedeți o instalare curată a Magento 2 cu tema implicită Luma:

Pagina de pornire în tema implicită Luma

Dacă mergeți la http://magento2.dev/admin, ar trebui să vedeți pagina de conectare a aplicației de administrare:

Pagina de conectare a aplicației de administrator

Apoi utilizați acreditările de mai jos pentru a vă autentifica:

Utilizator: admin Parola: admin123

Suntem în sfârșit gata să începem să scriem codul nostru!

Crearea primului nostru modul Magento 2

Pentru a finaliza modulul nostru, va trebui să creăm următoarele fișiere, iar eu vă voi ghida prin întregul proces. Noi vom avea nevoie:

  • Câteva fișiere de înregistrare standard, pentru ca Magento să cunoască modulul nostru Blog
  • Un fișier de interfață, pentru a defini contractul nostru de date pentru Post
  • Un Post Model, pentru a reprezenta o Postare în codul nostru, implementând interfața de date Post
  • Un model de resurse post, pentru a lega modelul de post la baza de date
  • O colecție de postări, pentru a prelua mai multe postări simultan din baza de date cu ajutorul modelului de resurse
  • Două clase de migrare, pentru a configura schema și conținutul tabelului nostru
  • Două acțiuni: una pentru a enumera toate postările și alta pentru a afișa fiecare postare individual
  • Câte două fișiere Blocuri, Vizualizări și Aspect: câte unul pentru acțiunea din listă și câte unul pentru vizualizare

Mai întâi, să aruncăm o privire rapidă asupra structurii directorului codului sursă de bază, astfel încât să putem defini unde să plasăm codul. Modul în care am instalat are tot codul de bază Magento 2, împreună cu toate dependențele sale, care trăiesc în folderul vendor al compozitorului.

Aspect de director al codului de bază Magento 2

Înregistrarea modulului nostru

Vom păstra codul într-un folder separat, app/code . Numele fiecărui modul este sub forma Namespace_ModuleName , iar locația sa pe sistemul de fișiere trebuie să reflecte acel nume, app/code/Namespace/ModuleName pentru acest exemplu. Urmând acest model, vom numi modulul nostru Toptal_Blog și vom plasa fișierele noastre în app/code/Toptal/Blog . Continuați și creați acea structură de foldere.

Aspect de director al modulului nostru Toptal_Blog

Acum, trebuie să creăm câteva fișiere standard pentru a avea modulul înregistrat cu Magento. Mai întâi, creați app/code/Toptal/Blog/composer.json :

 {}

Acest fișier va fi încărcat de Composer de fiecare dată când îl rulați. Chiar dacă nu folosim de fapt Composer cu modulul nostru, trebuie să-l creăm pentru a-l face pe Composer fericit.

Acum vom înregistra modulul nostru cu Magento. Continuați și creați app/code/Toptal/Blog/registration.php :

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

Aici apelăm metoda register a clasei ComponentRegistrar , trimițând doi parametri: șirul 'module' , care este tipul de componentă pe care o înregistrăm, și numele modulului nostru, 'Toptal_Blog' . Cu aceste informații, încărcătorul automat Magento va fi conștient de spațiul nostru de nume și va ști unde să caute clasele și fișierele XML.

Un lucru interesant de observat aici este că avem tipul de componentă ( MODULE ) care este trimis ca parametru la funcția \Magento\Framework\Component\ComponentRegistrar::register . Nu numai că putem înregistra module, dar putem înregistra și alte tipuri de componente. De exemplu, temele, bibliotecile externe și pachetele lingvistice sunt de asemenea înregistrate folosind aceeași metodă.

Continuând, permiteți-ne să creăm ultimul nostru fișier de înregistrare, 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>

Acest fișier conține câteva informații foarte importante despre modulul nostru. Sunt:

  • Numele modulului este prezent din nou, expunând numele modulului nostru la configurația Magento.
  • Versiunea de configurare Magento, care va fi folosită de Magento pentru a decide când să ruleze scripturile de migrare a bazei de date.
  • Dependențe ale modulului nostru — Pe măsură ce scriem un modul simplu, depindem doar de două module de bază Magento: Magento_Directory și Magento_Config .

Acum, avem un modul care ar trebui să fie recunoscut de Magento 2. Să-l verificăm folosind CLI Magento 2.

În primul rând, trebuie să dezactivăm memoria cache a Magento. Mecanismele de cache ale Magento merită un articol dedicat lor. Deocamdată, deoarece dezvoltăm un modul și dorim ca modificările noastre să fie recunoscute de către Magento instantaneu, fără a fi nevoie să ștergem memoria cache în orice moment, îl vom dezactiva pur și simplu. Din linia de comandă, rulați:

 ./bin/magento cache:disable

Apoi, să vedem dacă Magento este deja la curent cu modificările noastre, uitându-ne la starea modulelor. Pur și simplu rulați următoarea comandă:

 ./bin/magento module:status

Rezultatul ultimului ar trebui să fie similar cu:

Ieșirea comenzii de stare, care arată că modulul Toptal_Blog este dezactivat

Modulul nostru este acolo, dar după cum arată rezultatul, este încă dezactivat. Pentru a-l activa, rulați:

 ./bin/magento module:enable Toptal_Blog

Asta ar fi trebuit să o facă. Pentru a fi sigur, puteți apela din nou module:status și căutați numele modulului nostru în lista activată:

Ieșirea comenzii de stare, care arată că modulul Toptal_Blog este activat

Gestionarea stocării datelor

Acum că ne-am activat modulul, trebuie să creăm tabelul bazei de date care conține postările noastre de blog. Aceasta este schema pentru tabelul pe care vrem să-l creăm:

Camp Tip Nul Cheie Mod implicit
post_id int(10) nesemnat NU PRI NUL
titlu text NU NUL
conţinut text NU NUL
creat la timestamp-ul NU CURRENT_TIMESTAMP

Obținem acest lucru prin crearea clasei InstallSchema , care este responsabilă de gestionarea instalării migrării schemei noastre. Fișierul se află la app/code/Toptal/Blog/Setup/InstallSchema.php și are următorul conținut:

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

Dacă analizați metoda de install , veți observa că pur și simplu creează tabelul nostru și își adaugă coloanele una câte una.

Pentru a determina când să ruleze o migrare a schemei, Magento păstrează un tabel cu toate versiunile de configurare curente pentru fiecare modul și, ori de câte ori o versiune de modul se modifică, clasele sale de migrare sunt inițializate. Acest tabel este setup_module , iar dacă aruncați o privire la conținutul acelui tabel, veți vedea că nu există nicio referință la modulul nostru până acum. Deci, hai să schimbăm asta. De la un terminal, lansați următoarea comandă:

 ./bin/magento setup:upgrade

Aceasta vă va arăta o listă cu toate modulele și scripturile sale de migrare care au fost executate, inclusiv al nostru:

Ieșirea comenzii de actualizare, care arată că migrarea noastră este efectuată

Acum, din clientul dvs. MySQL preferat, puteți verifica dacă tabelul a fost într-adevăr creat:

Demonstrarea tabelului nostru în clientul MySQL

Și la tabelul setup_module , acum există o referință la modulul nostru, schema sa și versiunea de date:

Conținutul tabelului setup_module

Ok, și cum rămâne cu upgrade-urile de schemă? Să adăugăm câteva postări la acel tabel printr-o actualizare pentru a vă arăta cum să faceți asta. Mai întâi, introduceți setup_version în fișierul nostru etc/module.xml :

Evidențiați valoarea modificată în fișierul nostru module.xml

Acum creăm fișierul nostru app/code/Toptal/Blog/Setup/UpgradeData.php , care este responsabil pentru migrarea datelor (nu a schemei):

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

Puteți vedea că este foarte asemănător cu clasa noastră Instalare. Singura diferență este că implementează un UpgradeDataInterface în loc de InstallSchemaInterface , iar metoda principală se numește upgrade . Cu această metodă, verificați versiunea instalată a modulului curent și, atunci când este mai mică decât a dvs., activați modificările pe care trebuie să le efectuați. În exemplul nostru, verificăm dacă versiunea curentă este mai mică decât 0.1.1 în următoarea linie folosind funcția version_compare :

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

$context->getVersion() va returna 0.1.0 atunci când comanda CLI setup:upgrade este apelată pentru prima dată. Apoi, mostrele de date sunt încărcate în baza de date, iar versiunea noastră este mărită la 0.1.1. Pentru a rula, mergeți mai departe și declanșați un setup:upgrade :

 ./bin/magento setup:upgrade

Și apoi verificați rezultatele la tabelul cu postări:

Conținutul tabelului nostru

Și la tabelul setup_module :

Conținut actualizat al tabelului setup_module

Observați că, deși am adăugat date în tabelul nostru folosind procesul de migrare, ar fi fost posibil să schimbăm și schema. Procesul este același; veți folosi doar UpgradeSchemaInterface în loc de UpgradeDataInterface .

Definirea modelului pentru posturi

Mergând mai departe, dacă vă amintiți prezentarea noastră de ansamblu asupra arhitecturii, următorul nostru bloc ar fi articolul de blog ResourceModel. Modelul de resurse este foarte simplu și precizează pur și simplu tabelul la care se va „conecta” modelul, împreună cu cheia sa principală. Ne vom crea ResourceModel la app/code/Toptal/Blog/Model/ResourceModel/Post.php cu următorul conținut:

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

Toate operațiunile ResourceModel, cu excepția cazului în care aveți nevoie de ceva diferit de operațiunile obișnuite CRUD, sunt gestionate de clasa părinte AbstractDb .

Vom avea nevoie și de un alt ResourceModel, o colecție. Colecția va fi responsabilă de interogarea bazei de date pentru mai multe postări folosind ResourceModel-ul nostru și de livrarea unei serii de Modele instanțiate și completate cu informații. Creăm fișierul app/code/Toptal/Blog/Model/ResourceModel/Post/Collection.php cu următorul conținut:

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

Observați că în constructor menționăm pur și simplu Modelul, care va reprezenta entitatea post în codul nostru, și ResourceModel, care va prelua informațiile din baza de date.

Piesa lipsă pentru acest strat este Post Modelul însuși. Modelul ar trebui să conțină toate atributele pe care le-am definit în schema noastră, împreună cu orice logică de afaceri de care ai putea avea nevoie. Urmând modelul Magento 2, trebuie să creăm o interfață de date de la care modelul nostru se va extinde. Am plasat interfața la app/code/Toptal/Blog/Api/Data/PostInterface.php și ar trebui să conțină numele câmpurilor din tabel, împreună cu metodele de accesare a acestora:

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

Acum, la implementarea modelului, la app/code/Toptal/Blog/Model/Post.php . Vom crea metodele definite la interfață. De asemenea, vom specifica o etichetă cache prin constanta CACHE_TAG și, la constructor, vom specifica ResourceModel care va fi responsabil pentru accesul la baza de date pentru modelul nostru.

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

Această clasă este destul de simplă, iar obiectivul ei este doar de a încărca postările care urmează să fie afișate și de a oferi șablonului o metodă getPostUrl . Există însă câteva lucruri de observat.

Dacă vă amintiți, nu am definit o Toptal\Blog\Model\ResourceModel\Post\CollectionFactory . Am definit doar Toptal\Blog\Model\ResourceModel\Post\Collection . Deci cum funcționează asta? Pentru fiecare clasă pe care o definiți în modulul dvs., Magento 2 va crea automat o fabrică pentru dvs. Fabricile au două metode: create , care va returna o nouă instanță pentru fiecare apel și get , care va returna întotdeauna aceeași instanță ori de câte ori este apelată - folosită pentru a implementa modelul Singleton.

Al treilea parametru al blocului nostru, $data , este o matrice opțională. Deoarece este opțional și nu are indiciu de tip, nu va fi injectat de sistemul automat de injecție. Este important de observat că parametrii opționali ai constructorului trebuie să fie întotdeauna poziționați ultimii în parametri. De exemplu, constructorul Magento\Framework\View\Element\Template , clasa noastră părinte, are acești parametri:

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

Deoarece am vrut să adăugăm CollectionFactory la parametrii constructorului după extinderea clasei Template, a trebuit să o facem înainte de parametrul opțional, altfel injecția nu ar funcționa:

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

La metoda getPosts , care va fi accesată ulterior de șablonul nostru, numim pur și simplu metoda create din PostCollectionFactory , care ne va returna o nouă PostCollection și ne va permite să ne preluăm postările din baza de date și să o trimitem la răspunsul nostru.

Și pentru a finaliza aspectul acestui traseu, iată șablonul nostru 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; ?>

Observați că aici putem vedea stratul View accesând ModelView ( $block->getPosts() ) care, la rândul său, folosește un ResourceModel (colecția) pentru a prelua modelele noastre ( Toptal\Blog\Model\Post ) din baza de date. În fiecare șablon, ori de câte ori doriți să accesați metodele blocului său, va exista o variabilă $block definită și vă așteaptă apelurile.

Acum ar trebui să puteți vedea lista de postări apăsând din nou pe traseul nostru.

Pagina noastră de index, care arată lista de postări

Vizualizarea postărilor individuale

Acum, dacă dați clic pe titlul unei postări, veți obține un 404, așa că haideți să rezolvăm asta. Cu toată structura noastră la loc, acest lucru devine destul de simplu. Va trebui doar să creăm următoarele:

  • O nouă acțiune, responsabilă cu gestionarea solicitărilor către traseul blog/post/view
  • Un bloc pentru a reda postarea
  • Un șablon PHTML, responsabil pentru vizualizarea în sine
  • Un fișier de aspect pentru traseul blog/post/vizualizare, punând împreună aceste ultime piese.

Noua noastră acțiune este destul de simplă. Pur și simplu va primi id -ul parametrului de la cerere și îl va înregistra în registrul de bază Magento, un depozit central pentru informații care sunt disponibile pe parcursul unui singur ciclu de solicitare. Făcând acest lucru, vom pune ID-ul disponibil pentru bloc mai târziu. Fișierul ar trebui să fie localizat la app/code/Toptal/Blog/Controller/Post/View.php și acesta este conținutul său:

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

Observați că am adăugat parametrul $coreRegistry __construct și l-am salvat ca atribut pentru utilizare ulterioară. La metoda execute , preluăm parametrul id din cerere și îl înregistrăm. De asemenea, folosim o constantă de clasă, self::REGISTRY_KEY_POST_ID ca cheie pentru registru și vom folosi aceeași constantă la blocul nostru pentru a ne referi la id-ul din registru.

Să creăm blocul, la app/code/Toptal/Blog/Block/View.php cu următorul conținut:

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

La blocul de vizualizare, definim o metodă protejată _getPostId , care pur și simplu va prelua ID-ul postării din registrul de bază. Metoda publică getPost va încărca leneș postarea și va arunca o excepție dacă postarea nu există. Aruncarea unei excepții aici va face ca Magento să-și arate ecranul de eroare implicit, care ar putea să nu fie cea mai bună soluție într-un astfel de caz, dar o vom păstra astfel de dragul simplității.

Treceți la șablonul nostru PHTML. Adăugați app/code/Toptal/Blog/view/frontend/templates/post/view.phtml cu următorul conținut:

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

Frumos și simplu, accesând pur și simplu metoda View block getPost pe care am creat-o mai devreme.

Și, pentru a pune totul împreună, creăm un fișier de aspect pentru noua noastră rută la app/code/Toptal/Blog/view/frontend/layout/blog_post_view.xml cu următorul conținut:

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

Aceasta face același lucru pe care l-am făcut și înainte. Pur și simplu adaugă Toptal\Blog\Block\View în containerul de content , cu Toptal_Blog::post/view.phtml ca șablon asociat.

Pentru a-l vedea în acțiune, pur și simplu direcționați browserul către http://magento2.dev/blog/post/view/id/1 pentru a încărca cu succes o postare. Ar trebui să vedeți un ecran precum cel de mai jos:

Pagina pentru afișarea postărilor individuale

Și după cum puteți vedea, după crearea structurii noastre inițiale, este foarte simplu să adăugați funcții la platformă, iar cea mai mare parte a codului nostru inițial este reutilizat în acest proces.

În cazul în care doriți să testați rapid modulul, iată rezultatul total al muncii noastre.

Unde să mergi de aici

Dacă m-ați urmărit până aici, felicitări! Sunt convins că sunteți destul de aproape de a deveni un dezvoltator Magento 2. Am dezvoltat un modul personalizat Magento 2 destul de avansat și, deși este simplu în caracteristicile sale, a fost acoperit mult teren.

Unele lucruri au fost omise din acest articol, de dragul simplității. A numi câteva:

  • Admin editează formulare și grile pentru a gestiona conținutul blogului nostru
  • Categorii de bloguri, etichete și comentarii
  • Depozitele și câteva contracte de servicii pe care le-am fi putut stabili
  • Împachetarea modulelor ca extensii Magento 2

În orice caz, iată câteva link-uri utile unde vă puteți aprofunda și mai mult cunoștințele:

  • Blogul Alan Storm despre Magento 2 — Alan Storm are probabil cel mai didactic conținut atunci când vine vorba de învățarea Magento.
  • Blogul lui Alan Kent
  • Documentație Magento: documentele Magento 2 Dev

V-am oferit o introducere cuprinzătoare la toate aspectele relevante ale modului de a crea un modul în Magento 2 și câteva resurse suplimentare dacă aveți nevoie de ele. Acum rămâne la latitudinea dvs. să obțineți codificare sau mergeți la comentarii dacă doriți să cântăriți.