Magento 2 modul fejlesztés lépésről lépésre – 1. rész

Ebben a cikkben a következő témákat öleljük fel:

 

1) Követelmények, dokumentumok és Magento 2.0 installálás

A legfőbb dokumentum források a Magento 2.0 rendszerrel kapcsolatban az alábbi két linken érhetők el: http://devdocs.magento.com/ https://github.com/magento/magento2 A Magento 2.0 telepítése előtt a fejlesztői környezetünk minimális beállításait el kell végeznünk, hogy a rendszert telepíteni tudjuk. Részletes információt ezen a linken olvashatunk: http://devdocs.magento.com/guides/v2.0/install-gde/bk-install-guide.html Már elsőre láthatóak az alapvető követelmények:

  • PHP 5.5.x és ennél nagyobb verziók
  • MySQL 5.6.x és ennél nagyobb verziók

Amennyiben nem rendelkezünk ezekkel, a telepítés megkezdése előtt frissíteni kell lokális fejlesztői környezetünket. Az installálás során három módszert tudunk alkalmazni. Az első esetben egyszerűen letöltjük a Magento hivatalos oldalról a két elérhető verziót. Az egyik csak az alap rendszert tartalmazza, míg a másik példa adatokkal is ellátja az installálás során a rendszert (Sample Data). A két csomag itt érhető el: https://www.magentocommerce.com/download Második telepítési lehetőség, amikor a Github-ról klónozzuk a Magento rendszert. Ennek előnye, hogy folyamatosan frissíteni tudjuk a Git segítségével a fejlesztőkörnyezetünkben. Fontos itt megjegyezni, hogy a Git klónozás során a develop branch-et kapjuk meg, így ahogy a klónozás befejeződött, célszerű átváltani (git checkout) a 2.0 vagy a merchant_beta branch-ekre. A Magento 2.0 Github elérés: https://github.com/magento/magento2   A harmadik lehetőség a Metapackage installáció. Ebben az esetben a letöltött package után a composer segítségével installáljuk a Magento 2.0 rendszert, vagyis az alap Magento modulokat a hivatalos Magento Repo-ból kapjuk, az egyéb összetevőket más repo-ból. Metapackage installálási útmutató: devdocs.magento integrator install

2) Célszerű IDE beállítások a fejlesztés megkezdése előtt

Miután sikeresen installáltuk a Magento 2.0 rendszert fejlesztőkörnyezetünkbe és az megfelelőn működik, célszerű beállítani a fejlesztéshez használt IDE-t, ami e cikk esetében a Jetbrains PhpStorm-ra vonatkozik. A Magento 2.0 az alábbi technológiákat alkalmazza működése és fejlesztése során: devdocs magento tech stack Első lépésben a projekt megnyitása után a PhpStorm-ban célszerű lefuttatni a Detect PSR-0 Namspace Roots parancsot, ami a menüből elérhető, de egyébként az IDE ezt automatikusan fel fogja ajánlani. Következő lépés a megfelelő PHP CodeSniffer beállítása. Alapból az IDE a PSR-1 és PSR-2 coding standard-ot támogatja, így itt célszerű a PSR-2-őt választani, de ajánlottabb a PSR-4 alkalmazása (azonban ez alapból nincs a PhpStorm-ban). Az IDE-ben ezen beállítások elvégzésére a cikk nem tér ki. Amennyiben megnyitunk a vendor/magento könyvtárban lévő bármely modulhoz tartozó etc/module.xml fájlt (vagy egyéb itt található xml fájlt), láthatjuk, hogy az URN feloldás jelenleg sikertelen. A Magento 2.0 számos fejlesztői parancsot tartogat számunkra, ami a fejlesztés során segítségünkre lehet. Ezen parancsok listáját így tekintethetjük meg:

  • belépünk a bin könyvtárba a terminálunkban
  • kiadjuk a „php magento” parancsot
  • ennek eredményeképpen kilistázódik az összes lehetséges parancs, amit a fejlesztés során használhatunk és igen hasznos

Visszatérve az URN feloldási problémára, ezt az alábbi paranccsal tudjunk megoldani: php bin/magento dev:urn-catalog:generate .idea/misc.xml (látható, hogy ezt nem a bin könyvtárból futtatjuk, hanem root könyvtárból) A fejlesztés megkezdése előtt kikapcsoljuk a teljes cache-t (admin felületről, vagy fent említett php magento parancsok segítségével). Amennyiben Apache szerver alatt fejlesztünk, a root könyvtárban található .htaccess fájlban beállítjuk a developer módot: SetEnv MAGE_MODE developer Célszerű a fejlesztői környezetünkben az xdebug-ot kikapcsolni (php.ini beállítás), mert ez jelentősen lassítja a rendszert.

3) Alap modul felépítése és elkészítése

