我陷入了在Magento2中覆盖核心模型块和控制器的困境。有人可以帮忙吗?
让我们以列表工具栏为例,我需要添加一个新的排序选项,称为sort by most Popular。如何添加?我想为此,我们需要在块级别添加选项,并在List.php
集合级别添加条件。
我陷入了在Magento2中覆盖核心模型块和控制器的困境。有人可以帮忙吗?
让我们以列表工具栏为例,我需要添加一个新的排序选项,称为sort by most Popular。如何添加?我想为此,我们需要在块级别添加选项,并在List.php
集合级别添加条件。
Answers:
Magento2提出了非常好的概念,称为Plugin
我们可以在核心函数之前和之后执行任何操作,还可以调用另一个函数,在下面的代码前后都可以执行,这是覆盖所有信息的代码
在Mymodule / etc / di.xml中创建文件di.xml
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
<type name="Magento\Catalog\Block\Product\View">
<plugin name="inroduct-custom-module" type="Sugarcode\Test\Block\Plugin\Product\View" sortOrder="1"/>
</type>
<type name="Magento\Catalog\Model\Product">
<plugin name="getname-test-module" type="Sugarcode\Test\Model\Plugin\Product" sortOrder="10"/>
</type>
</config>
在此我以产品型号和产品视图块为例
我在Product View块中使用了周围的任何函数,都使用了前缀,然后确保2参数应该是第一个,而第二个是您使用的对象。Closure保留了旧的返回信息
<?php
namespace Sugarcode\Test\Block\Plugin\Product;
class View
{
public function aroundGetProduct(\Magento\Catalog\Block\Product\View $subject, \Closure $proceed)
{
echo 'Do Some Logic Before <br>';
$returnValue = $proceed(); // it get you old function return value
//$name='#'.$returnValue->getName().'#';
//$returnValue->setName($name);
echo 'Do Some Logic After <br>';
return $returnValue; // if its object make sure it return same object which you addition data
}
}
在我之前和之后使用的模型中
<?php
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Sugarcode\Test\Model\Plugin;
class Product
{
public function beforeSetName(\Magento\Catalog\Model\Product $subject, $name)
{
return array('(' . $name . ')');
}
public function afterGetName(\Magento\Catalog\Model\Product $subject, $result)
{
return '|' . $result . '|';
}
}
这样,我们可以保留旧代码,因此,如果明天更新Magento核心代码,我们将同时拥有新的更新代码和自定义逻辑(如果我们直接覆盖),那么我们将丢失该功能或文件的新的更新代码:-)
http://devdocs.magento.com/guides/v2.0/extension-dev-guide/plugins.html
终于我明白了 !!!!
我按照以下步骤覆盖了块,控制器和模型,并获得了产品模型和产品视图块和视图控制器/操作的示例
在/etc/di.xml中创建一个名为di.xml的文件
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
<preference for="Magento\Catalog\Model\Product" type="Sugarcode\Test\Model\Product" />
<preference for="Magento\Catalog\Block\Product\View" type="Sugarcode\Test\Block\Product\View" />
<preference for="Magento\Catalog\Controller\Product\View" type="Sugarcode\Test\Controller\Product\View" />
</config>
然后我在/Model/Product.php中创建了模型文件
<?php
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Sugarcode\Test\Model;
class Product extends \Magento\Catalog\Model\Product
{
/**
* Get product name
*
* @return string
* @codeCoverageIgnoreStart
*/
public function getName()
{
return $this->_getData(self::NAME).'Local';
}
}
然后我在/Block/Product/View.php中创建了Block文件
<?php
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Sugarcode\Test\Block\Product;
/**
* Product View block
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class View extends \Magento\Catalog\Block\Product\View
{
/**
* Retrieve current product model
*
* @return \Magento\Catalog\Model\Product
*/
public function getProduct()
{
echo 'Local Block';
if (!$this->_coreRegistry->registry('product') && $this->getProductId()) {
$product = $this->productRepository->getById($this->getProductId());
$this->_coreRegistry->register('product', $product);
}
return $this->_coreRegistry->registry('product');
}
}
现在创建Product View Controller /Controller/Product/View.php
<?php
/**
*
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Sugarcode\Test\Controller\Product;
class View extends \Magento\Catalog\Controller\Product\View
{
/**
* Product view action
*
* @return \Magento\Framework\Controller\Result\Forward|\Magento\Framework\Controller\Result\Redirect
*/
public function execute()
{
// Get initial data from request
echo 'I Am in Local Controller';
$categoryId = (int) $this->getRequest()->getParam('category', false);
$productId = (int) $this->getRequest()->getParam('id');
$specifyOptions = $this->getRequest()->getParam('options');
if ($this->getRequest()->isPost() && $this->getRequest()->getParam(self::PARAM_NAME_URL_ENCODED)) {
$product = $this->_initProduct();
if (!$product) {
return $this->noProductRedirect();
}
if ($specifyOptions) {
$notice = $product->getTypeInstance()->getSpecifyOptionMessage();
$this->messageManager->addNotice($notice);
}
if ($this->getRequest()->isAjax()) {
$this->getResponse()->representJson(
$this->_objectManager->get('Magento\Framework\Json\Helper\Data')->jsonEncode([
'backUrl' => $this->_redirect->getRedirectUrl()
])
);
return;
}
$resultRedirect = $this->resultRedirectFactory->create();
$resultRedirect->setRefererOrBaseUrl();
return $resultRedirect;
}
// Prepare helper and params
$params = new \Magento\Framework\Object();
$params->setCategoryId($categoryId);
$params->setSpecifyOptions($specifyOptions);
// Render page
try {
$page = $this->resultPageFactory->create(false, ['isIsolated' => true]);
$this->viewHelper->prepareAndRender($page, $productId, $this, $params);
return $page;
} catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
return $this->noProductRedirect();
} catch (\Exception $e) {
$this->_objectManager->get('Psr\Log\LoggerInterface')->critical($e);
$resultForward = $this->resultForwardFactory->create();
$resultForward->forward('noroute');
return $resultForward;
}
}
}
它对我来说很好:-)
有两个步骤可以覆盖块,模型和控制器文件
1)在di.xml中添加首选项
2)在模块中创建块,模型和控制器文件
命名空间:Prince
模块名称:Helloworld
例如,覆盖目录产品ListProduct块
1)在文件夹中创建di.xml文件Prince/Helloworld/etc
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Catalog\Model\Product" type="Prince\Helloworld\Model\Rewrite\Catalog\Product" />
</config>
2)在文件夹中创建ListProduct.phpPrince/Helloworld/Block/Rewrite/Product
<?php
namespace Prince\Helloworld\Block\Rewrite\Product;
class ListProduct extends \Magento\Catalog\Block\Product\ListProduct
{
public function _getProductCollection()
{
// Do your code here
}
}
例如,覆盖目录产品模型。
1)在di.xml中添加首选项Prince/Helloworld/etc
<preference for="Magento\Catalog\Model\Product" type="Prince\Helloworld\Model\Rewrite\Catalog\Product" />
2)在文件夹中创建Product.php模型文件 Prince/Helloworld/Model/Rewrite/Catalog
<?php
namespace Prince\Helloworld\Model\Rewrite\Catalog;
class Product extends \Magento\Catalog\Model\Product
{
public function isSalable()
{
// Do your code here
return parent::isSalable();
}
}
上位机控制器
1)在di.xml中添加首选项Prince/Helloworld/etc
<preference for="Magento\Catalog\Controller\Product\View" type="Prince\Helloworld\Controller\Rewrite\Product\View" />
2)在文件夹中创建View.phpPrince/Helloworld/Controller/Rewrite/Product
class View extends \Magento\Catalog\Controller\Product\View
{
public function execute()
{
// Do your stuff here
return parent::execute();
}
}
您可以使用相同的方法覆盖其他块,模型和控制器。
校正很小但有用的地方很大,我们无需在插件概念中为每个函数创建n个文件。对于一个模块,一个插件文件就足够了,因为您可以扩展所有模块,所有模型和完整的Magento控制器以及以下代码的自检控制器
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
<type name="Magento\Catalog\Block\Product\View">
<plugin name="inroduct-custom-module" type="Sugarcode\Test\Model\Plugin\Product" sortOrder="1"/>
</type>
<type name="Magento\Catalog\Model\Product">
<plugin name="getname-test-module" type="Sugarcode\Test\Model\Plugin\Product" sortOrder="10"/>
</type>
<type name="Magento\Catalog\Controller\Product\View">
<plugin name="product-cont-test-module" type="Sugarcode\Test\Model\Plugin\Product" sortOrder="10"/>
</type>
</config>
并在插件php文件中
<?php
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Sugarcode\Test\Model\Plugin;
class Product
{
public function beforeSetName(\Magento\Catalog\Model\Product $subject, $name)
{
return array('(' . $name . ')');
}
public function afterGetName(\Magento\Catalog\Model\Product $subject, $result)
{
return '|' . $result . '|';
}
public function aroundGetProduct(\Magento\Catalog\Block\Product\View $subject, \Closure $proceed)
{
echo 'Do Some Logic Before <br>';
$returnValue = $proceed(); // it get you old function return value
$name='#'.$returnValue->getName().'#';
$returnValue->setName($name);
echo 'Do Some Logic After <br>';
return $returnValue;// if its object make sure it return same object which you addition data
}
public function aroundExecute(\Magento\Catalog\Controller\Product\View $subject, \Closure $proceed)
{
echo 'I Am in Local Controller Before <br>';
$returnValue = $proceed(); // it get you old function return value
//$name='#'.$returnValue->getName().'#';
//$returnValue->setName($name);
echo 'I Am in Local Controller After <br>';
return $returnValue;// if its object make sure it return same object which you addition data
}
}
Magento2岩石
Uncaught Error: Call to undefined method Magento\\Backend\\Model\\View\\Result\\Redirect\\Interceptor::getEntityId()
这里\Clousure $proceed
来自Magento\\Backend\\Model\\View\\Result\\Redirect\\Interceptor
您可以直接在自定义块或控制器中扩展magento的块或控制器类。例如,在我的自定义模块中扩展PDF发票模型以更改发票PDF生成的徽标,您可以覆盖块或控制器的方式相同。创建di.xml文件,无需设置首选项。
class Invoice extends \Magento\Sales\Model\Order\Pdf\Invoice
{
/**
* Return PDF document
*
* @param array|Collection $invoices
* @return \Zend_Pdf
*/
public function getPdf($invoices = [])
{
$this->_beforeGetPdf();
$this->_initRenderer('invoice');
$pdf = new \Zend_Pdf();
$this->_setPdf($pdf);
$style = new \Zend_Pdf_Style();
$this->_setFontBold($style, 10);
foreach ($invoices as $invoice) {
if ($invoice->getStoreId()) {
$this->_localeResolver->emulate($invoice->getStoreId());
$this->_storeManager->setCurrentStore($invoice->getStoreId());
}
$page = $this->newPage();
$order = $invoice->getOrder();
/* Add image */
$this->insertCustomLogo($page, $invoice->getStore());
/* Add address */
$this->insertCustomAddress($page, $invoice->getStore());
/* Add head */
$this->insertOrder(
$page,
$order,
$this->_scopeConfig->isSetFlag(
self::XML_PATH_SALES_PDF_INVOICE_PUT_ORDER_ID,
\Magento\Store\Model\ScopeInterface::SCOPE_STORE,
$order->getStoreId()
)
);
/* Add document text and number */
$this->insertDocumentNumber($page, __('Invoice # ') . $invoice->getIncrementId());
/* Add table */
$this->_drawHeader($page);
/* Add body */
foreach ($invoice->getAllItems() as $item) {
if ($item->getOrderItem()->getParentItem()) {
continue;
}
/* Draw item */
$this->_drawItem($item, $page, $order);
$page = end($pdf->pages);
}
/* Add totals */
$this->insertTotals($page, $invoice);
if ($invoice->getStoreId()) {
$this->_localeResolver->revert();
}
}
$this->_afterGetPdf();
return $pdf;
}
protected function insertCustomLogo(&$page, $store = null)
{
$image='demo.png'
if ($image) {
$imagePath = '/logo/' . $image;
if ($this->_mediaDirectory->isFile($imagePath)) {
$image = \Zend_Pdf_Image::imageWithPath($this->_mediaDirectory->getAbsolutePath($imagePath));
$top = 830;
//top border of the page
$widthLimit = 270;
//half of the page width
$heightLimit = 270;
//assuming the image is not a "skyscraper"
$width = $image->getPixelWidth();
$height = $image->getPixelHeight();
//preserving aspect ratio (proportions)
$ratio = $width / $height;
if ($ratio > 1 && $width > $widthLimit) {
$width = $widthLimit;
$height = $width / $ratio;
} elseif ($ratio < 1 && $height > $heightLimit) {
$height = $heightLimit;
$width = $height * $ratio;
} elseif ($ratio == 1 && $height > $heightLimit) {
$height = $heightLimit;
$width = $widthLimit;
}
$y1 = $top - $height;
$y2 = $top;
$x1 = 25;
$x2 = $x1 + $width;
//coordinates after transformation are rounded by Zend
$page->drawImage($image, $x1, $y1, $x2, $y2);
$this->y = $y1 - 10;
}
}
}
}
di.xml
。我在您的答案中缺少这
开发人员/Helloworld/registration.php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Developer_Helloworld',
__DIR__
);
开发人员/Helloworld/etc/module.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Developer_Helloworld" setup_version="1.0.0">
</module>
</config>
开发人员/Helloworld/etc/di.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Catalog\Controller\Product\View" type="Developer\Helloworld\Controller\Catalog\Product\View" />
</config>
namespace Developer\Helloworld\Controller\Catalog\Product;
class View extends \Magento\Catalog\Controller\Product\View
{
public function execute(){
echo '__TEST__';exit;
}
}
希望对您有所帮助
可以按照与Magento 1中相同的方式来重写动作类。在Magento 1中,我们before
在标签周围添加了属性<routers>..<args><modules><... before="Mage_Catalog">Namespace_MyModule ..
在[module path]/etc/[nothing|adminhtml|frontend]/routes.xml
:
<config>
<router id="[admin|standard|]">
<route id="catalog" frontName="catalog">
<module name="Namespace_MyModule" before="Magento_Catalog"/>
</route>
</router>
</config>
动作课\Namespace\MyModule\Controller\[same path of action as in core module]\SameActionName.php
在哪里class SameActionName.php extends \Magento\Catalog\...\SameActionName
这是Magento_Catalog模块,Magento\Catalog\etc\adminhtml\routes.xml
在admin中注册新路由的文件:
<router id="admin">
<route id="catalog" frontName="catalog">
<module name="Magento_Catalog" before="Magento_Backend" />
</route>
</router>
http://devdocs.magento.com/guides/v2.1/extension-dev-guide/routing.html
要将路由中的控制器动作替换为自定义控制器,请在原始控制器之前添加自定义控制器类。
自定义控制器和动作应与原始名称共享相同的名称。
系统在原始路由之前处理自定义控制器,而路由保持不变。
如果必须重置路线和设计,请将请求处理转发到另一条路线:
$this->_forward('other/controller/action')
要删除控制器操作,请前进至noroute,例如在app / code / Company / SomeExtension / Controller / Account.php中:
我不认为Magento最佳实践中Action类的首选项或插件是个好主意。而且可能还有更多。
要直接覆盖类,您需要使用首选项。有关开发文档的更多信息,请参见:https : //devdocs.magento.com/guides/v2.0/extension-dev-guide/build/di-xml-file.html#abstraction-implementation-mappings
大多数情况下,我们使用拦截器(插件)方式,因为这是重写或添加部分修改的最佳实践。请参阅开发文档:https : //devdocs.magento.com/guides/v2.0/extension-dev-guide/plugins.html
通过添加“最流行”的新排序顺序来保留列表项排序示例,我为您提供修改结果的最佳方法。
创建一个自定义模块并创建配置app/code/Arsal/SortOption/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="Arsal_SortOption" setup_version="1.0.0" />
</config>
现在注册您的模块 app/code/Arsal/SortOption/registration.php
:
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Arsal_SortOption',
__DIR__
);
现在创建di.xml app/code/Arsal/SortOption/etc/di.xml
:
<?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\Catalog\Block\Product\ProductList\Toolbar">
<plugin name="toolbar_instance" type="Arsal\SortOption\Block\Product\ProductList\Toolbar" />
</type>
</config>
现在创建一个块类Arsal\SortOption\Block\Product\ProductListToolbar.php
:
<?php
namespace Arsal\SortOption\Block\Product\ProductList;
class Toolbar {
public function afterGetAvailableOrders (
\Magento\Catalog\Block\Product\ProductList\Toolbar $subject, $result
) {
$result ['most_popular'] = 'most popular';
return $result;
}