Come sovrascrivere Core Block, Model e controller in Magento2


49

Sono bloccato in blocchi di modello principale e controller in Magento2. Qualcuno può aiutare su questo?

Prendiamo la barra degli strumenti dell'elenco come esempio in cui ho bisogno di aggiungere una nuova opzione di ordinamento chiamata ordina per più popolari . Come lo aggiungo? Immagino che per questo dobbiamo aggiungere un'opzione a livello di blocco e una condizione a List.phplivello di raccolta.


1
Sostituire le classi principali è una cattiva idea e può essere fatto in molti modi diversi. Puoi descrivere il tuo caso specifico?
KAndy,

@KAndy: - Prendiamo l'esempio della barra degli strumenti dell'elenco in cui ho bisogno di aggiungere una nuova opzione di ordinamento chiamata ordina per più popolare, quindi come aggiungerlo spero per questo dobbiamo aggiungere un'opzione a livello di blocco e condizione nel livello di raccolta List.php
Pradeep Kumar,

È necessario utilizzare esattamente dopo Esegui plug-in su \ Magento \ Catalog \ Block \ Product \ ProductList \ Toolbar :: getAvailableOrders per questo. Se qualcun altro utilizzerà plugin, il cliente riceve tutti gli ordini. in caso di utilizzo di riscritture, si verificano conflitti tra i moduli e un modulo non funzionerà
KAndy,

@KAndy: - puoi per favore dare un esempio di codice non ricevo plugin ho bisogno di di.xml e del codice php del plugin come funziona e anche come aggiungere una nuova colonna per la griglia di amministrazione usando la griglia di ordine del plugin per favore aiutami in codice del plugin ex
Pradeep Kumar,

@Kandy: - per favore condividi qualsiasi esempio di codice plug-in nel modello del prodotto aggiungi del testo con getname del prodotto ()
Pradeep Kumar

Risposte:


30

Magento2 ha dato un ottimo concetto chiamato Plugin

possiamo fare qualunque cosa funzioni dopo e prima e abbiamo anche un'altra chiamata in giro che farà sia prima che dopo di seguito il codice che coprirà tutte le informazioni

Creare un file di.xml in 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>

in questo ho preso esempio di modello del prodotto e Product View Block

Ho usato in giro nel blocco Product View che è qualsiasi funzione usa il prefisso around e quindi assicurati che il parametro 2 sia presente c'è il primo è quale oggetto stai usando la seconda chiusura che è conservare le vecchie informazioni di ritorno

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


}

Nel modello ho usato prima e dopo

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

}

in questo modo possiamo conservare il vecchio codice, quindi se domani il codice principale di Magento viene aggiornato avremo sia il nuovo codice aggiornato sia la nostra logica personalizzata se eseguiamo l'override diretto, quindi perdiamo il nuovo codice aggiornato di quella funzione o file :-)

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


Cosa succede se si desidera aggiungere un nuovo metodo a una classe? A parte la preferenza quale opzione abbiamo?
MagoPsycho,

@MagePsycho: - se hai qualcosa di nuovo, significa che è fuori dal magento. se il suo blocco crea un nuovo blocco e lo estende dal core ma senza preferenze. se qualche modello poi scrive la sequenza, non spero in nessun altro modo
Pradeep Kumar,

19

Alla fine l'ho ottenuto !!!!
Seguo i passaggi seguenti per sovrascrivere Blocco, Controller e Modello Ho preso Exmaple di Modello prodotto e Visualizza prodotto Blocca e Visualizza Controller / Azione

Crea un file chiamato di.xml nel tuo /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>

Quindi ho creato il File modello in /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';
    }    
}

Quindi ho creato il file Block in /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');
    }


}

Ora crea 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;
        }
    }
}

Funziona benissimo per me :-)


6

Esistono due passaggi per sovrascrivere i file Block, Model And Controller

1) Aggiungi preferenza in di.xml

2) Crea file di blocco, modello e controller nel tuo modulo

Namespace: Prince

Nome del modulo: Helloworld

Ad esempio per sovrascrivere il catalogo di prodotti ListProduct block

1) Crea il file di.xml nella cartellaPrince/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) Crea ListProduct.php nella cartellaPrince/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
        }
    }

Ad esempio per sostituire il modello di prodotto del catalogo.

1) Aggiungi la preferenza in di.xml aPrince/Helloworld/etc

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

2) Crea il file del modello Product.php nella cartella 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();
    }

}

Controller prioritario