Amint installáltuk a Magento 2.0-át (és nem Github-ról klónoztuk), szembesülünk azzal, hogy a jól megszokott app/code könyvtár hiányzik és minden alap Magento modul a vendor/magento könyvtárban van elhelyezve. Ettől nem kell megijedni, a fejlesztés első lépése, hogy létrehozzuk az app/code könyvtárat. Miután ez elkészült, létrehozzuk a modulunk alap könyvtárát (Vendor/Module). Ez a mi esetünkben az Aion/Test könyvtár lesz. Következő lépésben két könyvtárat hozunk létre:

  • app/code/Aion/Test/etc
  • app/code/Aion/Test/Helper

A modul alap definíciós fájlja az app/code/Aion/Test/etc könyvtárba kerül elhelyezésre, melynek neve module.xml.

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Aion_Test" setup_version="2.0.0">
        <sequence>
            <module name="Magento_Store"/>
        </sequence>
    </module>
</config>

A fájlban definiáljuk a modul nevét (Vendor_Module -> Aion_Test), a verzió számát, ami jelen esetben 2.0.0 és a függőségeket, ami jelenleg csak az alap Magento_Store modul. A következő lépésben létrehozzuk a Helper fájlt. Az alap helper fájl az app/code/Aion/Test/Helper/Data.php lesz. Nézzük a tartalmát:

<?php
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Helper;

/**
 * Aion Test helper
 */
class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
    /**
     * Path to store config if extension is enabled
     *
     * @var string
     */
    const XML_PATH_ENABLED = 'aion/basic/enabled';

    /**
     * Check if extension enabled
     *
     * @return string|null
     */
    public function isEnabled()
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_ENABLED,
            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
        );
    }
}

A helper fájlt elég üresen is létrehozni, de mi már egy alap függvényt implementáltunk (public function is Enabled()) és egy konstanst. A konstansban meghatározott string (útvonal) később a modulhoz tartozó admin konfigurációs fájlban fog értelmet kapni (system.xml). Következő lépésben elhelyezzük a modul könyvtárba (app/code/Aion/Test) a registration.php fájlt, ami a modul felismerését hivatott elvégezni a Magento 2.0 rendszerben. Nézzük ennek tartalmát:

<?php
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Aion_Test',
    __DIR__
); 

A file tartalmát bármelyik alap Magento 2.0 modulból átmásolhatjuk, a lényeg, hogy a második paraméternél a saját modulunk szerepeljen (Vendor_Modul -> Aion_Test). Mivel az alap Magento 2.0 modulok is composer-t használnak a frissítésre, mi is elkészítjük saját composer.json fájlunkat. Ennek tartalma így néz ki:

{
    "name": "aion/module-test",
    "description": "N/A",
    "require": {
        "php": "~5.5.0|~5.6.0|~7.0.0",
        "magento/module-config": "100.0.*",
        "magento/module-store": "100.0.*",
        "magento/module-backend": "100.0.*",
        "magento/framework": "100.0.*"
    },
    "type": "magento2-module",
    "version": "100.0.2",
    "license": [
        "OSL-3.0",
        "AFL-3.0"
    ],
    "autoload": {
        "files": [
            "registration.php"
        ],
        "psr-4": {
            "Aion\\Test\\": ""
        }
    }
}

A composer.json tartalmazza modulunk alap információt, függőségeket, licenc, verzió stb. adatokat. Már szinte el is készültünk, de még két fájl hiányzik egy szépen felépített modulhoz. Ez a két fájl, mely szintén a modulunk könyvtárában (app/code/Aion/Test) kap helyet: README.md és COPYING.txt. Az előbbi a Git leírás, míg az utóbbi a licenc információkat tartalmazza, ami jelen esetben OSL 3.0. Tehát így néz ki a modulunk felépítése: app/code Aion/ Test/ etc/module.xml Helper/Data.php composer.json COPYING.txt README.md registration.php   A következő lépésben aktiválni kell a modult, és megvizsgálni, hogy a Magento 2.0 rendszer felismerte-e megfelelően. A Magento 1.x rendszerben ez a frontend vagy admin oldal frissítéssel történt meg, azonban a 2.0-ban a korábban már említett parancsokat kell alkalmaznunk. A terminálban belépünk a /bin könyvtárba, majd kiadjuk a php magento module:status parancsot. Ha mindent jól végeztünk el, akkor listázásra kerül az engedélyezett (Enabled) modulok listája és még nem aktív vagy nem engedélyezett (Disabled) modulok listája. Utóbbi helyen jelenik meg a saját modulunk is: Aion_Test. Amennyiben nem jelenik meg, a modulunk felépítése során valamit elrontottunk. Ezután kiadjuk a terminálban /bin könyvtárban a php magento module:enable Aion_Test parancsot. Ekkor megjelenik, hogy a modult a rendszer felismerte és engedélyezte. Ez valójában azt jelenti, hogy az app/etc/config.php fájlban lévő tömbbe bekerült az ’Aion_Test’ => 1 bejegyzés. Ezután értesítést kapunk, hogy a modult adatbázis szinten is „regisztráljuk” be. Kiadjuk a terminálban a php magento setup:upgrade parancsot. A parancs végrehajtása során a Magento 2.0 végigfut az összes alap modulon és egyedi modulon és update-eli a rendszert, vagyis lefuttatja a séma és adat scripteket, amennyiben nagyobb verzió számot talál, mint a jelenlegi. Mindez a mi modulunk esetében az jelenti, hogy korábban leírt 2.0.0 verzió szám beírásra kerül a setup_module táblába. Ha ezután belépünk az admin felületre és kiválasztjuk Store / Configuration menüpontot, majd ezt követően az Advanced / Advanced aloldalt, akkor megjelenik a modulunk a listában enabled állapotban. Ezzel el is készültünk az alap modulunkkal.

