Cómo anular el bloque central, el modelo y el controlador en Magento2

49

Estoy atascado en anular bloques de modelo y controladores centrales en Magento2. ¿Alguien puede ayudarme en esto?

Tomemos la barra de herramientas de lista como ejemplo donde necesito agregar una nueva opción de ordenación llamada ordenar por la más popular . ¿Cómo lo agrego? Supongo que para esto necesitamos agregar la opción en el nivel de bloque y la condición en el List.phpnivel de colección.

Pradeep Kumar
fuente
1
Reemplazar las clases principales es una mala idea, y se puede hacer de muchas maneras diferentes. ¿Puedes describir tu caso específico?
KAndy
@KAndy: - tomemos un ejemplo de la barra de herramientas de la lista donde necesito agregar una nueva opción de ordenación llamada ordenar por la más popular, luego, cómo agregarla, espero que para esto necesitemos agregar la opción en el nivel de bloque y la condición en el nivel de colección List.php
Pradeep Kumar
Debe usar exactamente después de Ejecutar complemento en \ Magento \ Catalog \ Block \ Product \ ProductList \ Toolbar :: getAvailableOrders para esto. Si algún otro usará complementos, el cliente recibirá todos los pedidos. en caso de usar reescrituras,
obtienes
@KAndy: - ¿puede dar un código de ejemplo? No estoy obteniendo el complemento. Necesito di.xml y el código php del complemento. Cómo funciona y también cómo agregar una nueva columna para la cuadrícula de administrador usando la cuadrícula de orden del complemento
Pradeep Kumar
@Kandy: - comparta cualquier ejemplo de código de complemento en el modelo del producto, agregue texto con el nombre del producto ()
Pradeep Kumar

Respuestas:

30

Magento2 dio muy buen concepto llamado Plugin

podemos hacer lo que sea después y antes de la función central y también tenemos una llamada más que hará tanto antes como después, a continuación se muestra un código que cubrirá toda la información

Cree un archivo di.xml en 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>

en esto tomé un ejemplo de modelo de producto y bloque de vista de producto

Utilicé alrededor del bloque de Vista del producto que es cualquier función, use el prefijo alrededor y luego me aseguro de que haya 2 parámetros, primero el objeto que está usando el segundo cierre que retiene la información de devolución anterior

<?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
    }


}

En el modelo que utilicé antes y después de eso es

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

}

de esta manera podemos retener el código anterior, de modo que si mañana se actualiza el código principal de Magento tendremos tanto el código nuevo actualizado como nuestra lógica personalizada si anulamos directamente y luego perdemos el nuevo código actualizado de esa función o archivo :-)

http://devdocs.magento.com/guides/v2.0/extension-dev-guide/plugins.html

Pradeep Kumar
fuente
¿Qué sucede si desea agregar un nuevo método a una clase? Aparte de la preferencia, ¿qué opción tenemos?
MagePsycho
@MagePsycho: - si tienes algo nuevo, significa que está fuera de magento. si su bloque crea un nuevo bloque y lo extiende desde el núcleo pero sin preferencia. si algún modelo entonces escribe secuencia, espero que no haya otra manera
Pradeep Kumar
19

Finalmente lo tengo !!!!
Sigo los pasos a continuación para anular el bloque, el controlador y el modelo. Tomé un ejemplo del modelo del producto y el bloque de vista del producto y ver el controlador / acción

Cree un archivo llamado di.xml en su /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>

Luego creé el archivo de modelo en /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';
    }    
}

Luego creé el archivo Block en /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');
    }


}

Ahora cree el controlador de vista de producto /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;
        }
    }
}

Esta trabajando bien para mi :-)

Pradeep Kumar
fuente
6

Hay dos pasos para anular el archivo de bloque, modelo y controlador

1) Añadir preferencia en di.xml

2) Cree un archivo de bloque, modelo y controlador en su módulo

Espacio de nombres: Príncipe

Nombre del módulo: Helloworld

Por ejemplo, para anular el catálogo del producto ListProduct block

1) Crear un archivo di.xml en la carpetaPrince/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) Crear ListProduct.php en la carpetaPrince/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
        }
    }

Por ejemplo, para anular el modelo de catálogo del producto.

1) Agregue preferencia en di.xml enPrince/Helloworld/etc

<preference for="Magento\Catalog\Model\Product" type="Prince\Helloworld\Model\Rewrite\Catalog\Product" /> 

2) Crear archivo de modelo Product.php en la carpeta 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();
    }

}

Controlador de anulación

1) Agregue preferencia en di.xml enPrince/Helloworld/etc

<preference for="Magento\Catalog\Controller\Product\View" type="Prince\Helloworld\Controller\Rewrite\Product\View" />

2) Crear View.php en la carpetaPrince/Helloworld/Controller/Rewrite/Product

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

Puede anular otros bloques, modelos y controladores con el mismo enfoque.

