Как переопределить основной блок, модель и контроллер в Magento2

49

Я застрял в основной модели блоков и контроллеров в Magento2. Может ли кто-нибудь помочь в этом?

Давайте возьмем панель инструментов списка в качестве примера, где мне нужно добавить новую опцию сортировки под названием сортировка по наиболее популярным . Как мне это добавить? Я думаю, для этого нам нужно добавить опцию на уровне блока и условия на List.phpуровне коллекции.

Прадип Кумар
источник
1
Переопределение базовых классов - плохая идея, и ее можно выполнять разными способами. Можете ли вы описать свой конкретный случай?
Канди
@KAndy: - давайте возьмем пример панели инструментов списка, где мне нужно добавить новую опцию сортировки, называемую сортировкой, по наиболее популярным, как ее добавить. Надеюсь, для этого нам нужно добавить опцию на уровне блока и условие на уровне коллекции List.php
Pradeep Кумар
Для этого вам необходимо использовать плагин Execute на \ Magento \ Catalog \ Block \ Product \ ProductList \ Toolbar :: getAvailableOrders. Если любой другой будет использовать плагины, клиент получает все заказы. в случае использования перезаписей вы получаете конфликты модулей, и один модуль не будет работать
KAndy
@Kandy: - не могли бы вы привести пример кода? Я не получаю плагин. Мне нужен di.xml и php-код плагина, как он работает, а также как добавить новый столбец для сетки администратора, используя сетку плагина ex order, пожалуйста, помогите мне в плагине ex code
Прадип Кумар
@Kandy: - поделитесь, пожалуйста, любым примером кода плагина в модели продукта, добавьте текст с названием продукта ()
Pradeep Kumar

Ответы:

30

Magento2 дал очень хорошую концепцию под названием плагин

мы можем делать то, что когда-либо после и до основной функции, а также у нас есть еще одна вызванная функция, которая будет делать как до, так и после, это код, который будет охватывать всю информацию

Создайте файл di.xml в Mymodule / etc / 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 параметра должны быть там. Первый - это объект, который вы используете, 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

Прадип Кумар
источник
Что если вы хотите добавить новый метод в класс? Помимо предпочтений, какой вариант у нас есть?
MagePsycho
@MagePsycho: - если у вас есть какая-то новая функция, это означает, что она вышла из magento. если его блок создает новый блок и расширяет его от ядра, но без предпочтения. если какая-то модель, то напишите последовательность, я надеюсь, что нет другого пути
Pradeep Kumar
19

Наконец то я понял !!!!
Я следую приведенным ниже шагам, чтобы переопределить блок, контроллер и модель. Я взял пример модели продукта и блок просмотра продукта и просмотр контроллера / действия.

Создайте файл с именем di.xml в вашем /etc/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

<?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');
    }


}

Теперь создайте контроллер представления продукта /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;
        }
    }
}

Это работает отлично для меня :-)

Прадип Кумар
источник
6

Есть два шага для переопределения файла блока, модели и контроллера

1) Добавить предпочтение в di.xml

2) Создайте блок, модель и файл контроллера в вашем модуле

Пространство имен: принц

Название модуля: Helloworld

Например, переопределить каталог товаров.

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.php в папкеPrince/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.php в папкеPrince/Helloworld/Controller/Rewrite/Product

class View extends \Magento\Catalog\Controller\Product\View
{
    public function execute()
    {
        // Do your stuff here
        return parent::execute();
    }
}

Вы можете переопределить другие блоки, модели и контроллеры, используя тот же подход.

Принц Патель
источник
Нужно ли добавлять переписывание после контроллера, модели и блока? У меня без добавления перезаписи тоже работал.
Сахарная сапкота
@sagarsapkota Да, вы можете использовать контроллер, модель и блок напрямую, без перезаписи папки.
Принц Патель
4

Небольшая коррекция, но большая полезность, нам не нужно создавать 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
Praful Rajput
3

Вы можете напрямую расширить класс блоков или контроллеров 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;
        }
    }
}

}

Nidhi
источник
Это действительно способ пойти в М2?
Макс
Хитрость в Magento 2 заключается в определении предпочтения в di.xml. Я
скучаю по
3
  • Разработчик / Helloworld / registration.php

    
    \Magento\Framework\Component\ComponentRegistrar::register(
        \Magento\Framework\Component\ComponentRegistrar::MODULE,
        'Developer_Helloworld',
        __DIR__
    );
    
  • Разработчик / Helloworld / и т.д. / 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 / и т.д. / 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>

  • Разработчик / Helloworld / Controller / Каталог / продукта / view.php

    namespace Developer\Helloworld\Controller\Catalog\Product;
    class View extends \Magento\Catalog\Controller\Product\View
    {
        public function execute(){
            echo '__TEST__';exit;
        }
    }

надеюсь, что это полезно
Шубхам Хунт
источник
2

Класс действия может быть переписан так же, как в 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регистрации нового маршрута в админке:

<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:

Я не верю, что предпочтения или плагины для классов Action являются хорошей идеей лучших практик Magento. И может быть что-то еще.

затенить
источник
0

Чтобы переопределить класс напрямую, вам необходимо использовать настройки. Более подробную информацию можно найти в документации разработчика: 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;
    }

Это добавит пользовательский параметр порядка сортировки в список порядка сортировки. введите описание изображения здесь }

Arsalan
источник