4) Modul admin konfiguráció és jogosultság kezelés

Miután az alap modulunk elkészült, felépítjük a hozzá tartozó admin konfigurációt és jogosultság kezelést. Első lépésben a konfigurációhoz szükséges fájlt hozzuk létre, ami az app/code/Aion/Test/etc/adminhtml könyvtárban lesz elhelyezve és a neve system.xml.

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="aion_tab" translate="label" sortOrder="500">
            <label>Aion Extensions</label>
        </tab>
        <section id="aion" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1">
            <label>Test Extension</label>
            <tab>aion_tab</tab>
            <resource>Aion_Test::test</resource>
            <group id="basic" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Basic</label>
                <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Enable Extension</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
            </group>
            <group id="more" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>More</label>
                <field id="variable" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Some Variable</label>
                </field>
            </group>
        </section>
    </system>
</config>

A fájlban definiálunk egy <tab> tag-et is, így a modulunk saját menüpontban(Aion Extensions -> Test Extension) fog megjelenni az admin Stores / Configuration aloldalán. Ezen belül implementálunk két tab-ot (basic és more id-kkal). Az elsőbe helyezzük el az alap No / Yes select típusú konfigurációs változót, amit a modul engedélyezésére és tiltására használunk. Lekérdezését már korábban implementáltuk a Data.php file-ban(Helper/Data.php). A példa kedvéért létrehozunk egy text típusú konfigurációs változót is Variable névvel későbbi használatra. A fájlban még fontos rész a <resource> tag is, aminek a jogosultság kezelés implementálásakor lesz szerepe. Ez a mi esetünkben Aion_Test::test.   A következő lépésben létrehozzuk a jogosultság kezeléshez szükséges fájlt is, ami az app/code/Aion/Test/etc könyvtárba kerül acl.xml néven. Ennek tartalma:

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    <acl>
        <resources>
            <resource id="Magento_Backend::admin">
                <resource id="Magento_Backend::stores">
                    <resource id="Magento_Backend::stores_settings">
                        <resource id="Magento_Config::config">
                            <resource id="Aion_Test::test" title="Aion Test Extension Section" />
                        </resource>
                    </resource>
                </resource>
            </resource>
        </resources>
    </acl>
</config>

Amint ezzel készen vagyunk, leellenőrizzük, hogy megfelelően létrejött-e a modulhoz tartozó jogosultság beállítás a Role Resource (Admin felhasználó szerepek) részben. Az admin felületen megnyitjuk a System / User Roles menüpontot, majd itt bármelyik adminisztrátori csoportot (alapból egy van) megnyitva a Role Resouces almenüt kiválasztva, majd a Resource Access-nél a Custom lehetőség alatt megvizsgáljuk, hogy a fában megjelenik-e a saját modulunk.   Ezt követően leellenőrizzük a korábban kialakított konfigurációs menüpont, és hogy tartalma is megjelenik-e. Az adminban a Stores / Configuration-t választva a bal menüben megnézzük, hogy megjelent-e a saját menüpontunk (Aion Extensions) és annak taralma (Test Extension). Az alap konfigurációs értékeket az app/code/Aion/Test/etc könyvtárban lévő config.xml fájlban határozzuk meg:

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <aion>
            <basic>
                <enabled>1</enabled> 
            </basic>
        </aion>
    </default>
</config>

Jól látható, hogy a file-ban látható tag-ek megfelelnek a system.xml-ben lévő saját id-kkal.

5) Alap frontend controller, block, layout és template

A következőkben létrehozunk egy saját frontend controller-t, a hozzátartozó layout és router konfigurációt. Majd ezt követően egy template fájlt és a hozzá tartozó block-ot is implementáljuk. Elsőként elkészítjük a controller fájlt, ami valójában egy Action-t jelent. Ezt az app/code/Aion/Test/Controller/Index/ könyvtárban lévő Index.php-val implementáljuk. A fájl tartalma:

<?php
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Controller\Index;

use Magento\Framework\App\Action\Context;
use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\Controller\Result\ForwardFactory;
use Magento\Framework\Exception\NotFoundException;
use Magento\Framework\App\RequestInterface;

/**
 * Contact index controller
 */
class Index extends \Magento\Framework\App\Action\Action
{
    /**
     * @var PageFactory
     */
    protected $resultPageFactory;

