Автоматизация затрагивает всё: расчет стоимости доставки, создание и отправка накладной, получение PDF этикетки, отслеживание статусов и многое другое.
Выводить всю информацию (кнопки, статусы, формы отправки) на базовом виде заказа - не удобно, можно вынести всё в дополнительную вкладку.
Пример вкладки для Magento 1:
Опираясь на опыт с Magento 1, сделаем аналогичный функционал для Magento 2.
Создание модуля
Создадим базу модуля, подключив его через composer к нашему магазину. В нашем примере — модуль разрабатывается как готовый пакет (package) для Magento 2. В тег name вписывается название пакета, по которому модуль будет искаться в репозиториях. M2 работает с autoload (PSR-4), что упрощает работу с различными внешними модулями.
composer.json
{ "name": "vendor/module", "type": "magento2-module", "license": "proprietary", "homepage": "http://www.example.ru/magento2-module.html", "description": "Модуль доставки", "keywords": [ "magento", "module", "delivery", "vendor" ], "authors": [ { "name": "Vendor", "email": "mail@example.net", "homepage": "http://www.vendor.ru/" } ], "require": { "php": "~5.5.0|~5.6.0|~7.0.0", "magento/framework": "*" }, "repositories": [ { "type": "composer", "url": "https://repo.magento.com" } ], "autoload": { "files": [ "registration.php" ], "psr-4": { "Vendor\\Module\\": "" } } }
Для работы модуля нужно задать его зарегистрировать в системе как компонент.
registration.php
<?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::Module, 'Vendor_Module', __DIR__ );
и etc/module.xml
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Vendor_Module" setup_version="2.0.0"> </module> </config>
Вторым шагом добавляем layout
Добавляем блок в нужную секцию layout.
view/adminhtml/layout/sales_order_view.xml
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="left"> <referenceBlock name="sales_order_tabs"> <action method="addTab"> <argument name="name" xsi:type="string">Vendor Module</argument> <argument name="block" xsi:type="string">Vendor\Module\Block\Adminhtml\Order\View\Tab\Module</argument> </action> </referenceBlock> </referenceContainer> </body> </page>
В теге argument мы передаем название блока, который будет подключен в качестве дополнительной вкладки на странице заказов
Определим сам блок:
Block/Adminhtml/Order/View/Tab/Module.php
<?php namespace Vendor\Module\Block\Adminhtml\Order\View\Tab; class Module extends \Magento\Backend\Block\Template implements \Magento\Backend\Block\Widget\Tab\TabInterface { protected $_template = 'order/view/tab/module.phtml'; /** * * @var \Magento\Framework\Registry */ protected $coreRegistry = null; /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\Registry $registry * @param array $data */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Framework\Registry $registry, array $data = [] ) { $this->coreRegistry = $registry; parent::__construct($context, $data); } public function getOrder() { return $this->coreRegistry->registry('current_order'); } /** * {@inheritdoc} */ public function getTabLabel() { return __('Module'); } /** * {@inheritdoc} */ public function getTabTitle() { return __('Module'); } /** * {@inheritdoc} */ public function canShowTab() { return true; } /** * {@inheritdoc} */ public function isHidden() { return false; } /** * * @return string */ public function getTabClass() { return 'ajax only'; } /** * * @return string */ public function getClass() { return $this->getTabClass(); } /** * * @return string */ public function getTabUrl() { return $this->getUrl('vendor_module/*/module', ['_current' => true]); } }
В функции $this->getUrl для получения url мы передаем параметры: адрес нужного нам роута 'vendor_module/*/module' и дополнительный массив параметров ['_current' => true].
Символ * в адресе роута 'vendor_module/*/module означает передачу текущего используемого роута. То есть, если мы находимся на контроллере sales, то вместо * в итоговом адресе будет тот же контроллер.
Параметр '_current' => true отвечает за передачу таких же дополнительных параметрах к основному адресу при получении ссылки. К примеру, если мы находимся на странице с параметрами order_id/12, то эти же параметры будут переданы при генерации url. Если мы не указываем нужный нам путь, то будет использован тот же модуль, контроллер и action.
Функцию canShowTab можно использовать для того, чтобы в нее передавать условия показа вкладки. К примеру - проверять, ваш ли метод доставки используется в этом заказе.
К примеру, определить принадлежность по $order в carrier можно так:
/** * * @param \Magento\Sales\Model\Order $order * @return boolean */ public function isShippedBy(\Magento\Sales\Model\Order $order) { if (strpos($order->getShippingMethod(), $this->_code . '_') !== false) { return true; } return false; }
Подошли к важному отличию Magento 2 от Magento 1 - в панели администрирования, данные по вкладкам грузятся ajax'ом, поэтому для модуля нужно создать роут (route)
etc/adminhtml/routes.xml
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router id="admin"> <route id="vendor_module" frontName="vendor_module"> <module name="Vendor_Module"/> </route> </router> </config>
Создадим контроллер, который будет отвечать за вкладку:
Controller/Adminhtml/Order/CustomTab.php
<?php namespace Vendor\Module\Controller\Adminhtml\Order; use Magento\Backend\App\Action; use Magento\Sales\Api\OrderManagementInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Psr\Log\LoggerInterface; class Module extends \Magento\Sales\Controller\Adminhtml\Order { /** * @var \Magento\Framework\View\LayoutFactory */ protected $layoutFactory; /** * @param Action\Context $context * @param \Magento\Framework\Registry $coreRegistry * @param \Magento\Framework\App\Response\Http\FileFactory $fileFactory * @param \Magento\Framework\Translate\InlineInterface $translateInline * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory * @param \Magento\Framework\View\Result\LayoutFactory $resultLayoutFactory * @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory * @param OrderManagementInterface $orderManagement * @param OrderRepositoryInterface $orderRepository * @param LoggerInterface $logger * @param \Magento\Framework\View\LayoutFactory $layoutFactory * * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ public function __construct( Action\Context $context, \Magento\Framework\Registry $coreRegistry, \Magento\Framework\App\Response\Http\FileFactory $fileFactory, \Magento\Framework\Translate\InlineInterface $translateInline, \Magento\Framework\View\Result\PageFactory $resultPageFactory, \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory, \Magento\Framework\View\Result\LayoutFactory $resultLayoutFactory, \Magento\Framework\Controller\Result\RawFactory $resultRawFactory, OrderManagementInterface $orderManagement, OrderRepositoryInterface $orderRepository, LoggerInterface $logger, \Magento\Framework\View\LayoutFactory $layoutFactory ) { $this->layoutFactory = $layoutFactory; parent::__construct( $context, $coreRegistry, $fileFactory, $translateInline, $resultPageFactory, $resultJsonFactory, $resultLayoutFactory, $resultRawFactory, $orderManagement, $orderRepository, $logger ); } public function execute() { $this->_initOrder(); $layout = $this->layoutFactory->create(); $html = $layout->createBlock('Vendor\Module\Block\Adminhtml\Order\View\Tab\Module') ->toHtml(); $this->_translateInline->processResponseBody($html); /** @var \Magento\Framework\Controller\Result\Raw $resultRaw */ $resultRaw = $this->resultRawFactory->create(); $resultRaw->setContents($html); return $resultRaw; } }
В данном контроллере функция execute() непосредственно отвечает за выполнение action.
Отметим еще отличие Magento 2 от Magento 1: в M2 каждый action на контроллер делается как отдельный файл. В M1 контроллер мог включать в себя несколько action сразу.
Создадим шаблон вывода:
order/view/tab/module.phtml
<section class="admin__page-section module-tab-content"> <h1>Привет из Санкт-Петербурга</h1> </section>
Делаем очистку кеша
php bin/magento cache:clean
Заходим в заказ и проверяем:
Кликаем:
Conclusion
Готово! Надеемся, что данная статья расширила ваши представления о принципах разработки на Magento 2.