<h5 style="color:red;">系统学习magento二次开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank">《Magento中文全栈二次开发 》</a></h5> <div class="image-container"> <p> <a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank"> <img src="https://www.maxiaoke.com/uploads/images/20230218/bb9c82995c24d1105676e02f373755f5.jpg" alt="Magento中文全栈二次开发"> </a> </p> </div> <div class="text-container" style="font-size:14px; color:#888"> <p>本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。</p> </div> <hr><p>Magento 2中的控制器,就像其他PHP MVC框架一样,是Mvc流的重要组成部分。例如,在Magento 2中,控制器有很多变化<span style="color: #6a9955;">;与Magento 1相比,它们的结构和工作方式。如果您熟悉Magento 1控制器,那么您就会知道它们可以具有多个操作(类方法)。在Magento 2中,控制器只有一个方法(执行)将由前端控制器调用。本文介绍控制器基础知识、匹配流、控制器类型(管理员和前端)、现有控制器上的更改、创建自定义控制器的说明以及如何创建几个控制器的示例。</span></p><p><span style="color: rgb(106, 153, 85);">### 控制器</span><br/></p><p>控制器是位于模块控制器文件夹中的类,负责特定的 URL 或 URL 组。它与Magento 1中的控制器不同,因为它只有一个调用方法(在Magento 1中,我们可以根据需要使用)。所有需要的数据都通过对象管理器的 DI 填充。对于每个操作,我们都有一个带有执行方法的控制器类。当路由器与控制器操作类匹配时,将调用 Execute 方法,它负责将响应返回给前端控制器。所有控制器都在扩展\Magento\Framework\App\Action\Action类,该类具有调度方法,该方法将在控制器中调用执行方法,但我们稍后将介绍流程。有两种控制器类型:前端和管理员。它们具有类似的行为,但管理员有其他方法进行权限检查。控制器以特定方式构建,因此可以匹配。用于匹配的 URL 结构为:</p><p>www.maxiaoke.com/frontName/action 路径/操作类/</p><p>frontName – 它在路由.xml配置中设置,并且具有唯一值,将由路由器匹配</p><p>操作路径 – 控制器文件夹中的文件夹名称,默认为索引</p><p>动作类 – 我们称之为控制器的操作类,默认为索引</p><p>现在,我们将通过动作控制器方法并解释它们的用途。</p><p><span style="color: #6a9955;">### 执行方法</span></p><p>此方法是“第一个”称为控制器操作类,它继承自每个控制器类扩展的\Magento\Framework\App\Action\Action。它由\Magento\Framework\App\Action\Action::d ispatch()方法调用。在这种方法中,我们应该拥有所有的控制器逻辑(当然,我们可以在其他方法中使用逻辑,但执行方法将调用它们),它将返回响应(主要是呈现的页面)。</p><p>\Magento\Framework\App\Action\Action</p><p>这是主要的Magento框架操作类,每个控制器都必须扩展此类(管理控制器正在扩展\Magento\后端\App\Action,它扩展了\Magento\Framework\App\Action\Action)。重要的是,每个控制器都扩展此类以继承所需的方法,并允许前端控制器调用调度方法(将调用执行方法)。</p><p>此方法将首先由Front Controller(Magento\Framework\App\FrontController)调用</p><p></p><pre class="brush:bash;toolbar:false">Magento\Framework\App\FrontController::d ispatch() – 在我们的 Action 类中调用 dispatch: $result = $actionInstance->dispatch($request);</pre><p>请务必了解 Action 类中的调度方法用于前端控制器。对于管理控制器,调度方法被重写(在\Magento\后端\App\Action中),因此它可以检查是否允许用户访问。我们来分析一下我们的调度方法:</p><pre class="brush:bash;toolbar:false">/** * 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; }</pre><p>如我们所见,我们的调度方法将从上下文中返回结果或响应。它将检查请求是否已调度,然后在扩展\Magento\Framework\App\Action\Action的控制器操作类中调用执行方法:</p><p>$<span style="color: #569cd6;">result</span> = $this→execute()<span style="color: #6a9955;">;</span></p><p>在我们的行动类中,我们还需要两种更重要的方法:_forward 和 _redirect。让我们解释一下这些方法:</p><p></p><pre class="brush:bash;toolbar:false">FrontController::d ispatch() while (!$request->isDispatched() && $routingCycleCounter++ < 100) { /** @var \Magento\Framework\App\RouterInterface $router */ foreach ($this->_routerList as $router) { try { $actionInstance = $router->match($request);</pre><p><span style="color: #6a9955;"></span><br/></p><p>它将首先匹配路由器,如上面的代码所示,路由器匹配将返回操作类(\Magento\Framework\App\ActionFactory)实例。之后,前端控制器将在操作类实例上调用调度方法:</p><p>$<span style="color: #569cd6;">result</span> = $actionInstance->dispatch($request)<span style="color: #6a9955;">;</span></p><p>由于我们已经介绍了调度方法,它将调用操作类执行方法:</p><p>$<span style="color: #569cd6;">result</span> = $this->execute()<span style="color: #6a9955;">;</span></p><p>这就是应用程序流进入我们的操作类执行方法的方式。</p><p>管理员和前端控制器之间的区别</p><p>这两个控制器之间的主要区别在于管理控制器中的附加检查和其他方法。两个控制器最终都会扩展 \Magento\Framework\App\Action\Action 类,但管理控制器扩展 \Magento\Backend\App\Action 类,该类扩展 \Magento\Framework\App\Action\Action。在管理控制器调度中,重写重定向和重写方法以提供检查 ACL(访问控制列表)的逻辑。</p><p><span style="color: #6a9955;">### 管理控制器</span></p><p>它扩展了\Magento\Backend\App\Action类,并具有_isAllowed检查访问控制的方法。在调度方法中,它将检查是否允许用户访问当前 URL,并将重定向到登录(如果不允许用户),或者它将设置状态为 403(禁止)的响应:</p><pre class="brush:bash;toolbar:false">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); }</pre><p>如果要创建管理控制器并想要添加一些自定义权限,则需要在_isAllowed方法中添加访问签入,例如:</p><pre class="brush:bash;toolbar:false">protected function _isAllowed() { return $this->_authorization->isAllowed('Magento_EncryptionKey::crypt_key'); }</pre><p><span style="color: #6a9955;">### 现有控制器上的更改</span></p><p>要更改现有控制器,有几种方法可以更改它们。您可以通过首选项,插件或在Magento 1之后/之前使用“旧”样式来做到这一点。首选项将使用您的控制器代码更改完整的控制器(我们可以将其称为完全重写)。插件将仅更改所需的受控方法。最后,之后和之前将更改自定义前端名称的控制器位置。示例是如何在管理区域添加新控制器:</p><pre class="brush:bash;toolbar:false"><router id="admin"> <route id="catalog" frontName="catalog"> <module name="Magento_Catalog" before="Magento_Backend" /> </route> </router></pre><p><span style="color: #6a9955;">### 操作包装类</span></p><p>动作包装类是控制器文件夹中的一个类,它扩展了Magento\Framework\App\Action\Action,然后我们的操作类扩展了该操作包装器。如果您有多个操作类的通用逻辑,那么我们将在操作包装器类中编写逻辑,并将其用于我们需要的每个操作类。</p><p>例如,让我们看看Magento\Catalog\Controller\Product 它是动作包装器类,它用于位于Catalog\Controller\*文件夹中的许多动作类(动作类:Magento\Catalog\Controller\Product\Compare,Magento\Catalog\Controller\Product\Gallery等),它们都使用负责通过帮助程序加载产品的_initProduct。</p><pre class="brush:bash;toolbar:false">/** * 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); } }</pre><p><span style="color: #6a9955;">### 如何创建自定义控制器</span></p><p>在 etc/frontend 或 etc/adminhtml 文件夹中创建路由.xml(第一个用于前端,第二个用于管理控制器)。</p><p>在 routes.xml 中添加控制器的自定义配置,例如: – 路由器:ID – 标准(前端)/管理员</p><p>– 路由:ID – 您的唯一路由 ID</p><p>– 路由:</p><p>frontName – URL 中的唯一名称,这是基本路由器 (www.inchootest.net/frontName/actionpath/actionclass/)</p><p>中 URL 的第一部分 – 模块名称 – 您的模块名称</p><p>按照上面的 url 结构创建操作类:</p><p>控制器/操作路径/操作类.php</p><p>示例 – 管理员和前端控制器</p><p>我们将创建用于自定义控制器演示的示例模块。所以首先,让我们创建一个模块:</p><p>module.xml:</p><pre class="brush:bash;toolbar:false"><?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></pre><p>现在,我们将为两个控制器配置创建操作类。在前端,让我们为 url siteurl/inchoofronttest/demo/sayhello/ 创建操作路径和操作类。您可以看到我们在控制器文件夹中需要演示文件夹(这称为操作路径),在该文件夹中我们需要 Sayhello.php控制器操作类:</p><pre class="brush:bash;toolbar:false"><?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"); } }</pre><p>对于管理控制器,让我们匹配网址siteurl/admin/inchooadmintest/demo/sayadmin/。为此,我们需要 Controller/Adminhtml 中的演示文件夹,在该操作类中,我们需要创建 Sayadmin.php</p><pre class="brush:bash;toolbar:false"><?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"); } }</pre><pre class="brush:bash;toolbar:false">php bin/magento setup:upgrade php bin/magento module:enable Inchoo_CustomControllers php bin/magento setup:upgrade</pre><p><br/></p>
文章列表
<h5 style="color:red;">系统学习magento二次开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank">《Magento中文全栈二次开发 》</a></h5> <div class="image-container"> <p> <a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank"> <img src="https://www.maxiaoke.com/uploads/images/20230218/bb9c82995c24d1105676e02f373755f5.jpg" alt="Magento中文全栈二次开发"> </a> </p> </div> <div class="text-container" style="font-size:14px; color:#888"> <p>本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。</p> </div> <hr><p>在这篇文章中,我们将演示如何使用运输方法创建Magento 2模块。如果您已经熟悉Magento 1,那么所有示例都将非常清楚。创建运输方式非常简单,所以让我们举个例子。</p><p>首先,您需要创建一个Magento模块。您应该创建目录结构,如下面的屏幕截图所示:</p><p>Magento 2模块二极管结构</p><p>创建模块后,您应该使用 shell 脚本启用模块:</p><pre class="brush:bash;toolbar:false">php -f /bin/magento module:enable Inchoo_Shipping</pre><p>此外,您可以使用命令检查是否启用模块(此脚本将打印所有已启用模块的列表):<br/></p><pre class="brush:bash;toolbar:false">php -f /bin/magento module:status</pre><p>让我们从一个处理运输方法的类开始。首先,运输方法应在文件配置.xml中定义,如下面的屏幕截图所示。没有它,它就无法工作。xml 中的主节点是“默认”节点,节点“承运人”的子节点应与运输类“Inchoo\Shipping\Model\Carrier\Example”中的属性 $_code 具有相同的名称。</p><pre class="brush:bash;toolbar:false"><?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> <carriers> <example> <active>1</active> <sallowspecific>0</sallowspecific> <model>Inchoo\Shipping\Model\Carrier\Example</model> <name>Inchoo Example Shipping</name> <price>15.00</price> <title>Inchoo Example</title> <type>I</type> <specificerrmsg>This shipping method is not available. To use this shipping method, please contact us.</specificerrmsg> </example> </carriers> </default> </config></pre><p>在我们的配置中.xml您会注意到XML节点“模型”,它定义了php类“Inchoo\Shipping\Model\Carrier\Example”。此型号类按运输方式收费。在此类中应实现所有用于运输计算的逻辑。</p><p>此外,每种运输方式都应该在管理中具有配置选项。您可以通过 system.xml 文件添加运输方式选项。示例如下:</p><pre class="brush:bash;toolbar:false"><?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="carriers" translate="label" type="text" sortOrder="320" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="example" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Inchoo Example</label> <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="name" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Method Name</label> </field> <field id="price" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Price</label> <validate>validate-number validate-zero-or-greater</validate> </field> <field id="handling_type" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Calculate Handling Fee</label> <source_model>Magento\Shipping\Model\Source\HandlingType</source_model> </field> <field id="handling_fee" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Handling Fee</label> <validate>validate-number validate-zero-or-greater</validate> </field> <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Sort Order</label> </field> <field id="title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Title</label> </field> <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Show Method if Not Applicable</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="specificerrmsg" translate="label" type="textarea" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Displayed Error Message</label> </field> </group> </section> </system> </config></pre><p>发货类别应类似于以下示例:</p><pre class="brush:bash;toolbar:false"><?php namespace Inchoo\Shipping\Model\Carrier; use Magento\Quote\Model\Quote\Address\RateRequest; use Magento\Shipping\Model\Rate\Result; class Example extends \Magento\Shipping\Model\Carrier\AbstractCarrier implements \Magento\Shipping\Model\Carrier\CarrierInterface { /** * @var string */ protected $_code = 'example'; /** * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory * @param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory * @param array $data */ public function __construct( \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory, \Psr\Log\LoggerInterface $logger, \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory, \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory, array $data = [] ) { $this->_rateResultFactory = $rateResultFactory; $this->_rateMethodFactory = $rateMethodFactory; parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data); } /** * @return array */ public function getAllowedMethods() { return ['example' => $this->getConfigData('name')]; } /** * @param RateRequest $request * @return bool|Result */ public function collectRates(RateRequest $request) { if (!$this->getConfigFlag('active')) { return false; } /** @var \Magento\Shipping\Model\Rate\Result $result */ $result = $this->_rateResultFactory->create(); /** @var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */ $method = $this->_rateMethodFactory->create(); $method->setCarrier('example'); $method->setCarrierTitle($this->getConfigData('title')); $method->setMethod('example'); $method->setMethodTitle($this->getConfigData('name')); /*you can fetch shipping price from different sources over some APIs, we used price from config.xml - xml node price*/ $amount = $this->getConfigData('price'); $method->setPrice($amount); $method->setCost($amount); $result->append($method); return $result; } }</pre><p>为了正确地为运输方法编写php类,您应该尊重一些Magento 2规则。每个Magento 2运输类都应该扩展“\Magento\Shipping\Model\Carrier\AbstractCarrier”并实现“\Magento\Shipping\Model\Carrier\CarrierInterface”。</p><p>在运输模型中,您需要创建至少两个 php 方法:“getAllowedMethods”和“collectRates”。抽象类和接口需要此方法。此外,您应该使用值定义属性 $_code。在我们的例子中,这就是“例子”。它与配置.xml和节点结构有关。</p><p>Php 方法 “collectRates” 接受参数 “$request”,它是类 “Magento\Quote\Model\Quote\Address\RateRequest” 的实例。此类包含有关购物车/报价、重量、送货地址等物料的所有信息。在此方法中,您可以实现运费计算的所有逻辑。通过此方法,您可以调用其他服务进行运费计算,但这取决于您的集成。</p><p>您可以在下面的屏幕截图中看到更多信息。</p><p>装运请求对象</p><p>如果您按照我写的格式实现了所有内容,您将能够在结帐时看到运输方式。</p><p><br/></p>
<h5 style="color:red;">系统学习magento二次开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank">《Magento中文全栈二次开发 》</a></h5> <div class="image-container"> <p> <a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank"> <img src="https://www.maxiaoke.com/uploads/images/20230218/bb9c82995c24d1105676e02f373755f5.jpg" alt="Magento中文全栈二次开发"> </a> </p> </div> <div class="text-container" style="font-size:14px; color:#888"> <p>本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。</p> </div> <hr><p>Magento 2在javascript中带来了一些新功能。其中一个是“requireJS”(javascript文件加载器),第二个是“jQuery”,一个非常流行的js库。最大的好处之一是使用requireJS,这意味着Magento 2已经为即将推出的HTTP2协议做好了准备。在接下来的几个步骤中,我们将解释如何创建有效的 javascript 代码以在 requireJS 中使用。让我们从一个例子开始。</p><p>像往常一样,我们需要创建一个有效的Magento 2模块,如下面的屏幕所示:</p><p><img src="/uploads/images/20230822/0431acdef3f69c10d2695b308ad0f925.png" title="3.png" alt=""/></p><p>您可能会注意到Magento 1和Magento 2之间的一些差异,因为所有javascript,layout和template phtml文件现在都属于该模块。此外,我们的javascript文件位于位于目录路径上的模块中:“app/code/Inchoo/Js/view/frontend/web/js/”。在这个例子中,我们创建了两个js文件:“inchoo.js”和“logger.js”</p><p>看看我们的javascript代码,它适用于requireJS环境。</p><p><br/></p><pre class="brush:bash;toolbar:false">define([ "jquery", "logger", "jquery/ui" ], function($, logger) { "use strict"; logger.log('inchoo.js is loaded!!'); logger.log(logger); //creating jquery widget $.widget('inchoo.js', { _create: function() { //options which you can pass from js.phtml file in json format logger.log(this.options); //access to element p#test logger.log(this.element); //for exmple, you can create some click event or something else this.element.on('click', function(e){ logger.log("You click on element: " + e.target); }); } }); return $.inchoo.js; });</pre><p>您会注意到顶部的javascript代码使用了“define”函数。</p><p>这个函数来自requireJs – “define”函数的第一个参数是依赖数组,第二个参数是我们函数的定义。</p><p>定义函数应始终返回一个对象。在我们的示例中,我们的函数返回 “$.inchoo.js”。</p><p>您可以在 url 上阅读有关定义函数的更多信息: http://requirejs.org/docs/api.html<span style="color: #6a9955;">#define</span></p><p>在我们的示例中,我们的javascript使用jQuery,jQueryUI和logger。所有依赖项都应在“定义”函数中声明。您可以看到我们将下一个数组作为第一个参数:</p><p>["jquery", "logger", "jquery/ui"]</p><p>在函数的定义中,我们创建了jquery小部件,并将所有逻辑放在小部件构造函数“_create”中。JQuery 和记录器作为下一个参数传递:“$”和“记录器”。</p><p>我们的记录器类的源代码如下。你可以注意到我们的类不需要任何依赖关系,在“define”函数中,只有定义函数返回我们之前提到的对象。</p><p><br/></p><pre class="brush:bash;toolbar:false">define(function() { "use strict"; return { log : function(param){ console.log(param); } }; });</pre><p>所有文件都应该在“requirejs-config.js”文件中声明 - 这是使用requireJs的配置文件。</p><pre class="brush:bash;toolbar:false">var config = { map: { '*': { inchoojs: 'Inchoo_Js/js/inchoo', logger: 'Inchoo_Js/js/logger' } } };</pre><p>下一步将是解释如何加载和使用我们的 JavaScript。为了使用/加载我们的javascript,你必须在某个phtml文件中添加下一个代码。我们完整的js.phtml文件示例如下:</p><p>测试元件</p><p><script <span style="color: #569cd6;">type</span>=<span style="color: #ce9178;">"text/x-magento-init"</span>>// <![CDATA[ { <span style="color: #ce9178;">"#test"</span>: { <span style="color: #ce9178;">"Inchoo_Js/js/inchoo"</span>: {<span style="color: #ce9178;">"url"</span>:<span style="color: #ce9178;">"http://www.example.url"</span>, <span style="color: #ce9178;">"method"</span>:<span style="color: #ce9178;">"post"</span>} } } // ]]></script></p><p>在Magento 2中,有一个javascript解析器(位于脚本“lib/web/mage/apply/scripts.js中),它读取属性类型=”text/x-magento-init“的脚本标签,并使用卷曲的brakets中的”子“json数据加载/处理它们:</p><pre class="brush:bash;toolbar:false"> "#test": { "Inchoo_Js/js/inchoo": {"url":"http://www.example.url", "method":"post"} }</pre><p>你会注意到“<span style="color: #6a9955;">#test”,这是传递给jquery小部件的html元素的id,它在我们的脚本中可用,通过属性“this.element”。其他元素“url”和“method”是选项,它们也从json对象传递到我们的小部件,这些选项在js属性“this.options”的脚本中可用。可以根据需要定义这些选项。</span></p><p>如果您按照上述方式执行了所有操作,javascript 应该可以工作,并且您应该在 html head 中有一个脚本标签</p><p><br/></p>
<h5 style="color:red;">系统学习magento二次开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank">《Magento中文全栈二次开发 》</a></h5> <div class="image-container"> <p> <a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank"> <img src="https://www.maxiaoke.com/uploads/images/20230218/bb9c82995c24d1105676e02f373755f5.jpg" alt="Magento中文全栈二次开发"> </a> </p> </div> <div class="text-container" style="font-size:14px; color:#888"> <p>本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。</p> </div> <hr><p>还记得那个时候你想创建一个Magento shell或php脚本来执行一些操作,但不知道把它放在哪里吗?</p><p>还记得根 Magento 安装中的 /shell 文件夹(或根文件夹,就此而言)吗?</p><p>还记得它曾经是如何混合 50 个 shell 和 php 脚本的,光看它们你就头疼吗?</p><p>Pepperidge Farm记得。</p><p>那是美好的旧时光,像所有美好的时光一样,它们现在已经过去。街区上有一个新的孩子,更强大,更酷,最好的部分是 - 它被集成到Magento 2中。</p><p>当然,我们正在谈论Magento 2的新功能,即其控制台组件。</p><p>根文件夹现在包含一个新目录 – ,其中有一个“magento”脚本,用于启动控制台组件。通过在终端中输入,我们会收到许多可以运行的命令,例如:/binbin/magento</p><p>创建管理员用户(管理员:用户:创建)</p><p>清除、禁用、启用缓存(缓存:清理、缓存:启用、缓存:禁用等)</p><p>运行 cron (cron:run)</p><p>启用和禁用模块(模块:启用,模块:禁用)</p><p>检查索引器状态,并在需要时重新索引(索引器:信息、索引器:重新索引等)</p><p>等等</p><p>与Magento 1相比,这是一个重大改进,就像Magento所做的一切一样(尽管这个是从Symfony借来的),它是高度可定制和可扩展的。我们可以相当轻松地添加自己的命令,这正是我们将在本文中要做的。</p><p>我们不会详细介绍在Magento 2中创建新模块,因为已经有很多教程可以遵循。</p><p>为了添加新命令,我们只需要执行几个步骤。首先,在模块的 etc 文件夹中创建一个 di.xml 文件(如果您还没有),并将其放入:</p><pre class="brush:bash;toolbar:false"><?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Framework\Console\CommandList"> <arguments> <argument name="commands" xsi:type="array"> <item name="hello_world" xsi:type="object">Inchoo\Console\Console\Command\HelloWorldCommand</item> </argument> </arguments> </type> </config></pre><p>我们在 di.xml 文件中添加了一个新条目。它有什么作用?如果我们检查类型标签及其名称,我们会看到它指向我们。此外,它还具有一些名为“命令”的参数和一个项目标签。看起来很奇怪。让我们去上课,看看有什么大惊小怪的:<span style="color: #ce9178;">"Magento\Framework\Console\CommandList"</span>CommandList</p><pre class="brush:bash;toolbar:false">class CommandList implements CommandListInterface { /** * @var string[] */ protected $commands; /** * Constructor * * @param array $commands */ public function __construct(array $commands = []) { $this->commands = $commands; } /** * {@inheritdoc} */ public function getCommands() { return $this->commands; } }</pre><p>这里没有太多事情发生。简单类,其中构造函数接收数组作为参数,并且它有一个 getter 方法和一个受保护的变量,以及。嗯..等等,构造函数正在接收数组?这看起来很熟悉。我们有一些东西提到了一个名为 .让我们再看一遍:commandscommandsdi.xmlcommands</p><pre class="brush:bash;toolbar:false"><arguments> <argument name="commands" xsi:type="array"> <item name="hello_world" xsi:type="object">Inchoo\Console\Console\Command\HelloWorldCommand</item> </argument> </arguments></pre><p>有趣。我们的参数标签确实被调用,并且它的类型设置为“数组”。看起来Magento正在使用某种魔法将我们的参数发送到CommandList类。 有一个 ,这必须是它发送的东西。</p><p>如果我们检查Magento文档(是的,您没看错):di.xmlscommands<argument><item></p><p>“参数在创建类实例期间注入到类实例中。参数名称必须与已配置类的构造函数参数相对应。</p><p>我们看到这实际上是工作中的依赖注入——将我们的 Command 对象注入到所有命令的数组中。整洁。</p><p>让我们看看这个 HelloWorldCommand 类(它位于我们模块的控制台目录中)。</p><pre class="brush:bash;toolbar:false">class HelloWorldCommand extends Command { protected function configure() { $this->setName('inchoo:hello_world')->setDescription('Prints hello world.'); } protected function execute(InputInterface $input, OutputInterface $output) { $output->writeln('Hello World!'); } }</pre><p>这里只有两种方法:和 。我们从父类 () 继承它们,它们允许我们设置命令。configureexecuteCommand</p><p>Configure方法用于将初始配置设置为命令,例如:名称,描述,命令行参数等。 方法是您编写逻辑的地方。</p><p>在这个例子中,我们刚刚将“Hello World!”打印到屏幕上,但希望你会有更多的想象力。Execute</p><p>要查看该命令的运行情况,请使用 bin/magento 调用它,如下所示:</p><p>bin/magento inchoo:hello_world</p><p>你会受到命令行的欢迎。什么感觉。</p><p>为了让您的生活更轻松,我们准备了一个可以安装的作曲家存储库,无需键入所有内容即可尝试该命令。</p><p>以下是说明:</p><p>将我们的模块添加为依赖项:</p><pre class="brush:bash;toolbar:false">composer config repositories.inchoo_console vcs git@bitbucket.org:lurajcevi/inchoo_console.git</pre><p>请composer下载它:</p><pre class="brush:bash;toolbar:false">composer require inchoo/console:dev-master</pre><p>如果 Composer 要求您提供一组凭据,请按照这些说明操作。</p><p>当composer施展魔法时,我们可以启用我们的模块:</p><pre class="brush:bash;toolbar:false">bin/magento module:enable Inchoo_Console</pre><p>及以后</p><pre class="brush:bash;toolbar:false">bin/magento setup:upgrade</pre><p>我们的模块已启用,我们可以使用它。由于这个想法是向控制台应用程序添加新命令,让我们看看它是否有效。再次呼叫。bin/magento</p><p>希望您会看到以下命令(以及其他所有命令):</p><p>...</p><p>inchoo</p><p>inchoo:hello_world Prints hello world.</p><p>...</p><p>让我们调用它:</p><p>bin/magento inchoo:hello_world</p><p>响应是,正如预期的那样:</p><pre class="brush:bash;toolbar:false">Hello World!</pre><p><br/></p>
<h5 style="color:red;">系统学习magento二次开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank">《Magento中文全栈二次开发 》</a></h5> <div class="image-container"> <p> <a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank"> <img src="https://www.maxiaoke.com/uploads/images/20230218/bb9c82995c24d1105676e02f373755f5.jpg" alt="Magento中文全栈二次开发"> </a> </p> </div> <div class="text-container" style="font-size:14px; color:#888"> <p>本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。</p> </div> <hr><p>由于Magento 2已经推出很长一段时间了,并且基于该平台的新项目正在迅速接近,毫无疑问,作为开发人员,我们需要为挑战做好充分的准备。</p><p>在这篇文章中,我将演示如何处理平台的默认javascript组件(widget实例)。更准确地说,是负责网站主导航功能的javascript组件。如果您有兴趣,请继续阅读。</p><p><span style="color: #6a9955;">### 介绍</span></p><p>Magento 2中主导航的前端部分是用javascript逻辑构建的,负责从移动设备的转换到台式计算机的悬停延迟计时的众多操作。在大多数情况下,默认方法就足够了,并且基本上涵盖了所有需求,但有时,在项目需要特定/自定义方法的情况下,我们迟早会发现自己处于修改默认逻辑的情况。</p><p>让我们把自己放在一个位置,我们的项目需要对主导航javascript部分进行某些修改。首先,我们需要了解该过程涉及的一些基本文件架构和技术。</p><p>Magento 2使用Require JS(AMD或异步模块加载器和jQuery/jQuery UI库)作为创建系统上当前存在的javascript组件的基础。</p><p>我不会进一步介绍requireJS,但如果您需要了解更多信息,您可以查看我们涵盖此主题的博客文章。另外,我强烈建议访问requireJS网站,以便在使用Magento 2之前获得AMD的坚实基础知识并要求JS基本用法。</p><p>另一个重要的注意事项是,jQuery UI widget factory用于提供简单的可扩展技术,与Magento 1中的旧原型类方法非常相似。如果你不熟悉jQuery UI小部件,我强烈建议你访问学习jQuery网站,其中包含小部件工厂示例,以熟悉这个概念。</p><p><span style="color: #6a9955;">### 扩展默认值</span></p><p>负责导航菜单功能的主文件是menu.js位于[project_root]/lib/web/mage/下,以及其他默认的js系统组件。</p><p>现在,如果我们不想扩展或覆盖它,我们需要确保我们遵循以下步骤。</p><p><span style="color: #6a9955;">### 步骤 1</span></p><p>要使用我们的自定义内容正确扩展菜单.js部分,第一步是映射我们的 js 文件,以便系统加载它而不是默认文件。</p><p>Magento使用requirejs-config.js文件成功地映射系统上的js组件。首先重要的是知道在哪里放置requirejs-config.js。它可以放置在多个层次上。</p><p>所有requireJS配置将按以下顺序合并和执行:</p><p>模块级别</p><p>主题模块级别(父主题)</p><p>主题模块级别(当前主题)</p><p>主题级别(父主题)</p><p>主题级别(当前主题)</p><p>让我们用一个真实的例子看看它是如何工作的。默认文件是菜单.js我们希望将其替换为我们的自定义文件。请注意,之后我仍将加载默认菜单.js文件作为依赖项。</p><p>我们需要创建将替换菜单.js文件的文件。我们称之为menu-custom.js并将其放在[current_theme]/web/js/目录下。</p><p><span style="color: #6a9955;">### 步骤 2</span></p><p>接下来,我们需要创建requirejs-config.js文件并将其放在[current_theme]/root目录下。这样我们就可以成功映射我们的文件以替换默认文件。请参阅以下示例:</p><pre class="brush:bash;toolbar:false">var config = { "map": { "*": { "menu": "js/menu-custom" } } };</pre><p>要真正确定该过程是否成功,请确保在加载新文件时签入开发人员工具。</p><p><img src="/uploads/images/20230822/209495ef98a89ddbd9a3fe51f81df742.png" title="6.png" alt=""/></p><p><span style="color: #6a9955;">### 步骤 3</span></p><p>现在是有趣的部分!我们不想扩展默认功能,这就是jQuery小部件工厂发挥作用的地方。我们将以下代码放入我们新创建的菜单自定义.js文件中。</p><p>检查以下示例:</p><pre class="brush:bash;toolbar:false">define([ 'jquery', 'jquery/ui', 'mage/menu'], function($){ $.widget('inchoo.menu', $.mage.menu, { _init: function () { alert("I'm Inchoo"); }, toggle: function () { alert("I'm Inchoo"); } }); return $.inchoo.menu; });</pre><p><span style="color: #6a9955;"></span><br/></p><p>从上面的例子中我们可以看到,我们正在使用 require js 来定义我们的依赖项。第一个是jquery,第二个是jquery/ui,最后一个是mage/menu。</p><p>这意味着在这 3 个完全加载之前,我们的脚本不会加载,因为我们的逻辑依赖于它。</p><p>我们名为 inchoo.menu 的自定义小部件实例正在扩展默认的 $.mage.menu,在本例中,为了本教程,我将扩展两种方法,_init 和切换。切换方法负责切换智能手机和平板电脑上的导航_init而该方法负责组件初始化。</p><p>默认菜单中的原始切换方法示例.js</p><pre class="brush:bash;toolbar:false">toggle: function () { if ($('html').hasClass('nav-open')) { $('html').removeClass('nav-open'); setTimeout(function () { $('html').removeClass('nav-before-open'); }, 300); } else { $('html').addClass('nav-before-open'); setTimeout(function () { $('html').addClass('nav-open'); }, 42); } },</pre><p><span style="color: #6a9955;">### 覆盖文件</span></p><p>在某些情况下(尽管在相对罕见的情况下),您会发现自己可以完全覆盖菜单逻辑(创建自定义导航)。在这种特定情况下,我们可以应用步骤 1 和步骤 2,并在菜单自定义.js文件中创建自定义导航逻辑。</p><p>就是这样!我们已经成功地扩展了默认菜单.js小部件实例。该过程可以应用于系统上当前显示的任何小部件实例。</p><p><br/></p>
<h5 style="color:red;">系统学习magento二次开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank">《Magento中文全栈二次开发 》</a></h5> <div class="image-container"> <p> <a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank"> <img src="https://www.maxiaoke.com/uploads/images/20230218/bb9c82995c24d1105676e02f373755f5.jpg" alt="Magento中文全栈二次开发"> </a> </p> </div> <div class="text-container" style="font-size:14px; color:#888"> <p>本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。</p> </div> <hr><p>通过检查各种电子商务网站,几乎所有网站都可以找到的一件事是特色产品。精心挑选的产品列表,通常放在主页上,用于促销。到目前为止,Magento缺乏大多数请求的功能,开发人员不得不寻找其他解决方案或编写自己的解决方案。让我们看看Magento 2的情况如何。</p><p>为了最终解决这个问题,Magento 2附带了一个新的小部件,可以根据来自各种属性和类别的基于条件的过滤器进行产品选择。如果有人曾经使用过购物车价格规则,这种方法对他们来说并不是什么新鲜事。可以根据产品属性和类别的任意组合创建条件,其中仅显示与规则匹配的产品。</p><p><img src="/uploads/images/20230822/b59a091ad9cf2bfb1100773e8a5cd7e1.png" title="4.png" alt="" width="652" height="746"/></p><p><span style="color: #6a9955;">### 配置选项有哪些?</span></p><p>除了定义条件外,小部件还提供了一些其他配置选项,例如定义标题,启用分页,模板选择,从中创建简单的产品列表。定义条件是一个非常好的功能,它提供了产品选择的灵活性,而不是简单的产品选择(也可以通过SKU属性定义条件来实现),但这种方法有一个缺点。通过定义条件,而不是特定的产品,输出可以做的事情不多,在前端显示之前基本上是未知的。我们大多数人需要和想要的一件事是定义产品列表中的项目顺序或排序,这在这种方法中是不可能的。</p><p><span style="color: #6a9955;">### 如何创建一个?</span></p><p>嗯,有两种方法可以做到这一点。第一种方法是创建并将它们作为CMS块和页面内容的一部分(在管理中可用,位于内容>元素>页面/块)。在所见即所得的编辑器中,有一个插入小部件按钮,允许它们快速创建并插入内容中的光标位置。从下拉列表中选择目录产品列表选项并填写字段。</p><p>另一种方法是创建它们并插入布局更新的位置。导航到内容>元素>微件,然后选择目录产品列表作为类型和您当前的设计主题。布局更新部分允许选择小部件将显示在哪些页面上以及在页面的哪个部分显示。</p><p><img src="/uploads/images/20230822/e67b17588e7d4ce5f2a96f0ee98a8f61.png" title="5.png" alt=""/></p><p>将产品列表小部件包含在Magento中是向前迈出的一步,由于安装中已经打包了这样的功能,因此开发变得更加容易。创建和包含产品列表小部件既简单又快速,对于那些不需要完全控制产品列表的人来说,肯定会在许多新网站上使用。</p><p><br/></p>
<h5 style="color:red;">系统学习magento二次开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank">《Magento中文全栈二次开发 》</a></h5> <div class="image-container"> <p> <a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank"> <img src="https://www.maxiaoke.com/uploads/images/20230218/bb9c82995c24d1105676e02f373755f5.jpg" alt="Magento中文全栈二次开发"> </a> </p> </div> <div class="text-container" style="font-size:14px; color:#888"> <p>本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。</p> </div> <hr><p>当涉及到Magento 2和cron作业时,与Magento 1相比有一些改进。</p><p>它仍然是抽象的,因为您并不真正直接运行作业,而是使用 cron 语法通过 Magento 调度程序设置定期方法执行,该调度程序在系统的 cron 实用程序之上工作。</p><p>以下是一些有用的提示,可帮助您开始设置自己的提示。</p><p>当我们将其与Magento 1进行比较时,数据库似乎没有更改,但是配置确实发生了变化。</p><p>Magento 2将执行表中cron_schedule条目,就像在Magento 1中一样。</p><p>配置中的新闻是,现在我们能够对作业进行分组,并选择是否希望将作业作为单独的进程并行执行,这有时可能会非常方便。</p><p><img src="/uploads/images/20230822/42128e7de877c33833ae4105d89f0fa4.png" title="2.png" alt="" width="856" height="539"/></p><p>因此,为了在Magento中运行计划任务,首先我们需要确保将以下作业配置为通过系统cron运行。这是最简单的方法:</p><p>*/1 * * * * /path/to/php /path/to/m2/installation/bin/magento cron:run [>> /log/file &]</p><p>*/1 * * * * /path/to/php /path/to/m2/installation/update/cron.php [>> /log/file &]</p><p>*/1 * * * * /path/to/php /path/to/m2/installation/bin/magento setup:cron:run [>> /log/file &</p><p>此时,您已经准备好了所有这些 M2 默认作业!</p><p>那么,如何做好自己的工作呢?</p><p>或多或少,这是以与Magento 1类似的方式完成的。</p><p>首先,您需要在自定义模块中.xml crontab:</p><p></p><pre class="brush:bash;toolbar:false"><?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd"> <group id="default"> <!-- Doesn't need to be unique --> <job name="cron_example" instance="Inchoo\CronExample\Cron\Example" method="execute"> <schedule>* * * * *</schedule> </job> </group> </config></pre><p><span style="color: #ce9178;"></span><br/></p><p><span style="color: #ce9178;">上面的配置是为了确保每分钟运行一次Inchoo</span><span style="color: #d7ba7d;">\C</span><span style="color: #ce9178;">ronExample</span><span style="color: #d7ba7d;">\C</span><span style="color: #ce9178;">ron</span><span style="color: #d7ba7d;">\E</span><span style="color: #ce9178;">xample:execute(),这将(根据下面的代码每分钟记录它的名字。</span></p><p><span style="color: #ce9178;"></span></p><pre class="brush:bash;toolbar:false"><?php namespace Inchoo\CronExample\Cron; class Example { protected $_logger; public function __construct(\Psr\Log\LoggerInterface $logger) { $this->_logger = $logger; } public function execute() { $this->_logger->info(__METHOD__); return $this; } }</pre><p><span style="color: #ce9178;"></span><br/></p><p><span style="color: #ce9178;">还值得一提的是,您当时可以选择只运行一个作业组。</span></p><p><span style="color: #ce9178;">以下是您的操作方法:</span></p><pre class="brush:bash;toolbar:false">magento cron:run [--group="cron_group_name"]</pre><p><span style="color: #ce9178;"></span><br/></p><p><br/></p>
<h5 style="color:red;">系统学习magento二次开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank">《Magento中文全栈二次开发 》</a></h5> <div class="image-container"> <p> <a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank"> <img src="https://www.maxiaoke.com/uploads/images/20230218/bb9c82995c24d1105676e02f373755f5.jpg" alt="Magento中文全栈二次开发"> </a> </p> </div> <div class="text-container" style="font-size:14px; color:#888"> <p>本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。</p> </div> <hr><p>与以前的版本相比,Magento 2提出了依赖注入的新概念,其中类为对象注入依赖关系(不同的对象),而不是该对象在内部手动创建它们。这样覆盖和操作类就容易得多,并允许我们更多扩展本机功能的方法。</p><p>必须在类中注入哪些依赖项由文件控制。每个模块都可以有一个全局和特定于区域的文件,可以根据范围使用该文件。模块 di.xml 文件的路径:di.xmldi.xml</p><p><moduleDir>/etc/di.xml</p><p><moduleDir>/etc/<area>/di.xml</p><p>请务必注意,覆盖块、模型、帮助程序、控制器或其他内容之间没有更多区别。它们都是可以重写的类。我们将介绍扩展本机Magento类和方法的三种不同方法。</p><p><span style="color: #6a9955;">### 类首选项</span></p><p>让我们称之为覆盖我们习惯的类的老式方式,但略有不同。所有类都由其接口定义,并由 di.xml 文件配置。当类的构造函数签名通过其接口请求对象时,可以实现抽象实现映射。这意味着应该在可用的情况下使用接口,并且映射将告诉应该启动哪个类。</p><p>让我们看一下目录模块的 di.xml 文件中如何定义目录产品类:</p><pre class="brush:bash;toolbar:false"><config> <preference for="Magento\Catalog\Api\Data\ProductInterface" type="Magento\Catalog\Model\Product" /> </config></pre><p>要覆盖类,我们只需要定义我们的首选项并创建一个将扩展原始类的文件:Magento\Catalog\Model\Product</p><pre class="brush:bash;toolbar:false"><config> <preference for="Magento\Catalog\Api\Data\ProductInterface" type="Inchoo\Catalog\Model\Product" /> </config></pre><pre class="brush:bash;toolbar:false"><?php namespace Inchoo\Catalog\Model; class Product extends \Magento\Catalog\Model\Product { // code }</pre><p>为了确保模块依赖项的顺序正确,应该为示例中的Magento_Catalog定义模块序列。etc/module.xml</p><p><span style="color: #6a9955;">### 插件</span></p><p>如果多个类扩展同一原始类,则按类首选项重写可能会导致冲突。为了帮助解决这个问题,引入了插件的新概念。插件扩展了方法,并且不会像通过类首选项重写那样更改类本身,而是在其调用之前、之后或周围拦截方法调用。</p><p>插件在 di.xml 文件中配置,它们在被覆盖的方法之前、之后或周围调用。第一个参数始终是观察方法名称的对象,后跟原始方法的参数。</p><p>作为示例,我们将扩展目录产品模块中的一些方法。这是 di.xml 文件的外观:</p><pre class="brush:bash;toolbar:false"><config> <type name="Magento\Catalog\Api\Data\ProductInterface"> <plugin name="inchoo_catalog_product" type="Inchoo\Catalog\Plugin\Model\Product" /> </type> </config></pre><p><span style="color: #6a9955;">### before方法</span></p><p>在插件之前在观察到的方法之前运行,并且必须在数组中返回该方法接受的相同数量的参数或 null – 如果不应修改该方法。要扩展的方法必须具有相同的名称,前缀为“before”。</p><pre class="brush:bash;toolbar:false"><?php namespace Inchoo\Catalog\Plugin\Model; class Product { public function beforeSetPrice(\Magento\Catalog\Model\Product $subject, $price) { $price += 10; return [$price]; } }</pre><p>after方法</p><p>在调用原始方法后执行方法之后。在类对象旁边,该方法接受另一个参数,这也是必须返回的结果。要扩展的方法必须具有相同的名称,前缀为“after”。</p><pre class="brush:bash;toolbar:false"><?php namespace Inchoo\Catalog\Plugin\Model; class Product { public function afterGetName(\Magento\Catalog\Model\Product $subject, $result) { $result .= ' (Inchoo)'; return $result; } }</pre><p>around方法</p><p>Around 方法包装原始方法,并允许在原始方法调用之前和之后执行代码。在类对象旁边,该方法接受另一个参数接收是可调用的,允许链中的其他插件调用。要扩展的方法必须具有相同的名称,前缀为“around”。</p><pre class="brush:bash;toolbar:false"><?php namespace Inchoo\Catalog\Plugin\Model; class Product { public function aroundSave(\Magento\Catalog\Model\Product $subject, \callable $proceed) { // before save $result = $proceed(); // after save return $result; } }</pre><p>使用插件看起来像是覆盖方法的理想解决方案,但它有局限性。插件不能用于所有类型的方法,在尝试扩展以下方法时,必须寻找其他解决方案:</p><p>在 Magento\Framework\Interception 引导之前实例化的对象</p><p><span style="color: #6a9955;">### 最终方法</span></p><p>包含至少一个最终公共方法的任何类</p><p>非公共方法</p><p>类方法(如静态方法)</p><p>__construct</p><p>虚拟类型</p><p>构造函数参数</p><p>di.xml 配置哪些依赖项将被注入到类中,这意味着它们可以被控制并更改为对我们有用的东西。如果更改不需要是全局的,而是针对特定类的,</p><p>那么我们可以配置类接收的参数,而不是覆盖整个类或为不同的方法创建插件。</p><p><span style="color: #6a9955;">### 类型配置</span></p><p>目录产品模块接收的参数之一是帮助程序。使用 di.xml我们可以配置为改用我们的助手:\Magento\Catalog\Helper\Product $catalogProduct</p><pre class="brush:bash;toolbar:false"><config> <type name="Magento\Catalog\Api\Data\ProductInterface"> <arguments> <argument name="catalogProduct" xsi:type="object">Inchoo\Catalog\Helper\Product</argument> </arguments> </type> </config></pre><p>允许不同的参数类型,具体取决于要更改的内容。允许的类型为 、、、、 和 。objectstringbooleannumberconstnullarrayinit_parameter</p><p><span style="color: #6a9955;">### 虚拟类型配置</span></p><p>在文档中,虚拟类型被定义为一种类型,它允许您更改特定可注入依赖项的参数并更改特定类的行为。</p><p>如果我们回到帮助程序示例,而不是注入新的帮助程序,有时只更改原始帮助程序中的一个参数就可以完成工作,并且创建新文件是多余的。</p><p>在我们的示例中,这意味着从 创建一个虚拟类型,更改其参数并将该虚拟帮助程序用作目录产品类的参数。Magento\Catalog\Helper\Product</p><pre class="brush:bash;toolbar:false"><config> <virtualType name="virtualHelper" type="Magento\Catalog\Helper\Product"> <arguments> <argument name="catalogSession" xsi:type="object">Inchoo\Catalog\Model\Session\Proxy</argument> </arguments> </type> <type name="Magento\Catalog\Api\Data\ProductInterface"> <arguments> <argument name="catalogProduct" xsi:type="object">virtualHelper</argument> </arguments> </type> </config></pre><p>当只需要更改依赖项的构造参数而不创建任何其他文件,只需在 xml 文件中配置它时,虚拟类型会派上用场。</p><p>覆盖类和方法的方法不止一种,选择使用哪一种方法将取决于您遇到的情况。</p><p>虽然使用类首选项作为覆盖方式可能看起来是在大多数情况下都有效的最简单方法,但当不同的模块尝试覆盖相同的类和相同的方法时,</p><p>这是许多冲突的原因,这就是为什么应该考虑所有方法的原因。</p><p><br/></p>
<h5 style="color:red;">系统学习magento二次开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank">《Magento中文全栈二次开发 》</a></h5> <div class="image-container"> <p> <a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank"> <img src="https://www.maxiaoke.com/uploads/images/20230218/bb9c82995c24d1105676e02f373755f5.jpg" alt="Magento中文全栈二次开发"> </a> </p> </div> <div class="text-container" style="font-size:14px; color:#888"> <p>本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。</p> </div> <hr><p>大多数时候,使用Magento,单个数据库连接就足够了。</p><p>Magento具有出色的系统,可以在数据库中添加新表或扩展现有表。</p><p>那么,为什么需要在Magento系统之外建立外部数据库连接呢?嗯,一个例子是从另一个电子商务系统进行数据迁移。</p><p>在本文中,通过 CRUD(创建、读取、更新、删除)示例介绍了与外部数据库的简单连接。</p><p><span style="color: #6a9955;">### 配置</span></p><p>此外部数据库连接同样被定义为Magento默认连接 - 在XML配置中。不同之处在于外部连接是在特定模块的 XML 配置中定义的。它定义读取和写入适配器、设置和数据库凭据信息。外部表的定义方式与 magento 表相同。它们位于inchoo_foreignconnection_resource节点下,因此稍后可以在代码中调用模型资源。出于演示目的,XML 配置中有一个前端节点,用于定义控制器 (fconn) 的前端名称。</p><pre class="brush:bash;toolbar:false"><?xml version="1.0"?> <config> <modules> <Inchoo_ForeignConnection> <version>1.4.2</version> </Inchoo_ForeignConnection> </modules> <global> <models> <inchoo_foreignconnection> <class>Inchoo_ForeignConnection_Model</class> <resourceModel>inchoo_foreignconnection_resource</resourceModel> </inchoo_foreignconnection> <inchoo_foreignconnection_resource> <class>Inchoo_ForeignConnection_Model_Resource</class> <entities> <product> <table>product_description</table> </product> </entities> </inchoo_foreignconnection_resource> </models> <resources> <inchoo_foreignconnection_write> <connection> <use>inchoo_foreignconnection_database</use> </connection> </inchoo_foreignconnection_write> <inchoo_foreignconnection_read> <connection> <use>inchoo_foreignconnection_database</use> </connection> </inchoo_foreignconnection_read> <inchoo_foreignconnection_setup> <connection> <use>core_setup</use> </connection> </inchoo_foreignconnection_setup> <inchoo_foreignconnection_database> <connection> <host><![CDATA[localhost]]></host> <username><![CDATA[username]]></username> <password><![CDATA[password]]></password> <dbname><![CDATA[db_name]]></dbname> <initStatements><![CDATA[SET NAMES utf8]]></initStatements> <model><![CDATA[mysql4]]></model> <type><![CDATA[pdo_mysql]]></type> <pdo_type><![CDATA[]]></pdo_type> <active>1</active> </connection> </inchoo_foreignconnection_database> </resources> </global> <frontend> <routers> <inchoo_foreignconnection> <use>standard</use> <args> <module>Inchoo_ForeignConnection</module> <frontName>fconn</frontName> </args> </inchoo_foreignconnection> </routers> </frontend> </config></pre><p><span style="color: #6a9955;">### 模型</span></p><p>接下来是一个模型,该模型将使用定义的外部连接来获取数据或将数据保存在外部数据库中。在这里,模型使用 XML 配置中的产品表进行初始化,在本例中定义product_description表。</p><pre class="brush:bash;toolbar:false">class Inchoo_ForeignConnection_Model_Product extends Mage_Core_Model_Abstract { protected $_eventPrefix = 'inchoo_foreignconnection_product'; protected $_eventObject = 'product'; protected function _construct() { $this->_init('inchoo_foreignconnection/product'); } }</pre><p>模型资源类也是使用 _init() 函数中相同的 xml 配置节点定义的,但使用 TABLE_PRIMARY_KEY 参数。在此类中,可以创建多个将处理外部数据的函数。</p><p>第一个例子是createDataInResource函数,它将数据插入模型的表中。它需要将插入的参数数组。</p><p>第二个示例是 readDataFromResource 函数,它从模型的表中获取所有数据。必须先定义读取适配器。它是来自 xml 的配置节点,用于定义读取连接。读取适配器定义后,可以使用Magento数据库函数(select(),from(),limit()等)。当查询完全构造时,可以使用读取适配器执行它。可以使用 fetchPairs() 或 fetchAll() 函数检索数据。fetchAll() 用于获取从 mysql 返回的所有记录。</p><p>updateDataInResource 和 deleteDataFromResource 函数采用额外的$id参数,用于定义将更新或删除的记录。</p><pre class="brush:bash;toolbar:false">class Inchoo_ForeignConnection_Model_Resource_Product extends Mage_Core_Model_Resource_Db_Abstract { const TABLE_PRIMARY_KEY = 'product_id'; protected function _construct() { $this->_init('inchoo_foreignconnection/product', self::TABLE_PRIMARY_KEY); } public function createDataInResource($values = array()) { $writeAdapter = $this->_getWriteAdapter(); try { $writeAdapter->insert( $this->getMainTable(), $values ); } catch (Exception $e) { Mage::log('Unable to insert data to external resource. ' . $e->getMessage(), null, null, true); } } public function readDataFromResource() { $data = array(); $readAdapter = $this->_getReadAdapter(); $select = $readAdapter->select() ->from($this->getMainTable(), '*') ->limit(20); try { $data = $readAdapter->fetchAll($select); } catch (Exception $e) { Mage::log('Unable to fetch data from external resource. ' . $e->getMessage(), null, null, true); } return $data; } public function updateDataInResource($id, $values = array()) { $writeAdapter = $this->_getWriteAdapter(); try { $writeAdapter->update( $this->getMainTable(), $values, self::TABLE_PRIMARY_KEY . '=' . $id ); } catch (Exception $e) { Mage::log('Unable to update data in external resource. ' . $e->getMessage(), null, null, true); } } public function deleteDataFromResource($id) { $writeAdapter = $this->_getWriteAdapter(); try { $writeAdapter->delete( $this->getMainTable(), self::TABLE_PRIMARY_KEY . '=' . $id ); } catch (Exception $e) { Mage::log('Unable to delete data from external resource. ' . $e->getMessage(), null, null, true); } } } class Inchoo_ForeignConnection_Model_Resource_Product_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract { public function _construct() { $this->_init('inchoo_foreignconnection/product'); } }</pre><p><span style="color: #6a9955;">### 控制器中的用法</span></p><p>所有这些函数都在 IndexController 类中演示,但由于它们是在模型的资源类中定义的,因此可以在任何控制器类中调用它们。</p><pre class="brush:bash;toolbar:false">class Inchoo_ForeignConnection_IndexController extends Mage_Core_Controller_Front_Action { public function indexAction() { // Create $foreignProductCreate = Mage::getModel('inchoo_foreignconnection/product')->getResource(); $foreignProductCreate->createDataInResource( array( 'product_name' => 'Product name', 'product_description' => 'Product description' ) ); // Read $foreignProductRead = Mage::getModel('inchoo_foreignconnection/product')->getResource(); $result = $foreignProductRead->readDataFromResource(); var_dump($result); // Update $foreignProductUpdate = Mage::getModel('inchoo_foreignconnection/product')->getResource(); $foreignProductUpdate->updateDataInResource( 3394, array( 'product_name' => 'Product name updated', 'product_description' => 'Product description updated' ) ); // Delete $foreignProductDelete = Mage::getModel('inchoo_foreignconnection/product')->getResource(); $foreignProductDelete->deleteDataFromResource(3394); } }</pre><p>在大多数情况下,Magento将使用不同类型的外部连接来检索或发送数据,但有时像这样的外部数据库连接将是最好的方法。</p><p>其中一个例子是,当您想使用他们的xsell或追加销售产品将产品从另一个系统导入Magento时。</p><p>在这种情况下,读取连接将用于检索产品数据,写入连接将用于将xsell或追加销售产品ID保存在临时表中,以便在导入来自外部系统的所有产品时可以将它们分配给Magento产品。</p><p><br/></p>
<h5 style="color:red;">系统学习magento二次开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank">《Magento中文全栈二次开发 》</a></h5> <div class="image-container"> <p> <a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank"> <img src="https://www.maxiaoke.com/uploads/images/20230218/bb9c82995c24d1105676e02f373755f5.jpg" alt="Magento中文全栈二次开发"> </a> </p> </div> <div class="text-container" style="font-size:14px; color:#888"> <p>本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。</p> </div> <hr><p>无论是水平(更常见)还是垂直,选项卡都是通过将大型内容组织成易于消化的数据块来避免信息过载的好方法。如果做得好,它们提供所有信息(与一个特定主题相关),而不会让用户不知所措,允许他们通过一次仅显示一个选项卡中的数据来快速浏览内容。从用户体验的角度来看,标签的主要目的是简单地促进对信息的访问,并且知道它们不会以任何方式对您的SEO和网站排名产生任何负面影响也很有用。</p><p>在Magento中,与大多数其他电子商务平台一样,产品页面上使用选项卡式导航来显示各种产品信息和数据。默认情况下,Luma和Blank主题也是如此,产品页面上有三个选项卡:</p><p>–有关产品的详细信息,即描述</p><p>–存储产品属性和值</p><p>的更多信息 –产品买家和消费者提供的评论</p><p>这些选项卡可以轻松自定义,我们将向您展示如何自定义。但是,在我们开始之前,让我们花一些时间来探索并找出我们实际要自定义的模板和布局文件。一种方法是启用模板路径提示并通过Magento管理员将块名称添加到提示中:</p><p><span style="color: #569cd6;">Stores</span> => <span style="color: #569cd6;">Configuration</span> => <span style="color: #569cd6;">Advanced</span> => <span style="color: #569cd6;">Developer</span> => Debug</p><p>虽然,至少现在我们知道哪个Magento模块(提示:模块目录)负责产品信息选项卡,因此我们可以从自定义开始,这是值得怀疑的。让我们从简单开始。</p><p><span style="color: #6a9955;">### 重命名产品选项卡</span></p><p>为了为我们的选项卡设置另一个标题,我们必须覆盖文件夹中的基本布局文件。执行此操作的标准(Magento)方法是在我们的主题范围内创建新的布局文件,并将其命名为与基本文件完全相同的名称。catalog_product_view.xmlvendor/module_catalog</p><p>我们的文件路径应如下所示:</p><p>app/design/frontend/<Vendor>/<Theme>/Magento_Catalog/layout/catalog_product_view.xml</p><p>我们文件中的代码是这样的:</p><pre class="brush:bash;toolbar:false"><?xml version="1.0"?> <page layout="1column" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";; xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="product.info.details"> <referenceBlock name="product.info.description"> <arguments> <argument name="title" translate="true" xsi:type="string">Description</argument> </arguments> </referenceBlock> </referenceBlock> </body> </page></pre><p>如果我们分析上面的代码,我们将看到第一个布局处理程序<referenceBlock <span style="color: #569cd6;">name</span>=“product.info.details”>引用我们的产品选项卡式导航作为一个整体,而子处理程序<referenceBlock <span style="color: #569cd6;">name</span>=“product.info.description”>在我们的案例详细信息选项卡中引用单个选项卡。</p><p>使用 <参数名称=“title” <span style="color: #569cd6;">translate</span>=“true” xsi:<span style="color: #569cd6;">type</span>=“string”>我们只需为选项卡设置新标题。 <arguments>处理程序只是<argument>的(必需)容器,它没有自己的属性。</p><p><span style="color: #6a9955;">### 删除产品选项卡</span></p><p>这个更简单。我们只需要引用我们的目标块并将 remove 属性设置为 true。所以我们看起来像这样:catalog_product_view.xml</p><pre class="brush:bash;toolbar:false"><?xml version="1.0"?> <page layout="1column" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";; xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="product.info.review" remove="true" /> </body> </page></pre><p><span style="color: #6a9955;">### 添加自定义选项卡</span></p><p>现在,假设我们想在产品信息选项卡中创建一个额外的选项卡,并用内容填充它,例如某些特定属性的值。出于演示目的,假设我们的新选项卡将包含有关产品包装内容的信息。</p><p>首先,从Magento管理员那里,我们将创建新属性,让我们命名它并将其添加到属性集中。</p><p>接下来,我们将: – 创建新的模板文件,我们可以命名它 – 保存它</p><p>: – 粘贴以下代码:</p><p>Packagingdefaultpackaging-content.phtmlapp/design/frontend/<Vendor>/<Theme>/Magento_Catalog/templates/product/view/</p><p></p><pre class="brush:bash;toolbar:false"><?php $_helper = $this->helper('Magento\Catalog\Helper\Output'); $_product = $block->getProduct(); $_code = $block->getAtCode(); $_className = $block->getCssClass(); $_attributeLabel = $block->getAtLabel(); $_attributeType = $block->getAtType(); $_attributeAddAttribute = $block->getAddAttribute(); if ($_attributeLabel && $_attributeLabel == 'default') { $_attributeLabel = $_product->getResource()->getAttribute($_code)->getFrontendLabel(); } $_attributeValue = $_product->getResource()->getAttribute($_code)->getFrontend()->getValue($_product); ?> <?php if ($_attributeValue): ?> <div class="packaging-content" <?php echo $_attributeAddAttribute;?>> <?php echo $_attributeValue; ?> </div> <?php endif; ?></pre><p>注意:属性集(从第一步开始)必须与 if 语句中的字符串值匹配(行:9)</p><p>第三步也是最后一步是将以下代码放在我们的布局文件中:catalog_product_view.xml</p><p></p><pre class="brush:bash;toolbar:false"><?xml version="1.0"?> <page layout="1column" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";; xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="product.info.details"> <block class="Magento\Catalog\Block\Product\View\Description" name="packaging-content" template="Magento_Catalog::product/view/packaging-content.phtml" group="detailed_info"> <arguments> <argument name="at_call" xsi:type="string">getPackaging</argument> <argument name="at_code" xsi:type="string">packaging</argument> <argument name="css_class" xsi:type="string">packaging</argument> <argument name="at_label" xsi:type="string”>packaging</argument> <argument name="add_attribute" xsi:type="string">itemprop="packaging"</argument> <argument name="title" translate="true" xsi:type="string">Packaging content</argument> </arguments> </block> </referenceBlock> </body> </page></pre><p><span style="color: #ce9178;">### 在选项卡式导航中添加相关产品</span></p><p><span style="color: #ce9178;">为了添加相关产品,我们还需要两个文件,模板和布局: 我们的模板文件,我们将命名它并保存它,只有一行代码:</span></p><p><span style="color: #ce9178;">related-products.phtmlapp/design/frontend/<Vendor>/<Theme>/Magento_Catalog/templates/product/</span></p><pre class="brush:bash;toolbar:false"><?php echo $this->getBlockHtml('catalog.product.related'); ?></pre><p><span style="color: #ce9178;">我们的布局文件应如下所示:catalog_product_view.xml</span></p><p><span style="color: #ce9178;"></span></p><pre class="brush:bash;toolbar:false"><?xml version="1.0"?> <page layout="1column" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";; xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <!— 1st Code Block: Get Related Products as new tab --> <referenceBlock name="product.info.details"> <block class="Magento\Catalog\Block\Product\View" name="deliveryinfo.tab" as="deliveryinfo" template="Magento_Catalog::product/related-products.phtml" group="detailed_info" > <arguments> <argument translate="true" name="title" xsi:type="string">Related Products</argument> </arguments> </block> </referenceBlock> <!— 2nd Code Block: Move original block to product info tabs --> <move element="catalog.product.related" destination="product.info.details" /> </body> </page></pre><p><span style="color: #ce9178;"></span><br/></p><p><span style="color: #ce9178;">第一个代码块是设置包含相关产品的新选项卡,第二个代码块用于从布局流中删除原始块。</span></p><p><span style="color: #ce9178;">以类似的方式,也可以这样做来显示追加销售产品。我们需要做的就是将我们的模板(我们可以命名)文件更改为:upsell-products.phtml</span></p><p><span style="color: #ce9178;"><?php echo $this->getBlockHtml('product.info.upsell'); ?></span></p><p><span style="color: #ce9178;">在我们的布局文件中更改: – 模板文件的名称为 (行: 6) – 选项卡标题为“您可能感兴趣”或类似内容(行: 8) – 元素属性为 (行:</span></p><p><span style="color: #ce9178;">14)</span></p><p><span style="color: #ce9178;">upsell-products.phtmlproduct.info.upsell</span></p><p><span style="color: #ce9178;">总结</span></p><p><span style="color: #ce9178;">从这些示例中可以看出,自定义产品页面选项卡式导航相当容易。添加一些用于样式的CSS,您将在几个小时内拥有带有新自定义内容的新标签。</span></p><p><br/></p>