    /**
     * @var ForwardFactory
     */
    protected $resultForwardFactory;

    /**
     * @var \Aion\Test\Helper\Data
     */
    protected $helper;

    /**
     * Index constructor.
     *
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     * @param \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory
     * @param \Aion\Test\Helper\Data $helper
     */
    public function __construct(
        Context $context,
        PageFactory $resultPageFactory,
        ForwardFactory $resultForwardFactory,
        \Aion\Test\Helper\Data $helper
    ) {
        $this->resultPageFactory = $resultPageFactory;
        $this->resultForwardFactory = $resultForwardFactory;
        $this->helper = $helper;
        parent::__construct($context);
    }

    /**
     * Dispatch request
     *
     * @param RequestInterface $request
     * @return \Magento\Framework\App\ResponseInterface
     * @throws \Magento\Framework\Exception\NotFoundException
     */
    public function dispatch(RequestInterface $request)
    {
        if (!$this->helper->isEnabled()) {
            throw new NotFoundException(__('Page not found.'));
        }
        return parent::dispatch($request);
    }

    /**
     * Aion Test Page
     *
     * @return \Magento\Framework\View\Result\Page
     */
    public function execute()
    {
        /** @var \Magento\Framework\View\Result\Page $resultPage */
        $resultPage = $this->resultPageFactory->create();
        $resultPage->getConfig()->getTitle()->set(__('Aion Test Page'));
        if (!$resultPage) {
            $resultForward = $this->resultForwardFactory->create();
            return $resultForward->forward('noroute');
        }
        return $resultPage;
    }
} 

A fájlban három fontos függvény található, mindhárom a szülőosztályban van definiálva. A __construct függvényt használjuk arra, hogy az általunk használni kívánt egyéb osztályokat injektáljuk (pl. helper osztály, dependency injection).  A dispatch függvény automatikusan le fog futni a __construct után. Itt vizsgáljuk meg, hogy a modulunk engedélyezve van-e, vagy sem és megfelelően lekezeljük. Végül pedig az execute függvény jelenti magát az action-t, ami a jelenlegi esetben az Index action-nek felel meg. A működése során a $resultPageFactory objektumot létrehozzuk (ami a későbbi layout konfiguráció alapján felépíti az oldalt). Az objektum setConfig függvényével beállítunk egy oldal title-t. Ezután megvizsgáljuk, hogy az objektum létrejött-e, vagy sem. Amennyiben nincs hiba, visszatérünk a $resultPage objektummal, ellenkező esetben noroute (vagyis 404-es) oldalra irányítjuk az action-t.   Létrehozzuk az app/code/Aion/Test/etc/frontend könyvtárban a routes.xml fájlt, amiben meghatározzuk, hogy milyen url alatt szeretnénk meghívni a modulunkhoz controller-eket. A fájl tartalma:

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="test" frontName="test">
            <module name="Aion_Test" />
        </route>
    </router>
</config>

A router konfigurációban látható, hogy a modulunkhoz a „test” url-t rendeltük, vagyis ez esetben a {{base_url}}test/index/index fogja meghívni a fent részletezett controller-t és azon belül az execute függvényt (Index action). Természetesen index controllert és action-t nem kell megadni mindig az url-ben, a {{base_url}}test/ is ugyanerre a helyre fog minket irányítani.   Következőkben létrehozzuk a layout fájlt ami az app/code/Aion/Test/view/frontend/layout könyvtárban kap helyet test_index_index.xml néven. Jól látható, hogy a fájl neve követi a router -> controller -> action neveket. A fájl tartalma:

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <title>Aion Test Page</title>
    </head>
    <body>
        <referenceContainer name="content">
            <block class="Aion\Test\Block\Test" name="testPage" template="Aion_Test::test.phtml" />
        </referenceContainer>
    </body>
</page>

A layout file <head> részében beállítjuk az oldal alap címét, a tartalmi részben (content) egy block-ot és a hozzá tartozó template fájlt is definiáljuk. A block-ot az app/code/Aion/Test/Block könyvtárban lévő Test.php-ban valósítjuk meg. A fájl tartalma:

<?php
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Block;

use Magento\Framework\View\Element\Template;

/**
 * Aion Test Page block
 */
class Test extends Template
{
    /**
     * @param Template\Context $context
     * @param array $data
     */
    public function __construct(Template\Context $context, array $data = [])
    {
        parent::__construct($context, $data);
    }

    /**
     * Test function
     *
     * @return string
     */
    public function getTest()
    {
        return 'This is a test function for some logic...';
    }
}  

Észrevehetjük, hogy a példában használt __construct függvényben nem deklaráltunk semmit, így ez lényegében el is hagyható. Azonban célszerű már az elején megvalósítani, ha például később egy storeManager vagy Helper-t vagy bármi mást is szeretnénk implementálni.   A template file az app/code/Aion/Test/view/frontend/templates könyvtárban kap helyet test.phtml néven. A fájl tartalma:

<?php
/**
 * Copyright © 2015 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
?>

<?php
/**
 * @var $block \Aion\Test\Block\Test
 */
?>

<p><?php echo $block->getTest(); ?></p>
<p><?php echo __('This is a text.') ?></p>

A template file a példa kedvéért meghívja a Block-ban definiált teszt függvényt, és emellett egy egyszerű string-et is kiír, ami egy translate függvénybe van ágyazva. Ha mindennel elkészültünk, a modulunk így néz ki: app/code Aion/ Test/ Block/Test.php Controller/Index/Index.php etc/adminhtml/system.xml /frontend/routes.xml acl.xml config.xml module.xml Helper/Data.php view/frontend /layout/test_index_index.xml /templates/test.phtml composer.json COPYING.txt README.md registration.php     Az előzőekben megismerkedtünk egy alap Magento 2.0 modul elkészítésével és felépítésével. Most folytatjuk, és megismerkedünk hogyan készíthetünk saját adatbázis táblát, tölthetjük fel alapadatokkal a modulunkhoz, és a táblaadatok kezeléséhez szükséges modellekkel, ill. collection-nel. Emellett a frontend-en megnézzük, hogy érhetőek el tábla adatok és milyen módon jeleníthetjük meg őket.

6) Modulhoz tartozó adatbázis tábla elkészítése – 1

A modulhoz tartozó adatbázis táblát egy a Magento 1.x-ből már ismert installer script segítségével készíthetjük el, azonban a file neve és felépítése kicsit eltér.   Az adatbázis táblát az app/code/Aion/Test/Setup könyvtárban lévő InstallSchema.php-ban valósítjuk meg. A fájl tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Aion\Test\Setup;

use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\DB\Adapter\AdapterInterface;

/**
 * @codeCoverageIgnore
 */
class InstallSchema implements InstallSchemaInterface
{
    /**
     * Install table
     *
     * @param \Magento\Framework\Setup\SchemaSetupInterface $setup
     * @param \Magento\Framework\Setup\ModuleContextInterface $context
     * @throws \Zend_Db_Exception
     */
    public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
    {
        $installer = $setup;

        $installer->startSetup();
        /**
         * Create table 'aion_test'
         */
        $table = $installer->getConnection()->newTable(
            $installer->getTable('aion_test')
        )->addColumn(
            'test_id',
            \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
            null,
            ['identity' => true, 'nullable' => false, 'primary' => true],
            'Test ID'
        )->addColumn(
            'name',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            255,
            ['nullable' => false],
            'Test Name'
        )->addColumn(
            'email',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            255,
            ['nullable' => false],
            'Test Email'
        )->addColumn(
            'creation_time',
            \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
            null,
            ['nullable' => false, 'default' => \Magento\Framework\DB\Ddl\Table::TIMESTAMP_INIT],
            'Test Creation Time'
        )->addColumn(
            'update_time',
            \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
            null,
            ['nullable' => false, 'default' => \Magento\Framework\DB\Ddl\Table::TIMESTAMP_INIT_UPDATE],
            'Test Modification Time'
        )->addColumn(
            'is_active',
            \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
            null,
            ['nullable' => false, 'default' => '1'],
            'Is Test Active'
        )->addIndex(
            $setup->getIdxName(
                $installer->getTable('aion_test'),
                ['name', 'email'],
                AdapterInterface::INDEX_TYPE_FULLTEXT
            ),
            ['name', 'email'],
            ['type' => AdapterInterface::INDEX_TYPE_FULLTEXT]
        )->setComment(
            'Aion Test Table'
        );
        $installer->getConnection()->createTable($table);

        $installer->endSetup();
    }
}

A script létrehozza a modulhoz tartozó példa táblát, aminek neve „aion_test” lesz. Ezután létrehozza az alábbi mezőket is:

  • test_id – primary key, smallint (6)
  • name – varchar (255)
  • email – varchar (255)
  • creation_time – timestamp
  • update_time – timestamp
  • is_active – smallint (6)

Ezt követően a scriptben célszerű és erősen ajánlott index-szel (addIndex) ellátni azon text (text és varchar és egyéb, ha szükséges) típusú oszlopokat, melyekben keresni fogunk a későbbiekben vagy a leendő collection-t szűrni fogjuk. A példa esetében ez a két mező a name és az email.

7) Modulhoz tartozó adatbázis tábla elkészítése – 2

Korábban már adtunk verziószámot a modulunkhoz az app/code/Aion/Test/etc könyvtárban lévő module.xml segítségével ‒ amiben meghatároztuk az alap verziót, majd a modul engedélyezése (terminál parancsok) segítségével ez a verziószám bekerült a „setup_module” Magento 2.0 táblába. Arra, hogy fent elkészített script lefusson, két lehetőségünk van: növeljük a verziószámot a modulunkban, vagy töröljük a modulunkhoz tartozó korábbi verziószám bejegyzést. Mivel még csak a modul fejlesztésének elején tartunk, így célszerű törölni „setup_module” Magento 2.0 táblából a modulunkhoz tartozó bejegyzést. Miután ezt megtettük, ismét futtassuk le a setup:upgrade magento parancsot a terminálból. Ha mindent jól csináltunk, a modulhoz tartozó táblánk létrejön az adatbázisban.

