Magento 2: Override/Rewrite Block, Model, Controller, Helper using Plugin & Preference.
How to override helper, block, model view?
There are two step to override Block, Model And Controller file
1) Add override preference in di.xml
2) Create block,model and controller file in your module
Namespace: Prince
Module Name: Helloworld
For override catalog product ListProduct block.
1) Create di.xml file in Folder 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) Create ListProduct.php Block file in Folder 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
}
}
For override catalog product model.
1) Add preference in di.xml before
<preference for="Magento\Catalog\Model\Product" type="Prince\Helloworld\Model\Rewrite\Catalog\Product" />
2) Create Product.php Model file in Folder 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();
}
}
For Overriding Controller
1) Add preference in di.xml before
<preference for="Magento\Catalog\Controller\Product\View" type="Prince\Helloworld\Controller\Rewrite\Product\View" />
2) Create View.php Controller file in Folder Prince/Helloworld/Controller/Rewrite/Product
class View extends \Magento\Catalog\Controller\Product\View
{
/**
* #return \Magento\Framework\Controller\Result\Redirect|\Magento\Framework\View\Result\Page
*/
public function execute()
{
// Do your stuff here
return parent::execute();
}
}
You can override other block,model and controllers using same approach.
Related
I Need to Override updateItems function from \Magento\Checkout\Model\Cart
Also need to pass my custom helper class in __construct arguments . This is my __construct function of override class
namespace Vendor\Module\Model;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Checkout\Model\Session;
Class Cart extends \Magento\Checkout\Model\Cart
{
public function __construct(\Magento\Framework\Event\ManagerInterface $eventManager,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Checkout\Model\ResourceModel\Cart $resourceCart, Session $checkoutSession, \Magento\Customer\Model\Session $customerSession, \Magento\Framework\Message\ManagerInterface $messageManager, \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, \Magento\CatalogInventory\Api\StockStateInterface $stockState, \Magento\Quote\Api\CartRepositoryInterface $quoteRepository, ProductRepositoryInterface $productRepository,
\Vendor\Module\Helper\Data $helper, array $data = []
)
{
$this->helper = $helper;
parent::__construct($eventManager, $scopeConfig, $storeManager, $resourceCart, $checkoutSession, $customerSession, $messageManager, $stockRegistry, $stockState, $quoteRepository, $productRepository, $data);
}
}
After this i run setup:upgrade,compile, static content deploy commands. Also remove all folders in var. But when i pass the argument in __construct function. It is not working. It displays blank page. When i remove my arguments from __construct function. then page is loading.
If you want to override updateItems from checkout cart model class then you must add preference in your module di.xml file, something like this:
<?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\Checkout\Model\Cart" type="[Vendor_Name]\[Module_Name]\Model\Cart" />
</config>
then, your custom module app/code/[Vendor_Name]/[Module_Name]/Model/Cart.php should look like this:
<?php
namespace [Vendor_Name]\[Module_Name]\Model;
use Magento\Checkout\Model\Cart as MagentoCart;
use [Vendor_Name]\[Module_Name]\Helper\Data;
class Cart extends MagentoCart
{
protected $helper;
public function __construct(Data $helper)
{
$this->helper = $helper;
}
// Code ...
}
then compile dependencies and that's all. Read this article to know more about Overriding classes in Magento 2
we have a lot of old citrus xml Testcases and templates in our Projects. After Upgrading to newer version I decided to make the switch to Java DSL. Is it possible to keep using the old templates? If i Try to do so, I get a "No bean named .. is defined" exception.
I tried the to import the template file via #ImportResource but without success.
You can write a simple custom test action that loads the template and executes it with current test context:
Given the following template in templates/hello-template.xml
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
xmlns:spring="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.citrusframework.org/schema/testcase http://www.citrusframework.org/schema/testcase/citrus-testcase.xsd">
<template name="helloTemplate">
<echo>
<message>Hello ${user}</message>
</echo>
</template>
</spring:beans>
You can write a custom test action for loading that template:
public class TemplateTest extends TestNGCitrusTestRunner {
#Test
#CitrusTest
public void test() {
run(new CallTemplateAction("templates/hello-template.xml", "helloTemplate"));
}
private class CallTemplateAction extends AbstractTestAction {
private final String templateName;
private final String templateLocation;
public CallTemplateAction(String templateLocation, String templateName) {
this.templateLocation = templateLocation;
this.templateName = templateName;
}
#Override
public void doExecute(TestContext testContext) {
Template template = new ClassPathXmlApplicationContext(new String[] { templateLocation },
testContext.getApplicationContext())
.getBean(templateName, Template.class);
template.getParameter().put("user", "foo");
template.execute(testContext);
}
}
}
You should probably cache the template instance and/or close the application context when done with the action.
I have a component declared as:
<ipojo>
<component classname="HelloClass" name="helloCom" immediate="true">
<requires field="delayService" id="id1">
</requires>
</component>
<instance component="helloCom" name="hello">
<property name="requires.from">
<property name="id1" value="A"/>
</property>
</instance>
</ipojo>
The jar file of this component :helloComponent.jar
Now, i want to update (value="A") to (value="AA"). Thus, i implement a component using ConfigurationAdmin to update this property
public class ControllerReconfiguration {
private ConfigurationAdmin m_configAdmin;
#SuppressWarnings({ "rawtypes", "unchecked" })
public void reconfigure() throws IOException {
Configuration configuration = m_configAdmin.getConfiguration("hello","file:./helloComponent.jar");
configuration.setBundleLocation("file:./helloComponent.jar");
Properties props = new Properties();
//Dictionary props = new Hashtable();
props.put("id1", "AA");
configuration.update(props);
System.out.println("Update");
}
}
However, this ControllerReconfiguration component can't update the value 'A' (by 'AA') in 'hello' instance.
How to modify this ControllerReconfiguration component, please ?
Thanks you for your help.
Unfortunately, you can't push new 'from' configuration like this.
However, you can use the iPOJO introspection API directly: http://felix.apache.org/documentation/subprojects/apache-felix-ipojo/apache-felix-ipojo-userguide/ipojo-advanced-topics/using-ipojo-introspection-api.html
Retrieve the Architecture service of the instance
Retrieve the InstanceDescription and DependencyDescription
Call the setFilter method
Thanks Clement,
it works fine !!!!! :) I access InstanceManager using Factory.
Ex, in order to access InstanceManager of component "hello.call.CallHello"
#require
private Factory[] factories;
for (Factory factory : factories) {
/*
* "hello.call.CallHello" is a component name
* note: it is not component instance name
*/
if (factory.getName().equals("hello.call.CallHello")) {
/*
* a component can have many instances
* if there is only one instance.
* get(0) return the first instance.
*/
InstanceManager im = (InstanceManager) factory.getInstances().get(0);
}
I'm attempting to put together a multi-project application, wherein one of the sub-projects has multiple views for a single presenter. I am using Gin to inject views into my presenters.
The sub-project contains the presenter and the 2 different views. I have 2 separate gin modules, each binding one of the views to the view interface.
As per Thomas Broyer's suggestion on the answer to this post, my Ginjectors are wrapped in a "holder" class that calls the GWT.create on the particular ginjector. The appropriate holder is configured in the gwt.xml file using a replace-with statement.
When I run my project in Dev Mode, I see the alternate view appear as I expect it to. However, when I compile the project, I still only get the default view. Also, only 6 permutations (I would expect more on account of the replace-with logic), and I do not get the view I expect in the different scenarios.
Here is some code to illustrate.
Subproject.gwt.xml contains this:
<replace-with class="com.example.GinjectorDesktopHolder">
<when-type-is class="com.example.GinjectorHolder" />
</replace-with>
<replace-with class="com.example.GinjectorTabletHolder">
<when-type-is class="com.example.GinjectorHolder" />
<when-property-is name="formfactor" value="tablet" />
</replace-with>
The "formfactor" variable is defined in a gwt.xml copied verbatim from GWT's mobilewebapp sample project.
The Holder classes look like this:
public abstract class GinjectorHolder {
public abstract Ginjector getGinjector();
}
public class GinjectorTabletHolder extends GinjectorHolder {
#Override
public Ginjector getGinjector() {
return GWT.create(GinjectorTablet.class);
}
}
public class GinjectorDesktopHolder extends GinjectorHolder {
#Override
public Ginjector getGinjector() {
return GWT.create(GinjectorDesktop.class);
}
}
My Ginjectors look like this:
public interface MyGinjector {
MyView getView();
EventBus getEventBus();
}
#GinModules({ModuleDesktop.class})
public interface GinjectorDesktop extends Ginjector, MyGinjector {}
#GinModules({ModuleTablet.class})
public interface GinjectorTablet extends Ginjector, MyGinjector {}
My modules look like this:
public class ModuleDesktop extends AbstractGinModule {
#Override
protected void configure() {
bind(MyPresenter.View.class).to(DesktopView.class);
}
}
public class ModuleTablet extends AbstractGinModule {
#Override
protected void configure() {
bind(MyPresenter.View.class).to(TabletView.class);
}
}
And finally, in my presenter proxy, basically the entry point into this particular sub-project, I have this line:
GinjectorHolder holder = GWT.create(GinjectorHolder.class);
MyGinjector ginjector = holder.getGinjector();
As mentioned earlier, when I run in Dev Mode and put in breakpoints, I can see the appropriate GinjectorHolder is created. The FormFactor.gwt.xml (linked above) provides a switch for using a URL param to switch to the context you'd like to see. So I can do formfactor=tablet in the URL and the Tablet Ginjector Holder is created.
As mentioned in the comments, removing the line
<collapse-property name="formfactor" values="*"/>
leads to the expected increase in the number of permutations.
Still, it's mysterious, why this is necessary, because usually it should be possible to collapse any properties you like - it just means, that each browser has to download more code, but should still get everything it needs. Could be a bug.
First of all instead of mapping view to viewimpl you can bind it to viewprovider, and then based on user-agent values you can return the appropriate instance to bind to.
I'm using the standard MVC with modules. I have 2 view helper classes that are autoloaded in the config using resources...
resources.view.helperPath.Module1_View_Helper = "module1/views/helpers/"
resources.view.helperPath.Module2_View_Helper = "module2/views/helpers/"
...both contain the same class and method name except for the prefix on the class.
class Module1_View_Helper_Notice extends Zend_View_Helper_Abstract {
public function notice() {
class Module2_View_Helper_Notice extends Zend_View_Helper_Abstract {
public function notice() {
My file...
/modules/[module]/views/scripts/[action]/index.phtml
...contains...
<?php echo $this->notice() ?>
How can I use a specific module view helper based on the path I'm currently in so that I do not have to create specific names for each method?
Directly, I presume.
<?php
require_once (APPLICATION_PATH . '/modules/module1/views/helpers/Notice.php');
$helper = new Module1_View_Helper_Notice ();
$helper->setView ($this);
echo $helper->notice ();