Archives for : php 5.4

Resolving Magento extension rewrite conflicts using traits

A bit of history

On 5th of September I sent out a tweet:

Guys, this is serious. I guess, I just resolved Magento’s single intheritance problem by using traits. Details later. #magento #worksforme.

Those who have been serious in hacking Magento and 3rd party extensions know well what I’m talking about. But please let me explain further.

Magento extension rewrite conflicts

You have a Magento instance. You have created a custom module My_Custom that happens to rewrite a core model, let’s say Mage_Catalog_Model_Product. So you have following lines in your module’s config.xml:

Then you have have a 3rd party extension that rewrites the same core class. You’re stuck now? Not quite. You now have 3 good methods to fix the problem. So good so far. But then you need another 3rd party extension that does the same – rewrites catalog/product. Now you’re stuck, right!?
You cannot make your code extend the 1st extension because the the second wouldn’t work and you cannot make yours to extend the second either because the 1st wouldn’t work then. Chicken and egg problem or “nokk kinni, saba lahti” as we say in Estonian.

The solution – PHP 5 feature “traits”

There’s a solution to PHP’s single inheritance feature and Magento rewrites that together make a problem. The solution is achieved by utilizing PHP 5.4 (or newer) feature called traits. So get a coffee (it’s pretty long post) and let’s start.
We have a situation where 3 classes want to rewrite and extend Magento’s Mage_Catalog_Model_Product:
My_Custom_Model_Product (this is mine that I can freely change)
Extension_A_Model_Product
Extension_B_Model_Product
Let’s say Extension_A_Model_Product adds the following methods to catalog/product:
And let’s assume Extension_B_Model_Product wants to add the following methods to catalog/product:
And at last my custom module wants to add something extra to the core product model:

The idea is to make only My_Custom_Model_Product to rewrite catalog/product or at least to be the first one selected for rewrites. The other 2 3rd party module classes would just extend it and be used inside my product model via traits.

Add dependencies to your module

First it’s necessary to add dependencies to your module XML. This is to ensure the loading order of classes and rewrites.

Add traits

Folder Traits should be created under extension’s folder in similar manner to Block, Product and Helper:
My/Custom/Trait.
For naming we follow the same logic as other Magento building blocks but there’s no shorthand for traits as it exists for blocks, models and helpers (e.g catalog/product etc). No worries, the trait would still be found and loaded by the autoloader.
Under that folder create the trait folders and files for classes Extension_A_Model_Product and Extension_B_Model_Product
My/Custom/Trait/Extension/A/Model/Product.php

My/Custom/Trait/Extension/B/Model/Product.php

Trait for Extension_A_Model_Product

Trait for Extension_B_Model_Product

Add traits to My_Custom_Model_Product

Voilaa!:) Now you have all the 3rd party module functionality available in your extension. When Magento is calling getModel(‘catalog/product’) it will actually load My_Custom_Model_Product. My_Custom_Model_Product extends Mage_Catalog_Model_Product and it also exposes all methods from Extension_A_Model_Product and Extension_B_Model_Product.

A note about traits

With traits you can avoid duplicating original class method implementation’s part. You’ll just create a proxy for original class method’s thru the trait. However you need to maintain the traits whenever Extension_A or Extension_B are upgraded or changed. So you should document it that you created traits and solved multiple rewrite problem this way. Then you can look up the documentation when doing upgrade to extensions next time.

As always – feedback is welcome!