8) Modulhoz tartozó modellek és collection létrehozása

Ahhoz, hogy adatokkal tudjuk feltölteni az elkészült táblát vagy adatokat tudjunk lekérdezni, el kell készítenünk a modul model, resource és collection file-okat. Az alap model file az app/code/Aion/Test/Model könyvtárban lévő Test.php-ban valósítjuk meg. A fájl tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Model;

/**
 * Aion Test model
 *
 * @method \Aion\Test\Model\ResourceModel\Test _getResource()
 * @method \Aion\Test\Model\ResourceModel\Test getResource()
 * @method string getId()
 * @method string getName()
 * @method string getEmail()
 * @method setSortOrder()
 * @method int getSortOrder()
 */
class Test extends \Magento\Framework\Model\AbstractModel
{
    /**
     * Statuses
     */
    const STATUS_ENABLED = 1;
    const STATUS_DISABLED = 0;

    /**
     * Aion Test cache tag
     */
    const CACHE_TAG = 'aion_test';

    /**
     * @var string
     */
    protected $_cacheTag = 'aion_test';

    /**
     * Prefix of model events names
     *
     * @var string
     */
    protected $_eventPrefix = 'aion_test';

    /**
     * @return void
     */
    protected function _construct()
    {
        $this->_init('Aion\Test\Model\ResourceModel\Test');
    }

    /**
     * Get identities
     *
     * @return array
     */
    public function getIdentities()
    {
        return [self::CACHE_TAG . '_' . $this->getId(), self::CACHE_TAG . '_' . $this->getId()];
    }

    /**
     * Prepare item's statuses
     *
     * @return array
     */
    public function getAvailableStatuses()
    {
        return [self::STATUS_ENABLED => __('Enabled'), self::STATUS_DISABLED => __('Disabled')];
    }

}

A model file-ban definiáljuk a Magento 2.0 cache működéshez szükséges két cache tag-et. Fontos még event prefix-et is definiálni, hogy később observer-ek használata esetén tudjuk használni, mint event name. A legfontosabb a _construct() függvény, melyben meghatározzuk a modellhez tartozó resource modellt. A getAvailableStatuses() függvényt és két STATUS_* konstansokat későbbi használatra célszerű létrehozni. A getIdentities() függvény implementálása nem kötelező, de a cache kezelés megfelelő működéséhez célszerű.   Miután elkészült az alap modell fájlunk, hozzuk létre a hozzá tartozó resource modellt is. Az alap resource modell fájlt az app/code/Aion/Test/Model/ResourceModel könyvtárban lévő Test.php-ban valósítjuk meg. A fájl tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Model\ResourceModel;

/**
 * Aion Test resource model
 */
class Test extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
    /**
     * Define main table
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_init('aion_test', 'test_id');
    }
          
}

A legfontosabb, amit implementálnunk kell, a protected _construct() függvény, amiben meghatározzuk, hogy a korábban létrehozott táblához melyik mező a primary key. Miután elkészültünk a resource modellel, készítsük el a collection-t is. Az alap collection osztály fájlt az app/code/Aion/Test/Model/ResourceModel/Test könyvtárban lévő Collection.php-ban valósítjuk meg. A fájl tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Model\ResourceModel\Test;

/**
 * Aion Test collection
 */
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
    /**
     * @var string
     */
    protected $_idFieldName = 'test_id';

    /**
     * Store manager
     *
     * @var \Magento\Store\Model\StoreManagerInterface
     */
    protected $storeManager;

    /**
     * @param \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory
     * @param \Psr\Log\LoggerInterface $logger
     * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy
     * @param \Magento\Framework\Event\ManagerInterface $eventManager
     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
     * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection
     * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb|null $resource
     */
    public function __construct(
        \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory,
        \Psr\Log\LoggerInterface $logger,
        \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
        \Magento\Framework\Event\ManagerInterface $eventManager,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Framework\DB\Adapter\AdapterInterface $connection = null,
        \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
    ) {
        parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource);
        $this->storeManager = $storeManager;
    }

    /**
     * Define resource model
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_init('Aion\Test\Model\Test', 'Aion\Test\Model\ResourceModel\Test');
    }
}

A collection osztályban a _construct() függvényt implementáljuk, ahol valójában meghatározzuk, hogy a korábban elkészített modell osztályhoz melyik resource modell osztály tartozik. Lényegében ez elég is lenne collection-nek, azonban előretekintve helyezzük be (injektáljuk) már most a store manager osztályt is a public __construct() függvénybe későbbi felhasználás végett, hiszen minden modulnál követelmény a multistore támogatás. Amennyiben a három fájllal elkészültünk, akkor már létre is hoztuk azon osztályokat, melyekkel adatokat tudunk írni, ill. olvasni korábban létrehozott adatbázis táblánkból.

9) Tábla feltöltése adatokkal, script segítségével

A modell struktúra elkészítése szükséges volt ahhoz, hogy script-tel is tudjunk adatokat hozzáadni a modul alap táblájához. Az adat script-et az app/code/Aion/Test/Setup könyvtárban lévő InstallData.php-ban valósítjuk meg. A fájl tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Setup;

use Aion\Test\Model\Test;
use Aion\Test\Model\TestFactory;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;

/**
 * @codeCoverageIgnore
 */
