Magento - multiple classes extending same core class
Asked Answered
C

1

20

I'm sure we've all run into a situation where you have multiple extensions with a block or model that rewrites the same core block/model. The problem I've run into is this: How do you control the order in which Magento sees these classes?

For example, let's say we have 2 extensions with the following 2 classes:

Class A

config.xml

<catalog>
    <rewrite>
        <product_view>My_ClassA_Block_Catalog_Product_View</product_view>
    </rewrite>
</catalog>

My/ClassA/Block/Catalog/Product/View.php

class My_ClassA_Block_Catalog_Product_View extends Mage_Catalog_Block_Product_View {}

Class B

<catalog>
    <rewrite>
        <product_view>My_ClassB_Block_Catalog_Product_View</product_view>
    </rewrite>
</catalog>

My/ClassB/Block/Catalog/Product/View.php

class My_ClassB_Block_Catalog_Product_View extends Mage_Catalog_Block_Product_View {}

--

The recommended solution is to change one of them so they extend the other and chain them together (class A extends B {}, class B extends C {}, etc):

My/ClassA/Block/Catalog/Product/View.php

class My_ClassA_Block_Catalog_Product_View extends My_ClassB_Block_Catalog_Product_View {}

My/ClassB/Block/Catalog/Product/View.php

class My_ClassB_Block_Catalog_Product_View extends Mage_Catalog_Block_Product_View {}

--

The problem I've run into is that Magento doesn't necessarily see it that way. I don't know if it's alphabetical or somewhat random, but sometimes this works and sometimes it doesn't. In some cases, Magento gives priority to ClassB and all calls to createBlock('catalog/product_view') create an instance of ClassB, completely bypassing any code in ClassA.

So my question is this: How do I control which class gets instantiated by createBlock('catalog/product_view') when 2 different extensions both rewrite the core catalog_product_view class?

Cohesion answered 21/9, 2011 at 14:28 Comment(0)
S
33

When Magento fetches the class to use for a particular block, it looks inside the merged config.xml tree for a single node at

catalog/rewrite/product_view

The problem with multiple rewrites is, only one node can be there due to the way Magento loads a module's XML, merges it with the config tree, and then loads another model. This means you can only ever have one class alias resolve to one class name.

That's where the files in

app/etc/modules/*.xml

come into play. These files tell Magento which modules to use. They also have support for a <depends> tag. This tag allows you to say certain modules depend on another module, which means their config.xml will be loaded after another module's config.xml. In this way, you can control which order the modules are loaded in, and therefore control which merged rewrite node "wins", which in turn will allow you to know which class needs to be the final in your inheritance chain.

Seeley answered 21/9, 2011 at 16:1 Comment(10)
Thank you, that's a great explanation. I assume, given my example, that ClassA would have in its config.xml something like <depends>ClassB</depends>, correct? If that's the case, what if ClassB doesn't exist? Does ClassA still get loaded? If not, then is there any way to make it generic enough to work in that case?Cohesion
I think you're almost getting it, but not quite. The depends tag doesn't go in config.xml, it goes in the Packagename_Modulename.xml file. (Look at existing files in there for examples). Each Magento modules can define one rewrite for a class. The problem you're seeing in because multiple modules define a rewrite for the same class. By defining the order your modules load in, you define which module "wins". Then, by including all the classes in the inheritance chain, you ensure the functionality of all rewrites in maintained.Seeley
One thing to keep in mind, and which might help. If both rewrites override the SAME method, and don't call parent::method at some point, even the A extends B, B extends C solution won't work.Seeley
Oops, you're right. I read app/etc/modules/*.xml and proceeded to edit those files, but when I wrote my comment, I wrote config.xml without thinking. I think I do get it...but my brain and fingers don't always cooperate ;)Cohesion
As for my first comment regarding whether it works if ClassB doesn't exist...it would seem in my testing that no...it doesn't work. Is there a such thing as <depends_if_exists>...not literally, but something that effectively accomplishes that?Cohesion
No, there's nothing like that.Seeley
Regarding <depends_if_exists>, you can use next workaround: add rewrites to both modules config.xml, add <depends>Packagename_ModulenameB</depends> to Packagename_ModulenameA.xml, and wrap classA to if: if (class_exists('ClassB')) {class ClassA extends ClassB {}} else {class ClassA extends CoreClass {}}. Now as you can see, it will work with and without <depends>.Ofay
@Zyava is correct, I'd forgotten about PHP ability to conditionally define a class (which always struck me as a questionable practice, but that's mostly due to taste)Seeley
@AlanStorm I'm following your example perfectly and can't get this to work. If you have any spare time, I would greatly appreciate your help>< magento.stackexchange.com/questions/5143/…Sarge
I added <depends>ModuleA</depends> to etc/modules/ModuleB.xml but the files with classes are loaded in ModuleB, ModuleA order. I didn't get any error, just curious why the order of loading is not as expected?Barnhill

© 2022 - 2024 — McMap. All rights reserved.