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?

  • 2
    I like the approach, thought a while about it and couldn't see any problems with it. – Fabian Schmengler Dec 14 '15 at 15:25
  • @fschmengler thanks for your comment. I am going to stick with this pattern unless I find and feel something better. – MagePsycho Dec 14 '15 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 Jan 21 '16 at 0:55
  • @MaxPronko $someConditionToShowDropdown is based on system settings where it says Choose Field Type: Dropdown | Checkbox – MagePsycho Feb 18 '16 at 20:51
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.

|improve this answer|||||
  • Awesome. I am gonna try this approach and let you know the feedback. Thanks a lot for your input! – MagePsycho Feb 18 '16 at 20:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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