class InstallData implements InstallDataInterface
{
    /**
     * Test factory
     *
     * @var TestFactory
     */
    private $testFactory;

    /**
     * Init
     *
     * @param TestFactory $testFactory
     */
    public function __construct(TestFactory $testFactory)
    {
        $this->testFactory = $testFactory;
    }

    /**
     * {@inheritdoc}
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
     */
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $testItems = [
            [
                'name' => 'John Doe',
                'email' => 'john.doe@example.com',
                'is_active' => 1,
            ],
            [
                'name' => 'Jane Doe',
                'email' => 'jane.doe@example.com',
                'is_active' => 0,
            ],

            [
                'name' => 'Steve Test',
                'email' => 'steve.test@example.com',
                'is_active' => 1,
            ],
        ];

        /**
         * Insert default items
         */
        foreach ($testItems as $data) {
            $this->createTest()->setData($data)->save();
        }

        $setup->endSetup();
    }

    /**
     * Create Test item
     *
     * @return Test
     */
    public function createTest()
    {
        return $this->testFactory->create();
    }
}

Az InstallData osztályban implementáljuk az install(…) függvényt, melyben létrehozzuk a teszt adatokat egy többdimenziós tömbben. Majd ezen végig iterálva meghívjuk a createTest() függvényt, melynek az új Test modell létrehozása a feladata, és a tömbök, mint adatok hozzáadása után mentjük a modellt. Itt fontos megemlíteni az osztályban létrehozott és __construct() függvényben injektált TestFactory osztályt is. Ezt az osztályt a Magento 2.0 hozza létre automatikusan a /var/generation/Aion/Test/Model könyvtárba az első futása során ($this->testFactory-> create()). Mivel még mindig a modulunk fejlesztése elején tartunk, dobjuk el manuálisan a korábban létrehozott „aion_test” táblát az adatbázisból, és töröljük a „setup_module” Magento 2.0 táblából a modulunkhoz tartozó bejegyzést. Miután ezt megtettük, ismét futtassuk le a setup:upgrade magento parancsot terminálból. Ha mindent jól csináltunk, a modulhoz tartozó táblánk ismét létrejön az adatbázisban immár adatokkal feltöltve. Természetesen ehelyett verziószám módosítással (emeléssel) is tudjuk az adatfeltöltést futtatni parancssorból, így ekkor nem kell manuálisan eldobni a korábban létrehozott táblánkat és „setup_module” táblába sem kell belenyúlni.

10) Tábla update script és verzió növelés

Ahogy fejlesztjük a modulunkat, sokszor kell módosítani az alap adatbázis táblát, esetleg új adatbázis táblát létrehozni. A Magento 1.x-ben ezt upgrade script-ekkel tehettük meg, mint adatbázis, mint pedig data oldalon. A Magento 2.0 modulunk sincs másképp, csak szerencsére az adatbázis script-eket egy fájlban kell ezúton kezelni, több különálló fájl helyett. Az adatbázis update script-et az app/code/Aion/Test/Setup könyvtárban lévő UpgradeSchema.php-ban valósítjuk meg. A fájl tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Aion\Test\Setup;

use Magento\Framework\Setup\UpgradeSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;

/**
 * @codeCoverageIgnore
 */
class UpgradeSchema implements UpgradeSchemaInterface
{
    /**
     * Upgrades DB schema, add sort_order
     *
     * @param SchemaSetupInterface $setup
     * @param ModuleContextInterface $context
     * @return void
     */
    public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
    {
        if (version_compare($context->getVersion(), '2.0.1') < 0) {
            $setup->startSetup();
            $setup->getConnection()->addColumn(
                $setup->getTable('aion_test'),
                'sort_order',
                [
                    'type' => \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
                    'length' => null,
                    'nullable' => false,
                    'default' => 0,
                    'comment' => 'Test Sort Order'
                ]
            );
            $setup->endSetup();
        }
    }
}

A példában egy új mezőt (sort_order) adunk az alap adatbázis táblához („aion_test”) upgrade script segítségével. Az upgrade függvényben implementált módosítás csak akkor fog lefutni, ha a modul verziószáma eléri a példában látható 2.0.1 értéket.

11) Adatok megjelenítése frontend-en

Ahhoz, hogy frontend-en a létrehozott adatokat meg tudjuk jeleníteni, módosítani kell a frontend template-et és a hozzá tartozó block osztályt is. Az block osztályt már korábban elkészítettük, most kiegészítjük. A block osztály az  app/code/Aion/Block/ könyvtárban lévő Test.php-ban valósítjuk meg. A fájl tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Block;

