Tworzenie formularza w adminie za pomocą komponentu UI w Magento 2
W tej notce omówimy tworzenie formularza w adminie za pomocą komponentu UI w Magento 2.
W poprzednim wpisie został utworzony moduł Anna_GuestbookAdminUI, w którym dodano stronę w adminie wyświetlającą grid. Na początek dodamy elementy do istniejącej konfiguracji komponentu UI, aby mieć możliwość zarówno dodania nowego jak i edycji istniejącego wpisu. Następnie przejdziemy do głównego tematu, czyli stworzenia strony z formularzem i jego obsługą.
Modyfikacja listingu grida
Pierwszą rzeczą, jaką zrobimy, to dodanie przycisku „Add new entry” nad gridem oraz dodamy kolumnę akcyjną, która będzie zawierała link do edycji danego wpisu.
Dodanie przycisku akcji „Add new entry” na listingu grida
Dodanie przycisku we wskazane miejsce, możemy dokonać, dodając do głównego węzła <settings>, konfigurację z elementem <buttons>:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<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">guestbook_listing.guestbook_listing_data_source</item> </item> </argument> <settings> <spinner>guestbook_columns</spinner> <buttons> <button name="add"> <url path="*/*/new"/> <class>primary</class> <label translate="true">Add new entry</label> </button> </buttons> <deps> <dep>guestbook_listing.guestbook_listing_data_source</dep> </deps> </settings> <!-- ... --> </listing> |
Uwzględniając konfigurację modułu Anna_GuestbookAdminUI, ścieżka */*/new zostanie zmapowana do guestbook/index/new. Tym samym, będziemy potrzebowali utworzyć później klasę akcji w pliku Controller/Adminhtml/Index/NewAction.php.
Dodanie kolumny akcji
Link do formularza edycyjnego będzie miał postać guestbook/index/edit/id/1. Na początek do listingu grida, dodamy nową kolumnę i zdefiniujemy klasę odpowiedzialną za generowanie linków.
W pliku guestbook_listing.xml dodajmy kolumnę akcyjną:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
<?xml version="1.0" encoding="UTF-8"?> <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">guestbook_listing.guestbook_listing_data_source</item> </item> </argument> <settings> <spinner>guestbook_columns</spinner> <buttons> <button name="add"> <url path="*/*/new"/> <class>primary</class> <label translate="true">Add new entry</label> </button> </buttons> <deps> <dep>guestbook_listing.guestbook_listing_data_source</dep> </deps> </settings> <dataSource name="guestbook_listing_data_source" component="Magento_Ui/js/grid/provider"> <settings> <storageConfig> <param name="indexField" xsi:type="string">entry_id</param> </storageConfig> <updateUrl path="mui/index/render"/> </settings> <aclResource>Anna_GuestbookAdminUI::manage</aclResource> <dataProvider class="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider" name="guestbook_listing_data_source"> <settings> <requestFieldName>id</requestFieldName> <primaryFieldName>entry_id</primaryFieldName> </settings> </dataProvider> </dataSource> <listingToolbar name="listing_top"> <settings> <sticky>true</sticky> </settings> <bookmark name="bookmarks"/> <columnsControls name="columns_controls"/> <filters name="listing_filters"/> <paging name="listing_paging"/> </listingToolbar> <columns name="guestbook_columns" class="Magento\Ui\Component\Listing\Columns"> <!-- ... --> <column name="created_at" sortOrder="60"> <settings> <filter>text</filter> <label translate="true">Created At</label> </settings> </column> <actionsColumn name="actions" class="Anna\GuestbookAdminUI\Ui\Component\Listing\Column\Actions"> <settings> <indexField>entry_id</indexField> </settings> </actionsColumn> </columns> </listing> |
Kolejnym korkiem jest utworzenie klasy, która zwróci nam dane do wyświetlenia linków. Poniżej kod dla klasy Anna\GuestbookAdminUI\Ui\Component\Listing\Column\Actions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<?php declare(strict_types=1); namespace Anna\GuestbookAdminUI\Ui\Component\Listing\Column; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Component\Listing\Columns\Column; use Magento\Framework\UrlInterface; class Actions extends Column { /** * @var UrlInterface */ protected $urlBuilder; /** * @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 */ public function prepareDataSource(array $dataSource): array { if (isset($dataSource['data']['items'])) { foreach ($dataSource['data']['items'] as &$item) { $item[$this->getData('name')]['edit'] = [ 'href' => $this->urlBuilder->getUrl( 'guestbook/*/edit', ['entry_id' => $item['entry_id']] ), 'label' => __('Edit'), 'hidden' => false, ]; } } return $dataSource; } } |
Stworzenie i wyświetlenie formularza
Ponownie jak w przypadku grida, potrzebujemy mieć do wyświetlenia stworzyć odpowiednią konfigurację w plikach XML. Potrzebujemy stworzyć plik layoutu do akcji wyświetlającej formularz, konfigurację samego formularza UI, klasę DataProvider przekazującą dane do formularza oraz obsłużmy poszczególne akcje CRUD.
Stworzenie klasy akcji NewAction
Pora na stworzenie klas akcji. Akcja new będzie przekazywała dalszą obsługę do akcji edit, która będzie odpowiadać za wyświetlenie formularza, również dla nowych wpisów. Nie możemy stworzyć klasy z nazwą ’New’, więc w nazwie obowiązkowo musimy dodać na końcu ’Action’. Ostatecznie powstaje klasa Anna\GuestbookAdminUI\Controller\Adminhtml\Index\NewAction:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<?php declare(strict_types=1); namespace Anna\GuestbookAdminUI\Controller\Adminhtml\Index; use Magento\Backend\App\Action; use Magento\Backend\Model\View\Result\ForwardFactory; use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Controller\ResultInterface; class NewAction extends Action implements HttpGetActionInterface { /** * Authorization level of a basic admin session * * @see _isAllowed() */ const ADMIN_RESOURCE = 'Anna_GuestbookAdminUI::manage'; /** * @var ForwardFactory */ protected $resultForwardFactory; /** * @param Action\Context $context * @param ForwardFactory $resultForwardFactory */ public function __construct( Action\Context $context, ForwardFactory $resultForwardFactory, ) { parent::__construct($context); $this->resultForwardFactory = $resultForwardFactory; } /** * @return ResponseInterface|ResultInterface */ public function execute() { $resultForward = $this->resultForwardFactory->create(); $resultForward->forward('edit'); return $resultForward; } } |
Stworzenie pliku layoutu dla akcji Edit
Na początek plik layoutu, do akcji guestbook/index/edit, której zadaniem będzie wyświetlenie formularza. W tym celu tworzymy plik guesbook_index_edit.xml, w podkatalogu modułu view/adminhtml/layout, o następującej zawartości:
1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <update handle="formkey"/> <body> <referenceContainer name="content"> <uiComponent name="guestbook_form" /> </referenceContainer> </body> </page> |
Następnym krokiem będzie zdefiniowanie konfiguracji komponentu UI dla formularza.
Konfiguracja komponentu UI formularza
Potrzebujemy teraz stworzyć plik XML o tej samej nazwie. Plik guestbook_form.xml tworzymy w katalogu Anna/GuestbookAdminUI/view/adminhtml/ui_component/guestbook_form.xml.
Zanim zapodam właściwy xml, daję taki ogólny. Komponenty formularza zwykle są tworzone w pliku o nazwie *_form.xml i mają ogólnie taką postać:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<?xml version="1.0" encoding="UTF-8"?> <form 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">*_form.*_form_data_source</item> </item> <item name="template" xsi:type="string">templates/form/collapsible</item> </argument> <settings> <buttons> <!-- ... --> </buttons> <namespace>*_form</namespace> <dataScope>data</dataScope> <deps> <dep>*_form.*_form_data_source</dep> </deps> </settings> <dataSource name="*_form_data_source"> <argument name="data" xsi:type="array"> <item name="js_config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item> </item> </argument> <settings> <!-- form submit url: --> <submitUrl path="*/*/save"/> </settings> <dataProvider class="Vendor\Module\Ui\Component\Form\DataProvider" name="*_form_data_source"> <settings> <requestFieldName>id</requestFieldName> <primaryFieldName>id</primaryFieldName> </settings> </dataProvider> </dataSource> <fieldset name="fieldsetname1"> <!-- form fields ... --> </fieldset> <fieldset name="fieldsetname2"> <!-- form fields ... --> </fieldset> </form> |
Gdzie *_form to oczywiście nazwa naszego komponentu.
Przejdźmy teraz do właściwego pliku komponentu guestbook_form.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
<?xml version="1.0" encoding="UTF-8"?> <form 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">guestbook_form.guestbook_form_data_source</item> </item> <item name="template" xsi:type="string">templates/form/collapsible</item> </argument> <settings> <buttons> <button name="save"> <class>save primary</class> <label translate="true">Save Entry</label> </button> <button name="back"> <url path="*/*/"/> <class>back</class> <label translate="true">Back</label> </button> <button name="delete" class="Magento\Cms\Block\Adminhtml\Page\Edit\DeleteButton"/> </buttons> <namespace>guestbook_form</namespace> <dataScope>data</dataScope> <deps> <dep>guestbook_form.guestbook_form_data_source</dep> </deps> </settings> <dataSource name="guestbook_form_data_source"> <argument name="data" xsi:type="array"> <item name="js_config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item> </item> </argument> <settings> <submitUrl path="*/*/save"/> </settings> <dataProvider class="Anna\GuestbookAdminUI\Ui\Component\Form\DataProvider" name="guestbook_form_data_source"> <settings> <requestFieldName>entry_id</requestFieldName> <primaryFieldName>entry_id</primaryFieldName> </settings> </dataProvider> </dataSource> <fieldset name="general"> <settings> <label translate="true">General Information</label> </settings> <field name="user_name" sortOrder="10" formElement="input"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="source" xsi:type="string">user_name</item> </item> </argument> <settings> <validation> <rule name="required-entry" xsi:type="boolean">true</rule> </validation> <dataType>text</dataType> <label translate="true">User name</label> <dataScope>user_name</dataScope> </settings> </field> <field name="subject" sortOrder="20" formElement="input"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="source" xsi:type="string">subject</item> </item> </argument> <settings> <validation> <rule name="required-entry" xsi:type="boolean">true</rule> </validation> <dataType>text</dataType> <label translate="true">Subject</label> <dataScope>subject</dataScope> </settings> </field> <field name="content" formElement="textarea"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="source" xsi:type="string">content</item> </item> </argument> <settings> <validation> <rule name="required-entry" xsi:type="boolean">true</rule> </validation> <dataType>text</dataType> <label translate="true">Content</label> <dataScope>content</dataScope> </settings> </field> </fieldset> </form> |
W elemencie <buttons> mamy listę skonfigurowanych akcji takich jak usuwanie, dodawanie czy powrót do poprzedniej strony, które szerzej zaraz omówię.
Listę poszczególnych pól formularza umieszcza się w komponentach Fieldset. Podany przykład zawiera również prostą walidację — sprawdza, czy określone pola są wypełnione.
Klasa Providera dla formularza
Jeśli w linku do formularza jest podany identyfikator, będziemy chcieli pobrać dane do formularza. Zadanie wykona klasa Anna\GuestbookAdminUI\Ui\Component\Form\DataProvider, którą określiliśmy w konfiguracji guestbook_form.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
<?php declare(strict_types=1); namespace Anna\GuestbookAdminUI\Ui\Component\Form; use Anna\Guestbook\Model\ResourceModel\Guestbook\Collection; use Anna\Guestbook\Model\ResourceModel\Guestbook\CollectionFactory; use Anna\Guestbook\Model\ResourceModel\Guestbook as GuestbookResourceModel; use Anna\Guestbook\Model\Guestbook; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Ui\DataProvider\AbstractDataProvider; class DataProvider extends AbstractDataProvider { /** * @var Collection */ protected $collection; /** * @var array */ protected $loadedData; /** * @var GuestbookResourceModel */ protected $guestbookResourceModel; /** * @var DataPersistorInterface */ protected $dataPersistor; /** * @param string $name * @param string $primaryFieldName * @param string $requestFieldName * @param CollectionFactory $collectionFactory * @param GuestbookResourceModel $guestbookResourceModel * @param DataPersistorInterface $dataPersistor * @param array $meta * @param array $data */ public function __construct( string $name, string $primaryFieldName, string $requestFieldName, CollectionFactory $collectionFactory, GuestbookResourceModel $guestbookResourceModel, DataPersistorInterface $dataPersistor, array $meta = [], array $data = [] ) { $this->collection = $collectionFactory->create(); $this->guestbookResourceModel = $guestbookResourceModel; $this->dataPersistor = $dataPersistor; parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); } /** * @return array */ public function getData(): array { if (isset($this->loadedData)) { return $this->loadedData; } $this->loadedData = []; $items = $this->collection->getItems(); /** @var Guestbook $entry */ foreach ($items as $entry) { $this->guestbookResourceModel->load($entry, $entry->getId()); $this->loadedData[$entry->getId()] = $entry->getData(); } $data = $this->dataPersistor->get('guestbook_entry'); if (!empty($data)) { $entry = $this->collection->getNewEmptyItem(); $entry->setData($data); $this->loadedData[$entry->getId()] = $entry->getData(); $this->dataPersistor->clear('guestbook_entry'); } return $this->loadedData; } } |
Klasa DataProvider zajmuje się pobraniem danych. Pozostaje nam jeszcze pokazać ten formularz, więc czas na stworzenie klasy akcji Edit. Na początek przyjrzyjmy się samej konfiguracji linków do poszczególnych przycisków na formularzu.
Konfiguracja przycisków akcji formularza
Żeby dodać przycisk zapisu i określić do niego url, umieszczono taką konfigurację w guestbook_form.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?xml version="1.0" encoding="UTF-8"?> <form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> <!-- ... --> <settings> <buttons> <button name="save"> <class>save primary</class> <label translate="true">Save Entry</label> </button> <button name="back"> <url path="*/*/"/> <class>back</class> <label translate="true">Back</label> </button> <button name="delete" class="Anna\GuestbookAdminUI\Block\Adminhtml\Guestbook\Edit\DeleteButton"/> </buttons> <!-- ... --> </settings> <dataSource name="guestbook_form_data_source"> <argument name="data" xsi:type="array"> <item name="js_config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item> </item> </argument> <settings> <submitUrl path="*/*/save"/> </settings> <!-- ... --> </dataSource> <!-- ... --> </form> |
W elemencie możemy określić nasz własny url do akcji mającej zapisywać wysłane dane z formularza.
Jak najbardziej możemy stworzyć też taką konfigurację przycisków za pomocą klasy. Możemy zerknąć do modułu Magento_Customer na klasę Magento\Customer\Block\Adminhtml\Edit\SaveButton, której użycie jest skonfigurowane w pliku view/adminhtml/ui_component/cms_page_form.xml.
Podobnie mamy zrobioną konfigurację dla przycisku wróć:
1 2 3 4 5 |
<button name="back"> <url path="*/*/"/> <class>back</class> <label translate="true">Back</label> </button> |
Nie ma tu wyszukanej logiki. Podanie ścieżki, klasy css dla tego przycisku oraz zdefiniowanie etykiety do wyświetlanej nazwy — wystarczy.
Możemy dla przykładu sprawdzić moduł Magento_Cms i zobaczyć jak wygląda implementacja przycisku Back, który jest zawarty w kodzie klasy Magento\Cms\Block\Adminhtml\Page\Edit\BackButton.
Kolejna akcja, którą zaraz też zaimplementujemy to usuwanie, czyli Delete. Obsługa przycisku wymaga wygenerowania odpowiedniego linku, więc wymagało to już stworzenia osobnej klasy:
1 |
<button name="delete" class="Anna\GuestbookAdminUI\Block\Adminhtml\Guestbook\Edit\DeleteButton"/> |
Tak jak w module Magento_Customer, na początek stworzymy klasę typu Generic, która będzie zawierała podstawowe metody, które będziemy używać w konkretnej klasie przycisku:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<?php declare(strict_types=1); namespace Anna\GuestbookAdminUI\Block\Adminhtml\Guestbook\Edit; use Anna\Guestbook\Api\GuestbookRepositoryInterface; use Magento\Backend\Block\Template\Context; use Magento\Framework\Exception\NoSuchEntityException; class GenericButton { /** * @var Context */ protected $context; /** * @var GuestbookRepositoryInterface */ protected $guestbookRepository; /** * @param Context $context * @param GuestbookRepositoryInterface $guestbookRepository */ public function __construct( Context $context, GuestbookRepositoryInterface $guestbookRepository ) { $this->context = $context; $this->guestbookRepository = $guestbookRepository; } /** * @return int|null */ public function getEntryId(): ?int { try { return $this->guestbookRepository->getById( (int)$this->context->getRequest()->getParam('entry_id') )->getId(); } catch (NoSuchEntityException $e) { } return null; } /** * @param string $route * @param array $params * @return string */ public function getUrl(string $route = '', array $params = []): string { return $this->context->getUrlBuilder()->getUrl($route, $params); } } |
Dodaliśmy dwie metody, jedna która zwraca nam id oraz metodę pomocniczą do generowania linków.
Każda klasa przycisku, powinna implementować interfejs Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ declare(strict_types=1); namespace Magento\Framework\View\Element\UiComponent\Control; /** * Interface ButtonProviderInterface * * @api */ interface ButtonProviderInterface { /** * Retrieve button-specified settings * * @return array */ public function getButtonData(); } |
Implementacja przycisku do usuwania wpisu, wygląda ostatecznie tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<?php declare(strict_types=1); namespace Anna\GuestbookAdminUI\Block\Adminhtml\Guestbook\Edit; use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface; class DeleteButton extends GenericButton implements ButtonProviderInterface { /** * {@inheritdoc} */ public function getButtonData(): array { $data = []; if ($this->getEntryId()) { $data = [ 'label' => __('Delete Entry'), 'class' => 'delete', 'on_click' => 'deleteConfirm(\'' . __( 'Are you sure you want to do this?' ) . '\', \'' . $this->getDeleteUrl() . '\', {"data": {}})', 'sort_order' => 20, ]; } return $data; } /** * @return string */ public function getDeleteUrl(): string { return $this->getUrl('*/*/delete', ['entry_id' => $this->getEntryId()]); } } |
Klasa akcji Edit
Klasa akcji Edit jest odpowiedzialna za wyświetlenie naszego formularza. Dla określonego tu wcześniej pliku layoutu guesbook_index_edit.xml potrzebujemy stworzyć klasę akcji Anna/GuestbookAdminUI/Controller/Adminhtml/Index/Edit:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
<?php declare(strict_types=1); namespace Anna\GuestbookAdminUI\Controller\Adminhtml\Index; use Anna\Guestbook\Api\GuestbookRepositoryInterface; use Magento\Backend\App\Action; use Magento\Backend\Model\View\Result\Redirect; use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\View\Result\PageFactory; class Edit extends Action implements HttpGetActionInterface { /** * Authorization level of a basic admin session * * @see _isAllowed() */ const ADMIN_RESOURCE = 'Anna_GuestbookAdminUI::manage'; /** * @var GuestbookRepositoryInterface */ protected $guestbookRepository; /** * @var PageFactory */ protected $resultPageFactory; /** * @param Action\Context $context * @param PageFactory $resultPageFactory * @param GuestbookRepositoryInterface $guestbookRepository */ public function __construct( Action\Context $context, PageFactory $resultPageFactory, GuestbookRepositoryInterface $guestbookRepository ) { parent::__construct($context); $this->resultPageFactory = $resultPageFactory; $this->guestbookRepository= $guestbookRepository; } /** * @return ResultInterface */ public function execute() { /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); $id = (int)$this->getRequest()->getParam('entry_id'); if ($id) { try { $this->guestbookRepository->getById($id); } catch (NoSuchEntityException $e) { $this->messageManager->addErrorMessage(__('This guestbook entry no longer exists.')); return $resultRedirect->setPath('*/*/'); } } $resultPage = $this->resultPageFactory->create(); $resultPage->setActiveMenu('Anna_GuestbookAdminUI::guestbook'); $resultPage->getConfig()->getTitle()->prepend(__('Guestbook')); return $resultPage; } } |
Jeśli w linku mamy parametr entry_id, to dodatkowo, za pomocą klasy repozytorium sprawdzamy, czy wpis istnieje. Jeśli nie ma błędu, renderujemy stronę w przeciwnym wypadku przekierowujemy na poprzednią stronę, tutaj do naszego grida.
Klasa akcji Save
Pora na obsługę akcji zapisywania wpisu do księgi gości, poniżej implementacja klasy Anna\GuestbookAdminUI\Controller\Adminhtml\Index\Save:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
<?php declare(strict_types=1); namespace Anna\GuestbookAdminUI\Controller\Adminhtml\Index; use Anna\Guestbook\Api\Data\GuestbookInterfaceFactory; use Anna\Guestbook\Api\GuestbookRepositoryInterface; use Magento\Backend\App\Action; use Magento\Backend\App\Action\Context; use Magento\Backend\Model\View\Result\Redirect; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; class Save extends Action implements HttpPostActionInterface { /** * @var GuestbookInterfaceFactory */ protected $guestbookFactory; /** * @var GuestbookRepositoryInterface */ protected $guestbookRepository; /** * @var DataPersistorInterface */ protected $dataPersistor; /** * @param Context $context * @param GuestbookInterfaceFactory $guestbookFactory * @param GuestbookRepositoryInterface $guestbookRepository * @param DataPersistorInterface $dataPersistor */ public function __construct( Context $context, GuestbookInterfaceFactory $guestbookFactory, GuestbookRepositoryInterface $guestbookRepository, DataPersistorInterface $dataPersistor ) { parent::__construct($context); $this->guestbookFactory = $guestbookFactory; $this->guestbookRepository = $guestbookRepository; $this->dataPersistor = $dataPersistor; } /** * @return Redirect|ResultInterface|ResponseInterface */ public function execute(): Redirect|ResultInterface|ResponseInterface { $data = $this->getRequest()->getPostValue(); /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); if ($data) { $model = $this->guestbookFactory->create(); $id = (int)$this->getRequest()->getParam('entry_id'); if ($id) { try { $model = $this->guestbookRepository->getById($id); } catch (NoSuchEntityException $e) { $this->messageManager->addErrorMessage(__('This guestbook entry no longer exists.')); return $resultRedirect->setPath('*/*/'); } } $model->setData($data); try { $this->guestbookRepository->save($model); $this->dataPersistor->clear('guestbook_entry'); $this->messageManager->addSuccessMessage(__('You saved the entry.')); return $resultRedirect->setPath('*/*/'); } catch (LocalizedException $e) { $this->messageManager->addExceptionMessage($e->getPrevious() ?: $e); } catch (\Throwable $e) { $this->messageManager->addErrorMessage(__('Something went wrong while saving the page.')); } $this->dataPersistor->set('guestbook_entry', $data); return $resultRedirect->setPath('*/*/edit', ['entry_id' => $this->getRequest()->getParam('entry_id')]); } return $resultRedirect->setPath('*/*/'); } } |
Obiekt w zmiennej klasowej $dataPersitor ma za zadanie przechowywania danych w cache’u. Obiekt modelu tworzymy za pomocą odpowiedniej klasy fabryki i korzystamy z klasy repozytorium, aby wykonać odpowiednie zapytania.
Klasa akcji Delete
Ostatnią akcją jest usuwanie. Implementacja może wyglądać tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
<?php declare(strict_types=1); namespace Anna\GuestbookAdminUI\Controller\Adminhtml\Index; use Anna\Guestbook\Api\GuestbookRepositoryInterface; use Magento\Backend\App\Action; use Magento\Backend\App\Action\Context; use Magento\Backend\Model\View\Result\Redirect; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Exception\LocalizedException; class Delete extends Action implements HttpPostActionInterface { /** * @var GuestbookRepositoryInterface */ protected $guestbookRepository; /** * @param Context $context * @param GuestbookRepositoryInterface $guestbookRepository */ public function __construct( Context $context, GuestbookRepositoryInterface $guestbookRepository ) { parent::__construct($context); $this->guestbookRepository = $guestbookRepository; } /** * @return Redirect */ public function execute(): Redirect { /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); $id = (int)$this->getRequest()->getParam('entry_id'); if ($id) { try { $this->guestbookRepository->deleteById($id); $this->messageManager->addSuccessMessage(__('You deleted the guestbook entry.')); } catch (LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); return $resultRedirect->setPath('guestbook/*/edit', ['entry_id' => $id]); } catch (\Exception $e) { $this->messageManager->addErrorMessage( __('Something went wrong while trying to delete the guestbook entry.') ); return $resultRedirect->setPath('guestbook/*/edit', ['_current' => true]); } } return $resultRedirect->setPath('*/*/'); } } |