It's pretty easy to replace a class in Magento 2 using dependency injection. However, let's say I have two modules that want to replace the same class. For example, the dotmailer Magento2 extension sets a preference for Magento\Customer\Model\AccountManagement
but my own team's modules already override that class with one of our own. How do we resolve this conflict?
-
1Use plugins and observers :)– Siarhey UchukhlebauCommented May 31, 2017 at 14:41
-
@SiarheyUchukhlebau That doesn't work when the methods that need changing are private or protected - which is, unfortunately, a lot of the time.– Mr. Shiny and New 安宇Commented May 31, 2017 at 14:42
-
If the method is private you should not change it anyway.– Siarhey UchukhlebauCommented May 31, 2017 at 14:44
-
3@SiarheyUchukhlebau Private and protected methods can be wrong/inappropriate/buggy/defective just as much as public methods.– Mr. Shiny and New 安宇Commented May 31, 2017 at 14:59
2 Answers
It's the same problem like in M1 if modules overwrite the same class. You've to implement your own class which consolidate the changes of all conflicting modules in order to resolve it.
Your basic module structure
- app/code/YourNamespace
|- ConflictResolver
|- etc
| |- module.xml
| |- di.xml
|- Model
| |- YourClass.php
|- composer.json
|- registration.php
Your module structure should look something like this. The third party and Magento modules are located in vendor directory... If not see here for how to setup a Magento 2 project: http://devdocs.magento.com/guides/v2.0/install-gde/prereq/integrator_install_ce.html
In the module.xml
you have to define the sequence for your module loading
<?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="YourNamespace_ConflictResolver" setup_version="1.0.0">
<sequence>
<module name="ThirdParty_ModuleA" />
<module name="ThirdParty_ModuleB" />
<module name="Magento_ModuleA" />
</sequence>
</module>
</config>
In this example your module would be loaded after the three modules listed in the sequence
node and your DI config will overrule the previous loaded...
See this page for more details: http://devdocs.magento.com/guides/v2.0/extension-dev-guide/build/module-load-order.html
However this is not the preferred way of the Magento Engineers. Plugins have better compatibility and if a module overwrites by using preferences this is a sign for less quality... Of course I understand, that there is in some cases no other way because the core it self does not follow Magento 2 standards in most modules.
The Future...
I've told with some core developers about further engineering ideas and concepts. The new way to go will be that all modules will be separate into API / service contracts and (final; final is not really possible because of the autogenerated classes) implementation so there will be interfaces for everything, but you can't extend from core and have to implement your own logic... For your problem this would mean that you have to implement an own class which includes the right interface(s) anyway...
Edit: Alan Kent just published something about this: https://community.magento.com/t5/Magento-DevBlog/Service-Contracts-in-Separate-Packages/ba-p/68129
-
"Implement your own class to consolidate the changes of all conflicting modules" - that sounds reasonable - a conflict-resolution module that resolves conflicts between two fighting modules? Am I reading you correctly? Commented May 31, 2017 at 19:14
-
Yes, you do. I updated my answer in order to be more concrete, so I hope... Commented May 31, 2017 at 19:22
-
I'm new to Magento - is it possible for you to expand in your answer how to structure the module so that its consolidation class gets loaded instead of the two classes it's mediating? Or should I post that as a new question? Commented May 31, 2017 at 19:25
-
I'll try :) But currently I've only my smartphone, so you have to way till tomorrow... Commented May 31, 2017 at 19:30
-
Make your class extend the dot mailer one. And declare your module to be loaded after the dot mailer module in module.xml.
-
Okay, I guess that works in the simple example I posted, but what about if I have two third-party modules overriding the same class? Or if extending a third-party module's class isn't feasible? Commented May 31, 2017 at 18:19
-
1
-
So short of making one module depend on the other, there's no other solution for this? Commented May 31, 2017 at 18:22
-
Not that I know of. But there might be. Wait for an other answer. Don't take mine for granted.– Marius ♦Commented May 31, 2017 at 18:24
-
Thanks. I was going to wait anyway, since your answer's just bad news :) Commented May 31, 2017 at 18:25