use Magento\Framework\View\Element\Template;

/**
 * Aion Test Page block
 */
class Test extends Template
{
    /**
     * @var \Aion\Test\Model\Test
     */
    protected $test;

    /**
     * Test factory
     *
     * @var \Aion\Test\Model\TestFactory
     */
    protected $testFactory;

    /**
     * @var \Aion\Test\Model\ResourceModel\Test\CollectionFactory
     */
    protected $itemCollectionFactory;

    /**
     * @var \Aion\Test\Model\ResourceModel\Test\Collection
     */
    protected $items;

    /**
     * Test constructor.
     *
     * @param \Magento\Framework\View\Element\Template\Context $context
     * @param \Aion\Test\Model\Test $test
     * @param \Aion\Test\Model\TestFactory $testFactory
     * @param array $data
     */
    public function __construct(
        Template\Context $context,
        \Aion\Test\Model\Test $test,
        \Aion\Test\Model\TestFactory $testFactory,
        \Aion\Test\Model\ResourceModel\Test\CollectionFactory $itemCollectionFactory,
        array $data = []
    ) {
        $this->test = $test;
        $this->testFactory = $testFactory;
        $this->itemCollectionFactory = $itemCollectionFactory;
        parent::__construct($context, $data);
    }

    /**
     * Retrieve Test instance
     *
     * @return \Aion\Test\Model\Test
     */
    public function getTestModel()
    {
        if (!$this->hasData('test')) {
            if ($this->getTestId()) {
                /** @var \Aion\Test\Model\Test $test */
                $test = $this->testFactory->create();
                $test->load($this->getTestId());
            } else {
                $test = $this->test;
            }
            $this->setData('test', $test);
        }
        return $this->getData('test');
    }

    /**
     * Get items
     *
     * @return bool|\Aion\Test\Model\ResourceModel\Test\Collection
     */
    public function getItems()
    {
        if (!$this->items) {
            $this->items = $this->itemCollectionFactory->create()->addFieldToSelect(
                '*'
            )->addFieldToFilter(
                'is_active',
                ['eq' => \Aion\Test\Model\Test::STATUS_ENABLED]
            )->setOrder(
                'creation_time',
                'desc'
            );
        }
        return $this->items;
    }

    /**
     * Get Test Id
     *
     * @return int
     */
    public function getTestId()
    {
        return 1;
    }

    /**
     * Return identifiers for produced content
     *
     * @return array
     */
    public function getIdentities()
    {
        return [\Aion\Test\Model\Test::CACHE_TAG . '_' . $this->getTestModel()->getId()];
    }
} 

A block osztály konstruktorába implementáljuk a korábban létrehozott Test modelt és a hozzá tartozó testFactory-t és ezek mellett a collectionFactory-t (itemCollectionFactory) is. A collectionFactory osztály nagyon hasonló a testFactory-hoz, szintén a Magento 2.0 hozza létre a var/generation/Aion/Test/Model/ResourceModel/Test/ könyvtárban az első meghívása után. A getTestModel() függvény egy modellt példányosít, és a betöltésért felelős, míg a getItems() függvény egy teljes collection-t hoz létre.   A block-hoz tartozó template file-ban kiírathatjuk az adatokat tesztelésképp. A fájl tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
?>

<?php
/**
 * @var $block \Aion\Test\Block\Test
 */
?>
<div class="aion-test">
    <h2><?php echo __('This is a test extension!') ?></h2>
    <!-- See a sample model -->
    <?php \Zend_Debug::dump($block->getTestModel()->getData()); ?>
    <!-- See a sample collection -->
    <?php \Zend_Debug::dump($block->getItems()->getData()); ?>

    <!-- See a sample collection iteration -->
    <?php $items = $block->getItems(); ?>
    <?php if ($items->getSize()) : ?>
        <?php foreach ($items as $item) : ?>
            <h3><?php echo $block->stripTags($item->getName()) ?></h3>
            <p>
                <span><?php echo __('Email:'); ?></span>&nbsp;
                <span><?php echo $item->getEmail() ?></span>
            </p>
        <?php endforeach; ?>
    <?php endif; ?>
</div>

Ebben a cikkben részletesen átvettünk jó néhány témakört az alap Magento 2.0 modulunk fejlesztéséhez: hozzáadtunk egy saját adatbázis táblát, és teszt adatokkal feltöltöttük. Az ehhez szükséges modellt, resource-t és collection-t is létrehoztuk, ill. a teszt adatokat megjelenítettük a frontend-en is.   VÉGE AZ 1. RÉSZNEK   A 2. részben megismerkedünk a modulhoz tartozó admin táblázat (grid) elkészítésével, illetve a szükséges controllerek-kel.

 

0 válaszok

Hagyjon egy választ

Want to join the discussion?
Feel free to contribute!

Vélemény, hozzászólás?

Az email címet nem tesszük közzé.