Príncipe Patel
fuente
¿Necesitamos agregar reescribir después de Controlador, Modelo y Bloque? Para mí sin agregar reescritura también funcionó.
sagar sapkota
@sagarsapkota Sí, puede usar el controlador, el modelo y el bloque directamente sin reescribir la carpeta.
Prince Patel
4

Pequeña corrección pero muy útil, no necesitamos crear un número n de archivos para cada función en concepto de complemento. Para un módulo, un archivo de complemento es suficiente para que pueda extender todos los módulos, todos los modelos y bloques y el controlador de Magento completo.

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

y en el plugin php file

<?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
    }
}

Rocas Magento2

Pradeep Kumar
fuente
Hola Pradeep: has publicado tres respuestas sobre esta pregunta, probablemente valdría la pena combinarlas en una sola respuesta
Robbie Averill
Probé con esta respuesta, que muestra su error Uncaught Error: Call to undefined method Magento\\Backend\\Model\\View\\Result\\Redirect\\Interceptor::getEntityId()Aquí \Clousure $proceedconseguir obejct deMagento\\Backend\\Model\\View\\Result\\Redirect\\Interceptor
Praful Rajput
3

Puede extender directamente la clase de bloque o controlador de magento en su bloque o controlador personalizado. Por ejemplo, al extender el modelo de factura PDF en mi módulo personalizado para cambiar el logotipo de la factura, PDF generar de la misma manera que puede anular el bloque o el controlador. para crear un archivo di.xml y no es necesario establecer preferencias.

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
fuente
¿Es este realmente el camino a seguir en M2?
Max
El truco en Magento 2 es definir una preferencia en el di.xml. Me falta esta parte bastante esencial en su respuesta ...
7ochem
3
  • Desarrollador / Helloworld / Registration.php

    
    \Magento\Framework\Component\ComponentRegistrar::register(
        \Magento\Framework\Component\ComponentRegistrar::MODULE,
        'Developer_Helloworld',
        __DIR__
    );
  • Desarrollador / 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>

  • Desarrollador / 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>

  • Desarrollador / Helloworld / Controlador / Catálogo / Producto / View.php

    namespace Developer\Helloworld\Controller\Catalog\Product;
    class View extends \Magento\Catalog\Controller\Product\View
    {
        public function execute(){
            echo '__TEST__';exit;
        }
    }
espero que esto sea útil
Shubham Khunt
fuente
2

Una clase de acción puede reescribirse de la misma manera que en Magento 1. En Magento 1 teníamos beforeatributos alrededor de las etiquetas<routers>..<args><modules><... before="Mage_Catalog">Namespace_MyModule ..

En [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>

Y la clase de acción \Namespace\MyModule\Controller\[same path of action as in core module]\SameActionName.phpdondeclass SameActionName.php extends \Magento\Catalog\...\SameActionName

Este es el módulo Magento_Catalog, archivo que Magento\Catalog\etc\adminhtml\routes.xmlregistra una nueva ruta en 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

Para reemplazar la acción del controlador en una ruta con una personalizada, agregue la clase de controlador personalizado antes del controlador original.

El controlador personalizado y la acción deben compartir los mismos nombres con los originales.

El sistema procesa el controlador personalizado antes que el original, mientras que una ruta sigue siendo la misma.

Si debe restablecer una ruta y un diseño, reenvíe el procesamiento de la solicitud a otra ruta:

$this->_forward('other/controller/action')

Para eliminar la acción del controlador, reenvíe a noroute, por ejemplo, en app / code / Company / SomeExtension / Controller / Account.php:

No creo que las preferencias o complementos en las clases de Acción sean una buena idea por las mejores prácticas de Magento. Y puede haber más.

oscuro
fuente
0

Para anular directamente una clase, debe usar las preferencias. Ver más sobre documentos de desarrollo: https://devdocs.magento.com/guides/v2.0/extension-dev-guide/build/di-xml-file.html#abstraction-implementation-mappings
Durante la mayor parte del tiempo utilizamos interceptores (complementos) porque esta es la mejor práctica para reescribir o agregar parte de sus modificaciones. Ver documentos de desarrollo: https://devdocs.magento.com/guides/v2.0/extension-dev-guide/plugins.html

Al mantener su ejemplo de clasificación de elementos de la lista agregando el nuevo orden de clasificación 'Más popular' que le estoy proporcionando La mejor manera de modificar el resultado.
Cree un módulo personalizado y cree la configuración 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> 

Ahora registre su módulo app/code/Arsal/SortOption/registration.php:

<?php
 \Magento\Framework\Component\ComponentRegistrar::register(
     \Magento\Framework\Component\ComponentRegistrar::MODULE,
     'Arsal_SortOption',
      __DIR__
 );

Ahora crea 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>

Ahora cree una clase de bloque 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;
    }

Esto agregará la opción de orden personalizado para ordenar la lista de orden. ingrese la descripción de la imagen aquí }

Arsalan
fuente