1) Aggiungi la preferenza in di.xml aPrince/Helloworld/etc

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

2) Crea View.php nella cartellaPrince/Helloworld/Controller/Rewrite/Product

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

È possibile ignorare altri blocchi, modelli e controller usando lo stesso approccio.


Dobbiamo aggiungere la riscrittura dopo Controller, Model & Block? Anche per me senza aggiungere riscrittura ha funzionato.
sagar sapkota,

@sagarsapkota Sì, puoi utilizzare Controller, Model & Block direttamente senza riscrivere la cartella.
Prince Patel,

4

Piccola correzione ma molto utile, non è necessario creare n numero di file per ogni funzione nel concetto di plugin. Per un modulo è sufficiente un file plugin in quanto è possibile estendere tutto il modulo, tutti i modelli, i blocchi e il controller di 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>

e nel file php del plugin

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


Ciao Pradeep - hai pubblicato tre risposte su questa domanda - probabilmente varrebbe la pena combinarle in un'unica risposta
Robbie Averill,

Ho provato con questa risposta, sta mostrando l'errore Uncaught Error: Call to undefined method Magento\\Backend\\Model\\View\\Result\\Redirect\\Interceptor::getEntityId()Qui \Clousure $proceedottenere obbejct daMagento\\Backend\\Model\\View\\Result\\Redirect\\Interceptor
Praful Rajput,

3

Puoi estendere direttamente la classe di blocco o controller di magento nel tuo blocco o controller personalizzato.Ad esempio, estendendo il modello di fattura PDF nel mio modulo personalizzato per modificare il logo della fattura, il PDF generato allo stesso modo è possibile ignorare il blocco o il controller. per creare il file di.xml e non è necessario impostare le preferenze.

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

}


È davvero questo il modo di andare in M2?
Max

Il trucco di Magento 2 è definire una preferenza in di.xml. Mi manca questa parte piuttosto essenziale nella tua risposta ...
ottobre

3
  • Developer / Helloworld / registration.php

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

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

  • Developer / Helloworld / Controller / Catalogo / Prodotti / view.php

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

2

Una classe di azioni può essere riscritta nello stesso modo di Magento 1. In Magento 1 avevamo beforeattributo attorno ai tag<routers>..<args><modules><... before="Mage_Catalog">Namespace_MyModule ..

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

E la classe d'azione \Namespace\MyModule\Controller\[same path of action as in core module]\SameActionName.phpdoveclass SameActionName.php extends \Magento\Catalog\...\SameActionName

Questo è il modulo Magento_Catalog, il file Magento\Catalog\etc\adminhtml\routes.xmlregistra una nuova rotta in 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

Per sostituire l'azione del controller in una route con una personalizzata, aggiungere la classe controller personalizzata prima del controller originale.

Il controller e l'azione personalizzati devono condividere gli stessi nomi con quelli originali.

Il sistema elabora il controller personalizzato prima dell'originale, mentre un percorso rimane lo stesso.

Se è necessario reimpostare un percorso e un progetto, inoltrare l'elaborazione della richiesta a un altro percorso:

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

Per rimuovere l'azione del controller, inoltra a noroute, ad esempio, in app / code / Company / SomeExtension / Controller / Account.php:

Non credo che le preferenze o i plugin sui corsi di azione siano una buona idea delle migliori pratiche di Magento. E potrebbe essercene di più.


0

Per eseguire l'override diretto di una classe, devi utilizzare le preferenze. Per saperne di più sui documenti di sviluppo: https://devdocs.magento.com/guides/v2.0/extension-dev-guide/build/di-xml-file.html#abstraction-implementation-mappings
Per la maggior parte del tempo utilizziamo gli intercettori (plugin) in quanto questa è la procedura migliore per riscrivere o aggiungere parte delle modifiche. Consulta i documenti di sviluppo: https://devdocs.magento.com/guides/v2.0/extension-dev-guide/plugins.html

Mantenendo il tuo esempio di ordinamento delle voci dell'elenco aggiungendo un nuovo ordinamento "Più popolare" che ti sto fornendo il modo migliore per modificare il risultato.
Crea un modulo personalizzato e crea la configurazione 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> 

Ora registra il tuo modulo app/code/Arsal/SortOption/registration.php:

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

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

Ora crea una classe di blocchi 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;
    }

Ciò aggiungerà un'opzione di ordinamento personalizzata per ordinare l'elenco degli ordinamenti. inserisci qui la descrizione dell'immagine }

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.