系统学习magento二次开发,推荐小册:《Magento中文全栈二次开发 》
本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。
Magento 2中的控制器,就像其他PHP MVC框架一样,是Mvc流的重要组成部分。例如,在Magento 2中,控制器有很多变化;与Magento 1相比,它们的结构和工作方式。如果您熟悉Magento 1控制器,那么您就会知道它们可以具有多个操作(类方法)。在Magento 2中,控制器只有一个方法(执行)将由前端控制器调用。本文介绍控制器基础知识、匹配流、控制器类型(管理员和前端)、现有控制器上的更改、创建自定义控制器的说明以及如何创建几个控制器的示例。
### 控制器
控制器是位于模块控制器文件夹中的类,负责特定的 URL 或 URL 组。它与Magento 1中的控制器不同,因为它只有一个调用方法(在Magento 1中,我们可以根据需要使用)。所有需要的数据都通过对象管理器的 DI 填充。对于每个操作,我们都有一个带有执行方法的控制器类。当路由器与控制器操作类匹配时,将调用 Execute 方法,它负责将响应返回给前端控制器。所有控制器都在扩展\Magento\Framework\App\Action\Action类,该类具有调度方法,该方法将在控制器中调用执行方法,但我们稍后将介绍流程。有两种控制器类型:前端和管理员。它们具有类似的行为,但管理员有其他方法进行权限检查。控制器以特定方式构建,因此可以匹配。用于匹配的 URL 结构为:
www.maxiaoke.com/frontName/action 路径/操作类/
frontName – 它在路由.xml配置中设置,并且具有唯一值,将由路由器匹配
操作路径 – 控制器文件夹中的文件夹名称,默认为索引
动作类 – 我们称之为控制器的操作类,默认为索引
现在,我们将通过动作控制器方法并解释它们的用途。
### 执行方法
此方法是“第一个”称为控制器操作类,它继承自每个控制器类扩展的\Magento\Framework\App\Action\Action。它由\Magento\Framework\App\Action\Action::d ispatch()方法调用。在这种方法中,我们应该拥有所有的控制器逻辑(当然,我们可以在其他方法中使用逻辑,但执行方法将调用它们),它将返回响应(主要是呈现的页面)。
\Magento\Framework\App\Action\Action
这是主要的Magento框架操作类,每个控制器都必须扩展此类(管理控制器正在扩展\Magento\后端\App\Action,它扩展了\Magento\Framework\App\Action\Action)。重要的是,每个控制器都扩展此类以继承所需的方法,并允许前端控制器调用调度方法(将调用执行方法)。
此方法将首先由Front Controller(Magento\Framework\App\FrontController)调用
Magento\Framework\App\FrontController::d ispatch() – 在我们的 Action 类中调用 dispatch: $result = $actionInstance->dispatch($request);
请务必了解 Action 类中的调度方法用于前端控制器。对于管理控制器,调度方法被重写(在\Magento\后端\App\Action中),因此它可以检查是否允许用户访问。我们来分析一下我们的调度方法:
/** * Dispatch request * * @param RequestInterface $request * @return ResponseInterface * @throws NotFoundException */ public function dispatch(RequestInterface $request) { $this->_request = $request; $profilerKey = 'CONTROLLER_ACTION:' . $request->getFullActionName(); $eventParameters = ['controller_action' => $this, 'request' => $request]; $this->_eventManager->dispatch('controller_action_predispatch', $eventParameters); $this->_eventManager->dispatch('controller_action_predispatch_' . $request->getRouteName(), $eventParameters); $this->_eventManager->dispatch( 'controller_action_predispatch_' . $request->getFullActionName(), $eventParameters ); \Magento\Framework\Profiler::start($profilerKey); $result = null; if ($request->isDispatched() && !$this->_actionFlag->get('', self::FLAG_NO_DISPATCH)) { \Magento\Framework\Profiler::start('action_body'); $result = $this->execute(); \Magento\Framework\Profiler::start('postdispatch'); if (!$this->_actionFlag->get('', self::FLAG_NO_POST_DISPATCH)) { $this->_eventManager->dispatch( 'controller_action_postdispatch_' . $request->getFullActionName(), $eventParameters ); $this->_eventManager->dispatch( 'controller_action_postdispatch_' . $request->getRouteName(), $eventParameters ); $this->_eventManager->dispatch('controller_action_postdispatch', $eventParameters); } \Magento\Framework\Profiler::stop('postdispatch'); \Magento\Framework\Profiler::stop('action_body'); } \Magento\Framework\Profiler::stop($profilerKey); return $result ?: $this->_response; }
如我们所见,我们的调度方法将从上下文中返回结果或响应。它将检查请求是否已调度,然后在扩展\Magento\Framework\App\Action\Action的控制器操作类中调用执行方法:
$result = $this→execute();
在我们的行动类中,我们还需要两种更重要的方法:_forward 和 _redirect。让我们解释一下这些方法:
FrontController::d ispatch() while (!$request->isDispatched() && $routingCycleCounter++ < 100) { /** @var \Magento\Framework\App\RouterInterface $router */ foreach ($this->_routerList as $router) { try { $actionInstance = $router->match($request);
它将首先匹配路由器,如上面的代码所示,路由器匹配将返回操作类(\Magento\Framework\App\ActionFactory)实例。之后,前端控制器将在操作类实例上调用调度方法:
$result = $actionInstance->dispatch($request);
由于我们已经介绍了调度方法,它将调用操作类执行方法:
$result = $this->execute();
这就是应用程序流进入我们的操作类执行方法的方式。
管理员和前端控制器之间的区别
这两个控制器之间的主要区别在于管理控制器中的附加检查和其他方法。两个控制器最终都会扩展 \Magento\Framework\App\Action\Action 类,但管理控制器扩展 \Magento\Backend\App\Action 类,该类扩展 \Magento\Framework\App\Action\Action。在管理控制器调度中,重写重定向和重写方法以提供检查 ACL(访问控制列表)的逻辑。
### 管理控制器
它扩展了\Magento\Backend\App\Action类,并具有_isAllowed检查访问控制的方法。在调度方法中,它将检查是否允许用户访问当前 URL,并将重定向到登录(如果不允许用户),或者它将设置状态为 403(禁止)的响应:
public function dispatch(\Magento\Framework\App\RequestInterface $request) { if (!$this->_processUrlKeys()) { return parent::dispatch($request); } if ($request->isDispatched() && $request->getActionName() !== 'denied' && !$this->_isAllowed()) { $this->_response->setStatusHeader(403, '1.1', 'Forbidden'); if (!$this->_auth->isLoggedIn()) { return $this->_redirect('*/auth/login'); } $this->_view->loadLayout(['default', 'adminhtml_denied'], true, true, false); $this->_view->renderLayout(); $this->_request->setDispatched(true); return $this->_response; } if ($this->_isUrlChecked()) { $this->_actionFlag->set('', self::FLAG_IS_URLS_CHECKED, true); } $this->_processLocaleSettings(); return parent::dispatch($request); }
如果要创建管理控制器并想要添加一些自定义权限,则需要在_isAllowed方法中添加访问签入,例如:
protected function _isAllowed() { return $this->_authorization->isAllowed('Magento_EncryptionKey::crypt_key'); }
### 现有控制器上的更改
要更改现有控制器,有几种方法可以更改它们。您可以通过首选项,插件或在Magento 1之后/之前使用“旧”样式来做到这一点。首选项将使用您的控制器代码更改完整的控制器(我们可以将其称为完全重写)。插件将仅更改所需的受控方法。最后,之后和之前将更改自定义前端名称的控制器位置。示例是如何在管理区域添加新控制器:
<router id="admin"> <route id="catalog" frontName="catalog"> <module name="Magento_Catalog" before="Magento_Backend" /> </route> </router>
### 操作包装类
动作包装类是控制器文件夹中的一个类,它扩展了Magento\Framework\App\Action\Action,然后我们的操作类扩展了该操作包装器。如果您有多个操作类的通用逻辑,那么我们将在操作包装器类中编写逻辑,并将其用于我们需要的每个操作类。
例如,让我们看看Magento\Catalog\Controller\Product 它是动作包装器类,它用于位于Catalog\Controller\*文件夹中的许多动作类(动作类:Magento\Catalog\Controller\Product\Compare,Magento\Catalog\Controller\Product\Gallery等),它们都使用负责通过帮助程序加载产品的_initProduct。
/** * Product controller. * * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Catalog\Controller; use Magento\Catalog\Controller\Product\View\ViewInterface; use Magento\Catalog\Model\Product as ModelProduct; abstract class Product extends \Magento\Framework\App\Action\Action implements ViewInterface { /** * Initialize requested product object * * @return ModelProduct */ protected function _initProduct() { $categoryId = (int)$this->getRequest()->getParam('category', false); $productId = (int)$this->getRequest()->getParam('id'); $params = new \Magento\Framework\DataObject(); $params->setCategoryId($categoryId); /** @var \Magento\Catalog\Helper\Product $product */ $product = $this->_objectManager->get('Magento\Catalog\Helper\Product'); return $product->initProduct($productId, $this, $params); } }
### 如何创建自定义控制器
在 etc/frontend 或 etc/adminhtml 文件夹中创建路由.xml(第一个用于前端,第二个用于管理控制器)。
在 routes.xml 中添加控制器的自定义配置,例如: – 路由器:ID – 标准(前端)/管理员
– 路由:ID – 您的唯一路由 ID
– 路由:
frontName – URL 中的唯一名称,这是基本路由器 (www.inchootest.net/frontName/actionpath/actionclass/)
中 URL 的第一部分 – 模块名称 – 您的模块名称
按照上面的 url 结构创建操作类:
控制器/操作路径/操作类.php
示例 – 管理员和前端控制器
我们将创建用于自定义控制器演示的示例模块。所以首先,让我们创建一个模块:
module.xml:
<?xml version="1.0"?> <!-- /** * Copyright © 2015 Inchoo d.o.o. * created by Zoran Salamun(zoran.salamun@inchoo.net) * Module is created for Custom Controllers demonstration */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd"> <module name="Inchoo_CustomControllers" setup_version="2.0.0"></module> </config> etc/前端/路由.xml – 前端的路由配置;为了演示,我们将“InchooFrontTest”作为frontname(域名后面URL的一部分 - 例如:www.inchootest.net/inchoofronttest/) <?xml version="1.0"?> <!-- /** * Copyright © 2015 Inchoo d.o.o. * created by Zoran Salamun(zoran.salamun@inchoo.net) * Module is created for Custom Controllers demonstration */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/App/etc/routes.xsd"> <router id="standard"> <route id="inchootestfrontend" frontName="inchoofronttest"> <module name="Inchoo_CustomControllers" /> </route> </router> </config> etc/adminhtml/routes.xml – 管理员的路由配置,为了演示,我们将匹配“InchooadminTest”作为frontname(/admin/之后URL的一部分) <?xml version="1.0"?> <!-- /** * Copyright © 2015 Inchoo d.o.o. * created by Zoran Salamun(zoran.salamun@inchoo.net) * Module is created for Custom Controllers demonstration */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/App/etc/routes.xsd"> <router id="admin"> <route id="inchooadmintest" frontName="inchooadmintest"> <module name="Inchoo_CustomControllers" before="Magento_Backend" /> </route> </router> </config>
现在,我们将为两个控制器配置创建操作类。在前端,让我们为 url siteurl/inchoofronttest/demo/sayhello/ 创建操作路径和操作类。您可以看到我们在控制器文件夹中需要演示文件夹(这称为操作路径),在该文件夹中我们需要 Sayhello.php控制器操作类:
<?php /** * Copyright © 2015 Inchoo d.o.o. * created by Zoran Salamun(zoran.salamun@inchoo.net) */ namespace Inchoo\CustomControllers\Controller\Demonstration; class Sayhello extends \Magento\Framework\App\Action\Action { /** * say hello text */ public function execute() { die("Hello ;) - Inchoo\\CustomControllers\\Controller\\Demonstration\\Sayhello - execute() method"); } }
对于管理控制器,让我们匹配网址siteurl/admin/inchooadmintest/demo/sayadmin/。为此,我们需要 Controller/Adminhtml 中的演示文件夹,在该操作类中,我们需要创建 Sayadmin.php
<?php /** * Copyright © 2015 Inchoo d.o.o. * created by Zoran Salamun(zoran.salamun@inchoo.net) */ namespace Inchoo\CustomControllers\Controller\Adminhtml\Demonstration; class Sayadmin extends \Magento\Backend\App\Action { /** * say admin text */ public function execute() { die("Admin ;) - Inchoo\\CustomControllers\\Controller\\Adminhtml\\Demonstration\\Sayadmin - execute() method"); } }
php bin/magento setup:upgrade php bin/magento module:enable Inchoo_CustomControllers php bin/magento setup:upgrade