Proper Magento Class Overrides
The Good
Magento has a system that allows a developer to easily override a core class by introducing a rewrite into the module’s XML configuration. This works by utilising a string as an abstraction of the true class name, passed to the Mage::getModel($key) method for class instantiation, instead of the traditional $object = new MyClassName();.
It follows that we can then easily generate a lookup table mapping keys to class names, and return the corresponding class. This lookup table is populated and modified by the Magento initial configuration and rewrites.
This is great right? If I want to add some extra functionality to the getProduct() method of the Mage_Product_Model_Product class then I just create my own Nick_Rocks_Model_Product class and change the functionality, and add a rewrite.
The Bad
Now what happens when I need some extra-extra functionality added to the getProduct() method? I decide I don’t have enough time to rewrite the module myself, so I check out Magento Connect and find a module that’s a perfect fit for my current requirements. I install, only the find that the extra functionality added by my initial module has been removed and only the extra-extra functionality remains. Dammit. Bad ju-ju.
The F-ugly
How can we get around this problem? We have two situations.
- Overriding the class to only provide extra methods
- Overriding the class to change the functionality of new methods
In the first situation we can quite easily get around our problem by implementing multiple inheritance, of sorts. An implementation is offered by Josh Ribakoff. There is an obvious performance overhead introduced here — what if we have a long ass list of classes that are attempting to rewrite this class? We have to invoke every one of these classes and check the object for the existence of the method, or use some très clever reflection. Bad ju-ju.
The previous solution does not solve the problem of overriding specific methods. Consider our long ass list of classes all wanting to override the same method? Which one do we let do it?
The Solution
It’s simple. Use event-listeners, helpers, or extend (not override) the core classes to form your own.
The only drawn back to performing these kinds of changes is that they may require a design change when overriding a core class would not. Big deal? Anyone serious about their store will hire someone to install the module and make the design changes themselves. There should be no cutting corners when confidential user information is involved.
Okay, so it’s not so simple. Sometimes the hooks for observing simply do not exist in the code. Some of this is down to the core Magento developers. I’m sure they’d agree that they don’t always code with a module developer in mind. Take the mass action section of the order screen for example. The only way to add another option to the dropdown there is to extend the class and modify the button yourself.
Here are some ideas.
When to (ideally) use event-listeners:
- Adding a button the admin area (not supported by Magento)
- Adding a value to a dropdown
- Performing an action after/before an important system event (adding/deleting/updating orders/invoices/products/configuration)
When to use helpers:
- Use case: Display the number of reward points obtained by completing this order (
Mage::helper('rewards')->getOrderRewardPoints($_order), instead of $order->getRewardPoints())
When to extend the core classes:
- When you need to add some more logic to the internal class functionality
- Use case:
Mage::getModel('my/order')->load($_orderId)->sendSMS()
As Magento continues to grow we need loosely couple our modules to the internal workings of Magento. Honestly, a module should be able to run without extending a single core class, even if it needs some changes to the design.
I think this is the way to do it. What do you think?
Too sweeping. Retail is huge. There will always be a case that requires extending the core.
If you want to replace core logic with your own logic there is unfortunately sometimes no other way then extending a core class. Also while event listeners are great and certainly underused at the moment they can also have their own set of problems in the current implementation (ie send too late in the code, object is unused on return and at some stage the order of event observers will need to be addressed).
And sometimes it takes digging a little bit deeper to find an event/observer solution. Adding to the massaction dropdown is possible via core_block_abstract_prepare_layout_before
then check in your observer
$observer->getEvent()->getBlock() instanceof Mage_Adminhtml_Block_Widget_Grid_Massaction
and use $observer->getEvent()->getBlock()->addItem()
you might want to additionally restrict by controller to not add to every massaction drop down.