Ebben a cikkben a következő témákat öleljük fel:
1) Admin menüpont és táblázat (grid) elkészítése
Első lépésben létrehozunk az adminisztrációs felületen a modulunkhoz tartozó menüpontot. Ezt külön (új) főmenüben is elhelyezhetjük, de célszerű a modul működéséhez igazodva besorolni a már létező főmenüpontok alá. Jelen esetben a Content főmenüpontban helyezzük el a sajátunkat. Ehhez szükségünk lesz egy új file-ra. A menüpontot az app/code/Aion/Test/etc/adminhtml/ könyvtárban lévő menu.xml-ben valósítjuk meg. A fájl tartalma:
<?xml version="1.0"?>
<!--
/**
* Copyright © 2016 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_Backend:etc/menu.xsd">
<menu>
<add id="Aion_Test::content_elements" title="Test Extension" module="Magento_Backend" sortOrder="10" parent="Magento_Backend::content"
resource="Magento_Backend::content_elements"/>
<add id="Aion_Test::aion_test" title="Manage Items" module="Aion_Test" sortOrder="10" parent="Aion_Test::content_elements" action="test/test"
resource="Aion_Test::test"/>
</menu>
</config>
A file-ban első lépésben definiálunk egy főelemet, ami nem más, mint az id=Aion_Test::content_elements és a menüpontot ez alá helyezzük el, úgy hogy a menüpont (id=Aion_Test::aion_test) parent-nél a főelemet adjuk meg. Ha mindent jól csináltunk, az adminisztrációs felületen a Content főmenüpontban megjelenik a saját almenüpontunk is. A menüpontnál még fontos megemlíteni az Action=”test/test” paramétert, ami a később kialakítandó adminhtml controller útvonalát hivatott megadni. A következő lépésben szükséges elkészíteni az adminhtml controllert és layout file-t, ami a grid megjelenítésért felelős. Azonban előtte csinálunk egy abstract controller osztályt, hogy a backend jogosultság kezelést egy helyen valósítsuk meg. Az abstract controller osztályt az app/code/Aion/Test/Controller/Adminhtml/ 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\Controller\Adminhtml;
/**
* Aion manage items controller
*/
abstract class Test extends \Magento\Backend\App\Action
{
/**
* Core registry
*
* @var \Magento\Framework\Registry
*/
protected $_coreRegistry = null;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
*/
public function __construct(\Magento\Backend\App\Action\Context $context, \Magento\Framework\Registry $coreRegistry)
{
$this->_coreRegistry = $coreRegistry;
parent::__construct($context);
}
/**
* Init page
*
* @param \Magento\Backend\Model\View\Result\Page $resultPage
* @return \Magento\Backend\Model\View\Result\Page
*/
protected function initPage($resultPage)
{
$resultPage->setActiveMenu('Aion_Test::aion_test')
->addBreadcrumb(__('Test'), __('Test'))
->addBreadcrumb(__('Items'), __(''));
return $resultPage;
}
/**
* Check the permission to run it
*
* @return boolean
*/
protected function _isAllowed()
{
return $this->_authorization->isAllowed('Aion_Test::test_menu');
}
}
Az abstract controller initPage() függvénye (metódusa) felelős az aktív menüpont beállítása mellett a breadcrumb (morzsa menü) útvonal beállításáért is. A másik fontos függvény az _isAllowed(), ami az adminisztrátor jogosultságot ellenőrzi és kezeli. Ezután elkészítjük az admin táblázat (grid) kezeléséhez szükséges controllert is, amit az imént említett abstract controller-ből fogunk kiterjeszteni. A controller osztályt az app/code/Aion/Test/Controller/Adminhtml/Test könyvtárban lévő Index.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\Controller\Adminhtml\Test;
class Index extends \Aion\Test\Controller\Adminhtml\Test
{
/**
* @var \Magento\Framework\View\Result\PageFactory
*/
protected $resultPageFactory;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
* @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context, $coreRegistry);
}
/**
* Index action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
/** @var \Magento\Backend\Model\View\Result\Page $resultPage */
$resultPage = $this->resultPageFactory->create();
$this->initPage($resultPage)->getConfig()->getTitle()->prepend(__('Items'));
return $resultPage;
}
}
Az Index controller felelős az admin táblázat (grid) megjelenítésért. A Magento 2.0-ban minden controller osztály (file) valójában egy action-nek felel meg. Vagyis jelen esetben a Magento 1.x-ből megismert IndexAction() függvényt immár az execute() függvény helyettesíti. Tehát minden controller action-höz külön controller file tartozik és egyetlen execute() függvény. Ez első látásra felvetheti a kérdést, hogy miért jó ez? Lényegében így a teljes modul kódja jobban átlátható, mint a korábban Magento 1.x-ben lévő controller-ek esetében, amelyek sok esetben a fejlesztés végére hosszú script-et eredményeztek. A controller-ben lévő $resultPage és $this->resultPageFactory helyettesíti Magento 1.x-ből megismert $this->loadLayout() és $this->renderLayout() hívásokat. Ahhoz, hogy a Magento 2.0 felismerje a létrehozott adminhtml controller-ek útvonalát, ezt definiálnunk kell egy külön fájl-ban. Az útvonal definiálását az app/code/Aion/Test/etc/adminhtml/ könyvtárban lévő routes.xml-ban valósítjuk meg. A fájl tartalma:
<?xml version="1.0"?>
<!--
/**
* Copyright © 2016 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="admin">
<route id="test" frontName="test">
<module name="Aion_Test" before="Magento_Backend" />
</route>
</router>
</config>
A fájlban két lényeges tag és hozzá tartozó paraméter látható. Az első a <router id=”admin”> ami jelzi, hogy ez egy backend útvonal. A második a <route id=”test” frontName=”test”> ahol a frontName határozza meg az elkészült adminhtml controllerek fő útvonalát (az admin url utáni első paramétert). Következő lépésben létre kell hozni a collection-t, ami a fenti admin táblázatot (grid) fogja kiszolgálni adatokkal. A collection-t az app/code/Aion/Model/ResourceModel/Test/Grid/ 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\Grid;
use Magento\Framework\Api\Search\SearchResultInterface;
use Magento\Framework\Search\AggregationInterface;
use Aion\Test\Model\ResourceModel\Test\Collection as TestCollection;
/**
* Collection for displaying grid of Aion Items
*/
class Collection extends TestCollection implements SearchResultInterface
{
/**
* @var AggregationInterface
*/
protected $aggregations;
/**
* @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 string $mainTable
* @param string $eventPrefix
* @param string $eventObject
* @param string $resourceModel
* @param string $model
* @param string|null $connection
* @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
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,
$mainTable,
$eventPrefix,
$eventObject,
$resourceModel,
$model = 'Magento\Framework\View\Element\UiComponent\DataProvider\Document',
$connection = null,
\Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
) {
parent::__construct(
$entityFactory,
$logger,
$fetchStrategy,
$eventManager,
$storeManager,
$connection,
$resource
);
$this->_eventPrefix = $eventPrefix;
$this->_eventObject = $eventObject;
$this->_init($model, $resourceModel);
$this->setMainTable($mainTable);
}
/**
* @return AggregationInterface
*/
public function getAggregations()
{
return $this->aggregations;
}
/**
* @param AggregationInterface $aggregations
* @return $this
*/
public function setAggregations($aggregations)
{
$this->aggregations = $aggregations;
}
/**
* Retrieve all ids for collection
* Backward compatibility with EAV collection
*
* @param int $limit
* @param int $offset
* @return array
*/
public function getAllIds($limit = null, $offset = null)
{
return $this->getConnection()->fetchCol($this->_getAllIdsSelect($limit, $offset), $this->_bindParams);
}
/**
* Get search criteria.
*
* @return \Magento\Framework\Api\SearchCriteriaInterface|null
*/
public function getSearchCriteria()
{
return null;
}
/**
* Set search criteria.
*
* @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
* @return $this
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function setSearchCriteria(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria = null)
{
return $this;
}
/**
* Get total count.
*
* @return int
*/
public function getTotalCount()
{
return $this->getSize();
}
/**
* Set total count.
*
* @param int $totalCount
* @return $this
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function setTotalCount($totalCount)
{
return $this;
}
/**
* Set items list.
*
* @param \Magento\Framework\Api\ExtensibleDataInterface[] $items
* @return $this
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function setItems(array $items = null)
{
return $this;
}
}
A fájlban definiált osztály felelős a kialakítandó admin táblázatban(grid) az adatok hozzáadásáért, keresés és lapozás implementálásáért. A következőkben leírt UI Components megfelelő működéséhez szükséges a fenti függvények implementálása. Másik előnye, hogy az itt definiált osztályt könnyen máshol is fel tudjuk használni, amennyiben máshol is megszeretnénk jeleníteni a modul adatait admin táblázatban(grid), például egy product vagy customer ajax tab-on az adminisztrációs felületen. Már csak egy dolog van hátra, ami nem más, mint az Index controllerhez tartozó layout file elkészítése. A layout file-t az app/code/Aion/Test/view/adminhtml/layout/ könyvtárban lévő test_test_index.xml-ban valósítjuk meg. Jól látható a korábban definiált route a fájl nevében: alap route -> könyvtár -> controller action. A fájl tartalma:
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceContainer name="content">
<uiComponent name="test_test_listing"/>
</referenceContainer>
</body>
</page>
A layout fájlban lévő „content” referencia konténerben van definiálva a korábban említett UI component file neve, amire a következő pontban térünk ki részletesen.
2) UI Components avagy az admin táblázat (grid) új kialakítása
A Magento 2.0-ban bevezetett UI component-ek használatával sokkal könnyebben tudunk admin táblázatot (grid) létrehozni és emellett az adminisztrátor számára sokkal több lehetőséget nyújt a táblázatban történő kereséshez, szűréshez, az oszlopok tetszőleges megjelenítéséhez. Emellett külön megjelenítések menthetők le. A UI component-ek működéséhez több fájlt is létre kell hoznunk és megfelelően implementálnunk. A legfontosabb fájl, ami az admin táblázat működését és megjelenését meghatározza egy xml fájl. Ez a mi modulunk esetében a test_test_listing.xml nevet kapta (lásd előző pont) és az app/code/Aion/Test/view/adminhtml/ui_component könyvtárban van elhelyezve. A fájl nagyon hosszú, így tartalmát darabolva jelenítjük meg cikkünkben.
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
* Copyright © 2016 AionNext Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">test_test_listing.test_test_listing_data_source</item>
<item name="deps" xsi:type="string">test_test_listing.test_test_listing_data_source</item>
</item>
<item name="spinner" xsi:type="string">test_test_columns</item>
<item name="buttons" xsi:type="array">
<item name="add" xsi:type="array">
<item name="name" xsi:type="string">add</item>
<item name="label" xsi:type="string" translate="true">Add New Item</item>
<item name="class" xsi:type="string">primary</item>
<item name="url" xsi:type="string">*/*/new</item>
</item>
</item>
</argument>
<dataSource name="test_test_listing_data_source">
<argument name="dataProvider" xsi:type="configurableObject">
<argument name="class" xsi:type="string">TestGridDataProvider</argument>
<argument name="name" xsi:type="string">test_test_listing_data_source</argument>
<argument name="primaryFieldName" xsi:type="string">test_id</argument>
<argument name="requestFieldName" xsi:type="string">id</argument>
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="update_url" xsi:type="url" path="mui/index/render"/>
</item>
</argument>
</argument>
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item>
</item>
</argument>
</dataSource>
…
A fájlban lévő első argument tag-ben vannak definiálva az alábbiak:
- adat forrás név (test_test_listing_data_source) lásd második dataSource tag: <dataSource name=”test_test_listing_data_source”>
- oszlopok tag neve: test_test_columns, erre továbbiakban lesz szükségünk
- az új elem hozzáadása és egyéb gombok definiálása, lásd: <item name=”buttons” xsi:type=”array”> tag
…
<container name="listing_top">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="template" xsi:type="string">ui/grid/toolbar</item>
</item>
</argument>
<bookmark name="bookmarks">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="storageConfig" xsi:type="array">
<item name="namespace" xsi:type="string">test_test_listing</item>
</item>
</item>
</argument>
</bookmark>
<component name="columns_controls">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="columnsData" xsi:type="array">
<item name="provider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns</item>
</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/controls/columns</item>
<item name="displayArea" xsi:type="string">dataGridActions</item>
</item>
</argument>
</component>
…
Az xml fájlt tovább böngészve definiálásra kerülnek a táblázat felett elhelyezkedő funkciók. Ezek az alábbiak:
- lementhetjük az aktuális táblázat megjelenést több view-ban, lásd: <bookmark name=”bookmarks”> tag
- beállíthatjuk túl sok oszlop esetén, hogy melyek jelenjenek meg és az előbb említett bookmarks-nál menthetjük le, lásd: <component name=”columns_controls”> tag
…
<filterSearch name="fulltext">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="provider" xsi:type="string">test_test_listing.test_test_listing_data_source</item>
<item name="chipsProvider" xsi:type="string">test_test_listing.test_test_listing.listing_top.listing_filters_chips</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.search</item>
</item>
</item>
</argument>
</filterSearch>
<filters name="listing_filters">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="columnsProvider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.filters</item>
</item>
<item name="templates" xsi:type="array">
<item name="filters" xsi:type="array">
<item name="select" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/form/element/ui-select</item>
<item name="template" xsi:type="string">ui/grid/filters/elements/ui-select</item>
</item>
</item>
</item>
<item name="childDefaults" xsi:type="array">
<item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.listing_filters</item>
<item name="imports" xsi:type="array">
<item name="visible" xsi:type="string">test_test_listing.test_test_listing.test_test_columns.${ $.index }:visible</item>
</item>
</item>
</item>
</argument>
</filters>
…
Hozzáadásra kerül a text alapú kereső és a táblázat szűrése (filters). Ezek az alábbiak:
- varchar, text típusú oszlopokban kereshetünk egy input mezőben, lásd: <filterSearch name=”fulltext”> tag
- minden egyes oszlopot szűrhetünk megfelelő paraméterek szerint view(Aion\Test\Ui\Component\Listing\Column\Test\Options), select, dátum, ID(range), text típusú szűrések, lásd: <filters name=”listing_filters”> tag
…
<massaction name="listing_massaction">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="selectProvider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns.ids</item>
<item name="indexField" xsi:type="string">test_id</item>
</item>
</argument>
<action name="delete">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">delete</item>
<item name="label" xsi:type="string" translate="true">Delete</item>
<item name="url" xsi:type="url" path="test/test/massDelete"/>
<item name="confirm" xsi:type="array">
<item name="title" xsi:type="string" translate="true">Delete items</item>
<item name="message" xsi:type="string" translate="true">Are you sure you wan't to delete selected items?</item>
</item>
</item>
</argument>
</action>
<action name="disable">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">disable</item>
<item name="label" xsi:type="string" translate="true">Disable</item>
<item name="url" xsi:type="url" path="test/test/massDisable"/>
</item>
</argument>
</action>
<action name="enable">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">enable</item>
<item name="label" xsi:type="string" translate="true">Enable</item>
<item name="url" xsi:type="url" path="test/test/massEnable"/>
</item>
</argument>
</action>
</massaction>
…
A massaction tag-en belül kerülnek hozzáadásra tetszőleges általunk definiált műveletek, melyek a tömeges adatmódosításra szolgálnak. A modulunkban ezek az alábbiak:
- tömeges törlés, lásd: <action name=”delete”> tag
- tömeges engedélyezés és tiltás, lásd: <action name=”disable”> és <action name=”enable”> tag-ek. Ezek a modulunk adatbázis táblájában korában kialakított is_active adatot módosítják.
…
<paging name="listing_paging">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.paging</item>
</item>
<item name="selectProvider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns.ids</item>
</item>
</argument>
</paging>
</container>
…
A <paging name=”listing_paging”> tag implementálja lapozást és a listázott elemek számának választhatóságát (select) a táblázatunkban.
…
<columns name="test_test_columns">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current</item>
</item>
</item>
<item name="childDefaults" xsi:type="array">
<item name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns_editor</item>
<item name="target" xsi:type="string">startEdit</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">${ $.$data.rowIndex }</item>
<item name="1" xsi:type="boolean">true</item>
</item>
</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
<item name="root" xsi:type="string">columns.${ $.index }</item>
<item name="namespace" xsi:type="string">current.${ $.storageConfig.root }</item>
</item>
</item>
</item>
</argument>
<selectionsColumn name="ids">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="indexField" xsi:type="string">test_id</item>
</item>
</argument>
</selectionsColumn>
…
Fentiek után következik a táblázat oszlopainak meghatározása, lásd: <columns name=”test_test_columns”> tag. Ennek nevét már a fájl elején definiáltuk. A korábban említett mass action-ökhez beállított ID mező, lásd: <selectionsColumn name=”ids”> tag.
…
<column name="test_id">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">textRange</item>
<item name="sorting" xsi:type="string">asc</item>
<item name="label" xsi:type="string" translate="true">ID</item>
</item>
</argument>
</column>
<column name="name">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="editor" xsi:type="array">
<item name="editorType" xsi:type="string">text</item>
<item name="validation" xsi:type="array">
<item name="required-entry" xsi:type="boolean">true</item>
</item>
</item>
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">Name</item>
</item>
</argument>
</column>
<column name="email">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="editor" xsi:type="array">
<item name="editorType" xsi:type="string">text</item>
<item name="validation" xsi:type="array">
<item name="required-entry" xsi:type="boolean">true</item>
</item>
</item>
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">Email</item>
</item>
</argument>
</column>
<column name="creation_time" class="Magento\Ui\Component\Listing\Columns\Date">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">dateRange</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
<item name="dataType" xsi:type="string">date</item>
<item name="label" xsi:type="string" translate="true">Created</item>
</item>
</argument>
</column>
<column name="update_time" class="Magento\Ui\Component\Listing\Columns\Date">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">dateRange</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
<item name="dataType" xsi:type="string">date</item>
<item name="label" xsi:type="string" translate="true">Modified</item>
</item>
</argument>
</column>
<column name="sort_order">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="editor" xsi:type="array">
<item name="editorType" xsi:type="string">text</item>
<item name="validation" xsi:type="array">
<item name="required-entry" xsi:type="boolean">true</item>
</item>
</item>
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">Sort Order</item>
</item>
</argument>
</column>
<column name="is_active">
<argument name="data" xsi:type="array">
<item name="options" xsi:type="array">
<item name="disable" xsi:type="array">
<item name="value" xsi:type="string">0</item>
<item name="label" xsi:type="string" translate="true">Disabled</item>
</item>
<item name="enable" xsi:type="array">
<item name="value" xsi:type="string">1</item>
<item name="label" xsi:type="string" translate="true">Enabled</item>
</item>
</item>
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">select</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
<item name="editor" xsi:type="string">select</item>
<item name="dataType" xsi:type="string">select</item>
<item name="label" xsi:type="string" translate="true">Status</item>
</item>
</argument>
</column>
<actionsColumn name="actions" class="Aion\Test\Ui\Component\Listing\Column\TestActions">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="indexField" xsi:type="string">test_id</item>
</item>
</argument>
</actionsColumn>
</columns>
</listing>
Ezt követően a táblázatban lévő oszlopokat kell meghatároznunk. Az egyes oszlopoknál beállíthatjuk a típust, pl.: text, select, textRange, dateRange stb. Az utolsó oszlop az alap action-öket tartalmazza, lásd: <actionsColumn name=”actions” class=”Aion\Test\Ui\Component\Listing\Column\TestActions”> tag Ezzel el is készültünk a grid definíciós xml-lel (test_test_listing.xml). A továbbiakban megnézünk néhány osztályt, ami az utolsó oszlopban lévő action-ökért felel.
3) UI component osztályok
Az előző pontban kialakított grid definíciós xml-ben található action oszlop működéséhez szükségünk van egy osztályra, mely a megjelenítést és a működést segítik. Az első az előző pontban látható <actionsColumn name=”actions” class=”Aion\Test\Ui\Component\Listing\Column\TestActions”> tag-ben látható a TestActions osztály. A fájl az app/code/Aion/Test/Ui/Component/Listing/Column könyvtárban van elhelyezve TestActions.php néven. A fájl tartalma:
<?php
/**
* Copyright © 2016 AionNext Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Aion\Test\Ui\Component\Listing\Column;
use Magento\Framework\UrlInterface;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;
/**
* Class TestActions
*/
class TestActions extends Column
{
/**
* Url path
*/
const URL_PATH_EDIT = 'test/test/edit';
const URL_PATH_DELETE = 'test/test/delete';
/**
* @var UrlInterface
*/
protected $urlBuilder;
/**
* Constructor
*
* @param ContextInterface $context
* @param UiComponentFactory $uiComponentFactory
* @param UrlInterface $urlBuilder
* @param array $components
* @param array $data
*/
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
UrlInterface $urlBuilder,
array $components = [],
array $data = []
) {
$this->urlBuilder = $urlBuilder;
parent::__construct($context, $uiComponentFactory, $components, $data);
}
/**
* Prepare Data Source
*
* @param array $dataSource
* @return array
*/
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as & $item) {
if (isset($item['test_id'])) {
$item[$this->getData('name')] = [
'edit' => [
'href' => $this->urlBuilder->getUrl(
static::URL_PATH_EDIT,
[
'test_id' => $item['test_id']
]
),
'label' => __('Edit')
],
'delete' => [
'href' => $this->urlBuilder->getUrl(
static::URL_PATH_DELETE,
[
'test_id' => $item['test_id']
]
),
'label' => __('Delete'),
'confirm' => [
'title' => __('Delete "${ $.$data.name }"'),
'message' => __('Are you sure you wan\'t to delete a "${ $.$data.name }" record?')
]
]
];
}
}
}
return $dataSource;
}
}
Az osztály előállítja a mass action megjelenítéséhez szükséges tömböt a megfelelő formátumban. A file elején lévő konstantsoknál fontos a pontos útvonal meghatározása, hogy a megfelelő adminhtml controller-re mutassanak.
4) Adminhtml controller-ek
A grid teljes működéséhez néhány controller-t még el kell készíteni. Nézzük sorban őket. A tömeges törléshez a massDelete controller-t használjuk. A fájl az app/code/Aion/Test/Controller/Adminhtml/Test/ könyvtárban van elhelyezve MassDelete.php néven. A fájl tartalma:
<?php
/**
* Copyright © 2016 AionNext Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Aion\Test\Controller\Adminhtml\Test;
use Magento\Framework\Controller\ResultFactory;
use Magento\Backend\App\Action\Context;
use Magento\Ui\Component\MassAction\Filter;
use Aion\Test\Model\ResourceModel\Test\CollectionFactory;
/**
* Class MassDelete
*/
class MassDelete extends \Magento\Backend\App\Action
{
/**
* @var Filter
*/
protected $filter;
/**
* @var CollectionFactory
*/
protected $collectionFactory;
/**
* @param Context $context
* @param Filter $filter
* @param CollectionFactory $collectionFactory
*/
public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
{
$this->filter = $filter;
$this->collectionFactory = $collectionFactory;
parent::__construct($context);
}
/**
* Execute action
*
* @return \Magento\Backend\Model\View\Result\Redirect
* @throws \Magento\Framework\Exception\LocalizedException|\Exception
*/
public function execute()
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
$collectionSize = $collection->getSize();
foreach ($collection as $item) {
$item->delete();
}
$this->messageManager->addSuccess(__('A total of %1 record(s) have been deleted.', $collectionSize));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $resultRedirect->setPath('*/*/');
}
}
A controller osztály execute() függvénye – vagyis az action – egy collection-t kap (\Magento\Ui\Component\MassAction\Filter osztálytól), amin végig iterálva törli az elemeket. A tömeges státusz módosításhoz a massEnable és massDisable controller-eket használjuk. A fájlok az app/code/Aion/Test/Controller/Adminhtml/Test/ könyvtárban vannak elhelyezve MassEnable.php és MassDisable.php néven. A fájlok tartalma:
<?php
/**
* Copyright © 2016 AionNext Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Aion\Test\Controller\Adminhtml\Test;
use Magento\Framework\Controller\ResultFactory;
use Magento\Backend\App\Action\Context;
use Magento\Ui\Component\MassAction\Filter;
use Aion\Test\Model\ResourceModel\Test\CollectionFactory;
/**
* Class MassEnable
*/
class MassEnable extends \Magento\Backend\App\Action
{
/**
* @var Filter
*/
protected $filter;
/**
* @var CollectionFactory
*/
protected $collectionFactory;
/**
* @param Context $context
* @param Filter $filter
* @param CollectionFactory $collectionFactory
*/
public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
{
$this->filter = $filter;
$this->collectionFactory = $collectionFactory;
parent::__construct($context);
}
/**
* Execute action
*
* @return \Magento\Backend\Model\View\Result\Redirect
* @throws \Magento\Framework\Exception\LocalizedException|\Exception
*/
public function execute()
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
foreach ($collection as $item) {
$item->setIsActive(true);
$item->save();
}
$this->messageManager->addSuccess(__('A total of %1 record(s) have been enabled.', $collection->getSize()));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $resultRedirect->setPath('*/*/');
}
}
<?php
/**
* Copyright © 2016 AionNext Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Aion\Test\Controller\Adminhtml\Test;
use Magento\Framework\Controller\ResultFactory;
use Magento\Backend\App\Action\Context;
use Magento\Ui\Component\MassAction\Filter;
use Aion\Test\Model\ResourceModel\Test\CollectionFactory;
/**
* Class MassDisable
*/
class MassDisable extends \Magento\Backend\App\Action
{
/**
* @var Filter
*/
protected $filter;
/**
* @var CollectionFactory
*/
protected $collectionFactory;
/**
* @param Context $context
* @param Filter $filter
* @param CollectionFactory $collectionFactory
*/
public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
{
$this->filter = $filter;
$this->collectionFactory = $collectionFactory;
parent::__construct($context);
}
/**
* Execute action
*
* @return \Magento\Backend\Model\View\Result\Redirect
* @throws \Magento\Framework\Exception\LocalizedException|\Exception
*/
public function execute()
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
foreach ($collection as $item) {
$item->setIsActive(false);
$item->save();
}
$this->messageManager->addSuccess(__('A total of %1 record(s) have been disabled.', $collection->getSize()));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $resultRedirect->setPath('*/*/');
}
}
A két controller működése nagyon hasonló. Mindkettő a Filter osztálytól kapott collection-ön iterál végig, és állítja be az is_active data kulcsot massEnbale esetén true-ra, míg massDisable esetén false-ra, majd menti a collection elemeit.
5) Object manager konfigurációs
Ahhoz, hogy az elkészített admin táblázat(grid) megfelelően működjön, meg kell adnunk a forrás adat objektumokat és filter-eket. Ehhez szükségünk lesz egy definíciós xml-re. A fájl az app/code/Aion/Test/etc/ könyvtárban van elhelyezve di.xml néven. A fájl tartalma:
<?xml version="1.0"?>
<!--
/**
* Copyright © 2016 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:ObjectManager/etc/config.xsd">
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<arguments>
<argument name="collections" xsi:type="array">
<item name="test_test_listing_data_source" xsi:type="string">Aion\Test\Model\ResourceModel\Test\Grid\Collection</item>
</argument>
</arguments>
</type>
<type name="Aion\Test\Model\ResourceModel\Test\Grid\Collection">
<arguments>
<argument name="mainTable" xsi:type="string">aion_test</argument>
<argument name="eventPrefix" xsi:type="string">aion_test_grid_collection</argument>
<argument name="eventObject" xsi:type="string">test_grid_collection</argument>
<argument name="resourceModel" xsi:type="string">Aion\Test\Model\ResourceModel\Test</argument>
</arguments>
</type>
<virtualType name="TestGirdFilterPool" type="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool">
<arguments>
<argument name="appliers" xsi:type="array">
<item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item>
<item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item>
</argument>
</arguments>
</virtualType>
<virtualType name="TestGridDataProvider" type="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider">
<arguments>
<argument name="collection" xsi:type="object" shared="false">Aion\Test\Model\ResourceModel\Test\Collection</argument>
<argument name="filterPool" xsi:type="object" shared="false">TestGirdFilterPool</argument>
</arguments>
</virtualType>
</config>
Ebben a fájlban definiáljuk a grid-hez szükséges collection-t (lásd: <item name=”test_test_listing_data_source” xsi:type=”string”>Aion\Test\Model\ResourceModel\Test\Grid\Collection</item>), filter-t és data provider-t, mely UI component megfelelő működéséhez szükséges. Az egyes elemek editálását, mentését és egyenkénti törlését a következőkben írjuk le.
6) Editáláshoz szükséges admin block-ok létrehozása
Ahhoz, hogy a modulhoz tartozó adatokat létre tudjuk hozni az admin felületen és szerkeszteni tudjuk, szükségünk lesz a megfelelő osztályokra. Első lépésben a container osztályt kell létrehozni, mely később a form-ot fogja tartalmazni. Az osztályt az Aion/Test/Block/Adminhtml/ könyvtárban lévő Test.php fájlban valósítjuk meg:
<?php
/**
* Copyright © 2016 AionNext Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Aion\Test\Block\Adminhtml;
/**
* Adminhtml Aion items content block
*/
class Test extends \Magento\Backend\Block\Widget\Grid\Container
{
/**
* @return void
*/
protected function _construct()
{
$this->_blockGroup = 'Aion_Test';
$this->_controller = 'adminhtml_test';
$this->_headerText = __('Items');
$this->_addButtonLabel = __('Add New Item');
parent::_construct();
}
}
Az osztályban lényeges a megfelelő blockGroup és controller meghatározása. A következő lépésben szükségünk lesz a form container osztályra. Itt határozzuk meg szerkesztetés alatt álló objektum admin oldalának title-jét, és adhatunk hozzá tetszőleges button-okat az alap gombokon kívül, vagy távolíthatunk el. Az osztályt az Aion/Test/Block/Adminhtml/Test könyvtárban lévő Edit.php fájlban valósítjuk meg:
<?php
/**
* Copyright © 2016 AionNext Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Aion\Test\Block\Adminhtml\Test;
/**
* Aion item edit form container
*/
class Edit extends \Magento\Backend\Block\Widget\Form\Container
{
/**
* Core registry
*
* @var \Magento\Framework\Registry
*/
protected $_coreRegistry = null;
/**
* @param \Magento\Backend\Block\Widget\Context $context
* @param \Magento\Framework\Registry $registry
* @param array $data
*/
public function __construct(
\Magento\Backend\Block\Widget\Context $context,
\Magento\Framework\Registry $registry,
array $data = []
) {
$this->_coreRegistry = $registry;
parent::__construct($context, $data);
}
/**
* @return void
*/
protected function _construct()
{
$this->_objectId = 'test_id';
$this->_blockGroup = 'Aion_Test';
$this->_controller = 'adminhtml_test';
parent::_construct();
$this->buttonList->update('save', 'label', __('Save Item'));
$this->buttonList->update('delete', 'label', __('Delete Item'));
$this->buttonList->add(
'saveandcontinue',
[
'label' => __('Save and Continue Edit'),
'class' => 'save',
'data_attribute' => [
'mage-init' => ['button' => ['event' => 'saveAndContinueEdit', 'target' => '#edit_form']],
]
],
-100
);
}
/**
* Get edit form container header text
*
* @return \Magento\Framework\Phrase
*/
public function getHeaderText()
{
if ($this->_coreRegistry->registry('test_item')->getId()) {
return __("Edit Block '%1'", $this->escapeHtml($this->_coreRegistry->registry('test_item')->getName()));
} else {
return __('New Item');
}
}
}
Amennyiben WYSWYG editort is szeretnénk használni például textarea típusú mezőhöz, akkor azt a _construct() függvényben kell elhelyezni, vagy a prepareLayout() függvényben. Az osztályban lévő getHeaderText() függvény határozza meg az admin oldal title értékét. Az utolsó block, amit el kell készítenünk, a form megjelenítését és kezelését végzi. Az osztályt az Aion/Test/Block/Adminhtml/Test/Edit könyvtárban lévő Form.php fájlban valósítjuk meg:
<?php
/**
* Copyright © 2016 AionNext Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Aion\Test\Block\Adminhtml\Test\Edit;
/**
* Adminhtml Aion item edit form
*/
class Form extends \Magento\Backend\Block\Widget\Form\Generic
{
/**
* @var \Magento\Cms\Model\Wysiwyg\Config
*/
protected $_wysiwygConfig;
/**
* @var \Magento\Store\Model\System\Store
*/
protected $_systemStore;
/**
* @param \Magento\Backend\Block\Template\Context $context
* @param \Magento\Framework\Registry $registry
* @param \Magento\Framework\Data\FormFactory $formFactory
* @param \Magento\Cms\Model\Wysiwyg\Config $wysiwygConfig
* @param \Magento\Store\Model\System\Store $systemStore
* @param array $data
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Framework\Data\FormFactory $formFactory,
\Magento\Cms\Model\Wysiwyg\Config $wysiwygConfig,
\Magento\Store\Model\System\Store $systemStore,
array $data = []
) {
$this->_wysiwygConfig = $wysiwygConfig;
$this->_systemStore = $systemStore;
parent::__construct($context, $registry, $formFactory, $data);
}
/**
* Init form
*
* @return void
*/
protected function _construct()
{
parent::_construct();
$this->setId('test_form');
$this->setTitle(__('Item Information'));
}
/**
* Prepare form
*
* @return $this
*/
protected function _prepareForm()
{
$model = $this->_coreRegistry->registry('test_item');
/** @var \Magento\Framework\Data\Form $form */
$form = $this->_formFactory->create(
['data' => ['id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post']]
);
$form->setHtmlIdPrefix('item_');
$fieldset = $form->addFieldset(
'base_fieldset',
['legend' => __('General Information'), 'class' => 'fieldset-wide']
);
if ($model->getId()) {
$fieldset->addField('test_id', 'hidden', ['name' => 'test_id']);
}
$fieldset->addField(
'name',
'text',
[
'name' => 'name',
'label' => __('Name'),
'title' => __('Name'),
'required' => true
]
);
$fieldset->addField(
'email',
'text',
[
'name' => 'email',
'label' => __('Email'),
'title' => __('Email'),
'required' => true,
'class' => 'validate-email'
]
);
$fieldset->addField(
'is_active',
'select',
[
'label' => __('Status'),
'title' => __('Status'),
'name' => 'is_active',
'required' => true,
'options' => ['1' => __('Enabled'), '0' => __('Disabled')]
]
);
if (!$model->getId()) {
$model->setData('is_active', '1');
}
$fieldset->addField(
'sort_order',
'text',
[
'name' => 'sort_order',
'label' => __('Sort Order'),
'title' => __('Sort Order'),
'required' => false
]
);
$form->setValues($model->getData());
$form->setUseContainer(true);
$this->setForm($form);
return parent::_prepareForm();
}
}
Az osztály _prepareForm() függvényében adjuk hozzá a szerkesztésre szánt mezőket, ami a mi esetünkben a name, email és sort_order mezők. Ezek mellett szerepel még a multistore kezelés szempontjából fontos store_id field is, illetve is_active field is, ami jelen esetben select típusú és a szerkesztés alatt álló elem státuszát hivatott beállítani. A fent említett három osztállyal el is készítettük az adminisztrációs felületen történő szerkesztéshez szükséges fájlokat.
7) Controller-ek és layout létrehozása
Az editálás megvalósításához a fenti osztályokon kívül szükségünk lesz még a megfelelő controller osztályokra és layout fájlokra. Az első osztályt az Aion/Test/Controller/Adminhtml/Test/ könyvtárban lévő NewAction.php fájlban valósítjuk meg:
<?php
/**
* Copyright © 2016 AionNext Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Aion\Test\Controller\Adminhtml\Test;
class NewAction extends \Aion\Test\Controller\Adminhtml\Test
{
/**
* @var \Magento\Backend\Model\View\Result\ForwardFactory
*/
protected $resultForwardFactory;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
* @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry,
\Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory
) {
$this->resultForwardFactory = $resultForwardFactory;
parent::__construct($context, $coreRegistry);
}
/**
* Create new item
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
/** @var \Magento\Framework\Controller\Result\Forward $resultForward */
$resultForward = $this->resultForwardFactory->create();
return $resultForward->forward('edit');
}
}
Az osztály az új elemek létrehozására szolgál és lényegében az action függvénye (execute()) átirányít az Edit controller osztályra. A következő lépésben létrehozzuk a szerkesztéshez szükséges controller-t. Az osztályt az Aion/Test/Controller/Adminhtml/Test/ könyvtárban lévő Edit.php fájlban valósítjuk meg:
<?php
/**
* Copyright © 2016 AionNext Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Aion\Test\Controller\Adminhtml\Test;
class Edit extends \Aion\Test\Controller\Adminhtml\Test
{
/**
* @var \Magento\Framework\View\Result\PageFactory
*/
protected $resultPageFactory;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
* @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context, $coreRegistry);
}
/**
* Edit item
*
* @return \Magento\Framework\Controller\ResultInterface
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
public function execute()
{
// 1. Get ID and create model
$id = $this->getRequest()->getParam('test_id');
$model = $this->_objectManager->create('Aion\Test\Model\Test');
// 2. Initial checking
if ($id) {
$model->load($id);
if (!$model->getId()) {
$this->messageManager->addError(__('This item no longer exists.'));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
return $resultRedirect->setPath('*/*/');
}
}
// 3. Set entered data if was error when we do save
$data = $this->_objectManager->get('Magento\Backend\Model\Session')->getFormData(true);
if (!empty($data)) {
$model->setData($data);
}
// 4. Register model to use later in blocks
$this->_coreRegistry->register('test_item', $model);
/** @var \Magento\Backend\Model\View\Result\Page $resultPage */
$resultPage = $this->resultPageFactory->create();
// 5. Build edit form
$this->initPage($resultPage)->addBreadcrumb(
$id ? __('Edit Item') : __('New Item'),
$id ? __('Edit Item') : __('New Item')
);
$resultPage->getConfig()->getTitle()->prepend(__('Items'));
$resultPage->getConfig()->getTitle()->prepend($model->getId() ? $model->getName() : __('New Item'));
return $resultPage;
}
}
Az edit action(execute() függvény) első lépésben lekéri a test_id paramétert. Ezt követően inicializálja az Aion/Test/Model/Test modell osztályt. Amennyiben a test_id paraméternek van értéke, a modellt megpróbálja betöltelni az említett id-val. Sikertelen esetben hibaüzenet állít be, majd visszairányít. Ellenkező esetben a betöltött modellt a registry-ben tárolja ($this->_coreRegistry->register(’test_item’, $model)). Ezt olvassa ki a registry-ből a fent már említett form container osztály is, és használja fel. Végezetül létrehozza az oldalt ($resultPage), majd beállítja az oldal title paraméterét és breadcrumb-ot is. A controller-hez tartozó layout fájlt az Aion/Test/view/adminhtml/layout/ könyvtárban lévő test_test_edit.xml fájlban valósítjuk meg:
<?xml version="1.0"?>
<!--
/**
* Copyright © 2016 AionNext Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<update handle="editor"/>
<body>
<referenceContainer name="content">
<block class="Aion\Test\Block\Adminhtml\Test\Edit" name="test_test_edit"/>
</referenceContainer>
</body>
</page>
A következő lépés a mentés elkészítése. Az osztályt az Aion/Test/Controller/Adminhtml/Test/ könyvtárban lévő Save.php fájlban valósítjuk meg:
<?php
/**
* Copyright © 2016 AionNext Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Aion\Test\Controller\Adminhtml\Test;
class Save extends \Aion\Test\Controller\Adminhtml\Test
{
/**
* Save action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
// check if data sent
$data = $this->getRequest()->getPostValue();
if ($data) {
$id = $this->getRequest()->getParam('test_id');
$model = $this->_objectManager->create('Aion\Test\Model\Test')->load($id);
if (!$model->getId() && $id) {
$this->messageManager->addError(__('This item no longer exists.'));
return $resultRedirect->setPath('*/*/');
}
// init model and set data
$model->setData($data);
// try to save it
try {
// save the data
$model->save();
// display success message
$this->messageManager->addSuccess(__('You saved the item.'));
// clear previously saved data from session
$this->_objectManager->get('Magento\Backend\Model\Session')->setFormData(false);
// check if 'Save and Continue'
if ($this->getRequest()->getParam('back')) {
return $resultRedirect->setPath('*/*/edit', ['test_id' => $model->getId()]);
}
// go to grid
return $resultRedirect->setPath('*/*/');
} catch (\Exception $e) {
// display error message
$this->messageManager->addError($e->getMessage());
// save data in session
$this->_objectManager->get('Magento\Backend\Model\Session')->setFormData($data);
// redirect to edit form
return $resultRedirect->setPath('*/*/edit', ['test_id' => $this->getRequest()->getParam('test_id')]);
}
}
return $resultRedirect->setPath('*/*/');
}
}
A controller osztály első lépésben várja a korábban kialakított form által posztolt adatokat ($data = $this->getRequest()->getPostValue();). Amennyiben ez nem egy üres tömb, inicializálja az Aion/Test/Model/Test modell osztályt és ha létezik a paraméterként kapott test_id is (vagyis nem új objektum mentésére kerül sor), akkor betölti a megfelelő id-val. Ezt követően a post-ban kapott adatokat beállítja majd menti a modellt. Amennyiben mindezzel elkészültünk, akkor a korábban kialakított admin grid-ből (táblázat) új objektumokat tudunk hozzáadni és menteni, majd ezeket szerkeszteni. Még egy fontos controller van hátra, ami a törlést hivatott megvalósítani. Az osztályt az Aion/Test/Controller/Adminhtml/Test/ könyvtárban lévő Delete.php fájlban valósítjuk meg:
<?php
/**
* Copyright © 2016 AionNext Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Aion\Test\Controller\Adminhtml\Test;
class Delete extends \Aion\Test\Controller\Adminhtml\Test
{
/**
* Delete action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
// check if we know what should be deleted
$id = $this->getRequest()->getParam('test_id');
if ($id) {
try {
// init model and delete
$model = $this->_objectManager->create('Aion\Test\Model\Test');
$model->load($id);
$model->delete();
// display success message
$this->messageManager->addSuccess(__('You deleted the item.'));
// go to grid
return $resultRedirect->setPath('*/*/');
} catch (\Exception $e) {
// display error message
$this->messageManager->addError($e->getMessage());
// go back to edit form
return $resultRedirect->setPath('*/*/edit', ['test_id' => $id]);
}
}
// display error message
$this->messageManager->addError(__('We can\'t find the item to delete.'));
// go to grid
return $resultRedirect->setPath('*/*/');
}
}
A delete action (execute() függvény) első lépésben lekéri a test_id paramétert. Ezt követően inicializálja az Aion/Test/Model/Test model osztályt. Amennyiben a test_id paraméternek van értéke, a modellt megpróbálja betölteni az említett id-val, majd elvégzi a törlést. Bízom benne, hogy ebben a kétrészes átfogó cikkben sikerült átadnom mindazt a tudnivalót, mellyel sikeresen hozhatsz létre te is egy saját modult a Magento 2 rendszerben, és állíthatsz be, illetve szerkeszthetsz különböző elemeket, fájlokat hozzá, mint pl. adatbázis tábla, modell, collection, block, admin táblázat, layout stb. A cikk első része itt olvasható: Magento 2 modul fejlesztés lépésről lépésre – 1. rész