4

I am displaying a customer attribute in frontend as per the configuration settings (the attribute can be displayed as dropdown, checkbox etc.)

I have used block class for the purpose so that I can use the same template file in different pages (for example: register, checkout pages etc.).

The block class structure looks like:

MagePsycho/Customer/Block/Widget/Abstract.php
MagePsycho/Customer/Block/Widget/Type/Dropdown.php
MagePsycho/Customer/Block/Widget/Type/Checkbox.php
MagePsycho/Customer/Block/Widget/Type.php

To display the attribute, you have to include the following code in .phtml:

<?php echo $this->getLayout()->createBlock('magepsycho_customer/widget_type')->setObject($this->getFormData())->toHtml() ?>

From this you can assume that Type.php acts as a pseudo factory class for generating the required attribute type:

class MagePsycho_Customer_Block_Widget_Type extends MagePsycho_Customer_Block_Widget_Abstract
{
    public function _toHtml()
    {
        if ($someConditionToShowDropdown) {
            return $this->getLayout()->createBlock('magepsycho_customer/widget_type_dropdown')->setObject($this->getObject())->toHtml();
        } else if ($someConditionToShowCheckbox) {
            return $this->getLayout()->createBlock('magepsycho_customer/widget_type_checkbox')->setObject($this->getObject())->toHtml();
        }
        return '';
    }
}

I could have added the presentation logic of _toHtml() in .phtml but to make it look cleaner, I decided to move it to some generic block class Type.php.

My Question here is, what is the better way to generate the block class/template based on conditions. Is the way I am doing is correct? Any Suggestions?

4
  • 2
    I like the approach, thought a while about it and couldn't see any problems with it. Commented Dec 14, 2015 at 15:25
  • @fschmengler thanks for your comment. I am going to stick with this pattern unless I find and feel something better.
    – MagePsycho
    Commented Dec 14, 2015 at 17:02
  • @MagePsycho where do you get conditions for block generation? I see $someConditionToShowDropdown variable however it is not clear where do you get it originally. Do you mind share this info? Maybe i'd suggest something
    – Max Pronko
    Commented Jan 21, 2016 at 0:55
  • @MaxPronko $someConditionToShowDropdown is based on system settings where it says Choose Field Type: Dropdown | Checkbox
    – MagePsycho
    Commented Feb 18, 2016 at 20:51

1 Answer 1

6

Idea of using a factory block is not bad in general, but it is better not to create blocks directly in the templates. It might give an incredible pain for any other developer who does not know your code structure. Also, it gives very low flexibility for any feature customization.

My approach would look like the following:

  1. Create a type block in custom made layout handle with meaningful name. For instance: <magepsyho_amazingmodule_init_widgets>

  2. Assign each of the widget types as a child block

  3. Add your layout handle via observer on before load layout (if your functionality can be enabled / disabled per page) or include your handle via layout <handle /> tag on needed pages.

  4. Use own helper to retrieve your type block and invoke its rendering method

  5. Profit!

Here is a layout sample that might be useful for you:

<layout>
    <magepsyho_amazingmodule_init_widgets>
        <block type="magepsycho_customer/widget_type" name="magepsycho_customer.widget.renderer">
             <block type="magepsycho_customer/widget_type_checkbox" template="...." name="magepsycho_customer.widget.renderer.checkbox" as="checkbox" />
             <block type="magepsycho_customer/widget_type_radio" template="...." name="magepsycho_customer.widget.renderer.radio" as="radio" />
             <!-- ... and so on-->
        </block>
    </magepsyho_amazingmodule_init_widgets>
</layout>

In the block I would do the following:

class MagePsycho_Customer_Block_Widget_Type extends MagePsycho_Customer_Block_Widget_Abstract
{
    public function _toHtml()
    {
        $yourCoolObject = $this->getObject();
        if ($yourCoolObject && ($childBlock = $this->getChild($yourCoolObject->getRendererType())) {
            return $childBlock->setObject($yourCoolObject)->toHtml();
        }
        return '';
    }
}

And your helper might look like this:

class MagePsycho_Customer_Helper_Data extends Mage_Core_Helper_Abstract
{
     public function renderObject($object)
     {
         if ($renderer = $this->getLayout()->getBlock('magepsycho_customer.widget.renderer')) {
             return $renderer->setObject($object)->toHtml();
         }

         return '';
     }
}

Also, your layout inclusion might look as simple as that:

<layout>
    <customer_address_form>
        <update handle="magepsyho_amazingmodule_init_widgets" />
    </customer_address_form>
</layout>

And in the template as simple as this:

<?php echo $this->helper('magepsycho_customer')->renderObject($object);?>

What benefits does it give?

  1. You could change templates depending on theme

  2. Anyone could add new renderers without changing your templates

  3. It is easy to keep track of created blocks, and you can see it by searching over layout files, seeing it in the commerce bug, etc.

  4. You don't have overhead of creating a block for every render operation, you create it only once for the whole page.

1
  • Awesome. I am gonna try this approach and let you know the feedback. Thanks a lot for your input!
    – MagePsycho
    Commented Feb 18, 2016 at 20:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.