Thursday, 4 September 2014

By default every Magento product list page has to modes of view. They are List and Grid. We can set default mode through admin. For this , we need to go to

System  >  Configuration  >  Catalog  >  Frontend  >  List mode
and set default option there. However what about if we need to show different categories in different modes ? Obviously every one think to do this programmatically. But it's somewhat handy. So I decided to drop out programmatic option. So the last option remains there is layout updation. It looks indeed awesome. It will work like a charm.

In Detail

Let us take an example. Suppose we have three categories. They are category 1, category 2 and category 3. We need to show category 2 in grid mode and rest of the categories in list mode.

For this, first create local.xml file.

File: app\design\frontend\[your_package]\[your_theme]\layout\local.xml

 
  
   
    _current_grid_modegrid
   
  
  
   
    _current_grid_modelist
   
  
  
   
    _current_grid_modelist
   
  
 
We are done. Now remove the cache and then load the categories. You see the magic, don't you ? :)

Code in Detail

First I need to tell you about local.xml file. It is a special layout udpation file which will process by Magento after processing every other layout update XML files. Hence this layout updation file is more powerful in literal sense. For an example, for quick layout changes or updations, we can obviously use local.xml file.

product_list_toolbar block is the focused block here. This block holds management of product list modes. This block is defined in app\code\core\Mage\Catalog\Block\Product\List\Toolbar.php. I need your attention on Mage_Catalog_Block_Product_List_Toolbar::getCurrentMode() method. It looks like this

    public function getCurrentMode()
    {
        $mode = $this->_getData('_current_grid_mode');
        if ($mode) {
            return $mode;
        }
        $modes = array_keys($this->_availableMode);
        $defaultMode = current($modes);
        $mode = $this->getRequest()->getParam($this->getModeVarName());
        if ($mode) {
            if ($mode == $defaultMode) {
                Mage::getSingleton('catalog/session')->unsDisplayMode();
            } else {
                $this->_memorizeParam('display_mode', $mode);
            }
        } else {
            $mode = Mage::getSingleton('catalog/session')->getDisplayMode();
        }

        if (!$mode || !isset($this->_availableMode[$mode])) {
            $mode = $defaultMode;
        }
        $this->setData('_current_grid_mode', $mode);
        return $mode;
    }
This function's job is to return the current product list mode. But please note the line just above the return statement. ie

    $this->setData('_current_grid_mode', $mode);
This reveals the fact that, the property _current_grid_mode actually holds the mode information. So this is what we are doing through your layout update. ie

 
  _current_grid_modegrid
 

Note 1:
When I tried

    grid
it didn't work. It reveals the fact that, we cannot use the magic methods of Magento through layout update. Because.. you know experience is the greatest teacher in the world :)

Thanks for go through this. I will catch you later :)

Thursday, 21 August 2014

On 23:21 by Unknown in , ,    4 comments
Static blocks are very useful tools in Magento. We can effectively use them in different situations. They provide us enough power on content management. However I felt it lacks with something. It can show its content only in boring text format. If we are planning to use multiple static blocks in a page, then we will definitely seek for adding css and js for static block contents. However if we are planning to do it in the most effective way, we can't achieve this functionality in one step. For this small (some time big) headache, I decided to make an end. It results in a tiny magento extension Rkt_JsCssforSb.

What it does

This extension functionality is simple. For every static block, you can see a section for put javascript and css. You need to put css and js in those text areas. Rest will do by the extension. You don't need to put script and style html tag enclosures inside text area. This job will effectively carry out by extension. Css that added for static blocks will get append to head section and scripts will go to bottom section in body part.

In deep

Static block are least considered section in magento code wise !. I am telling this because, during the development of this extension, I have found out that, there are no static block specific events available. However cms pages has lot of events available. Also model of static block is somewhat "weak" when compare with other entity models! For example, there is no event prefix available for static blocks. This made me in serious trouble and as a result, I have done a model rewrite during the development of the extension. I know its bad. But I forced to do it. Along with this, I am observing to three events in order to make the work done.

But I personally believe this extension is highly useful. Since it makes static block somewhat "dynamic". We can set css and make static block content look nice and by applying some js , we can make those contents dynamic etc.

I am waiting for your feedbacks about this extension. Currently the extension is available on GITHUB and soon it will available in Magento connect. Hope you will use it.

Useful Links

  • http://stackoverflow.com/questions/17832914/how-do-i-extend-cms-block-on-save-event-of-magento
  • http://magento.stackexchange.com/questions/32324/how-to-check-whether-a-block-exist-based-on-its-type

Friday, 8 August 2014

On 04:55 by Unknown in , ,    No comments

Introduction
----------------------


When I started to do Module Development in Magento, a lot of time I got an error like this

 Fatal error: Class 'Mage_HelperReference_Helper_Data' not found

First time I wonder why Magento generates such an unwanted error, Since I really did't specify any helper anywhere in my code. But as time passes, I found out that, eventhough we don't use any helper assistance explicitly, Magento need its to be defined to do some of its operation. So in order to avoid this error, what we need to do is, define helper class for our Module and declare our helper class. Those two steps are as follows

Location : app/code/local/Namespace/Module/etc/config.xml

 
  
   
    
     Namespace_Module_Helper
    
   
  
 

Location: app/code/local/Namespace/Module/Helper/Data.php


class Namespace_Module_Helper_Data extends Mage_Core_Helper_Abstract{

}

The advantage of defining helper class is three according to my view

1. We can use this helper class almost everywhere in Magento, as helpers has a global view is provided by Magento

2. We can avoid un-necessary error popups as we discussed before

3. We can now call our helper with his alias name. In this case we can call this helper as
Mage::helper('my_helper_alias')


Why I wrote this blog
-----------------------


In recent past, I had a situation where I want to know the alias name for Mage_Sales_Helper_Data. So I looked in config.xml that is defined for Mage_Sales core module. Surprisingly, I couldn't find the helper definition of that class there !!!!

For curiosity I tried this code to know what would be the result

    print_r(get_class(my_helper_alias));

And it gave me this result

Mage_Sales_Helper_Data

So that means, somewhere Magento sets sales alias for Mage_Sales_Helper_Data. If that definition is not in config.xml, where would be it? So let us together find out where it is.

Jump into the situation
------------------------


Let us start with Mage::helper('sales'). We need to see what is inside the method helper() that is defined inside app/Mage.php in order find the mystery behind this. So helper() look like this.

public static function helper($name)

    {

        $registryKey = '_helper/' . $name;

        if (!self::registry($registryKey)) {

            $helperClass = self::getConfig()->getHelperClassName($name);

            self::register($registryKey, new $helperClass);

        }

        return self::registry($registryKey);

    }

In short, what this function does is, it creates an instances if a helper class as per the name that we passed to this function and register that instace global scope.Then it passes this helper instace to us.

Importance of the function :

Registration of our helper class is an important step. This is why all helper classes become globally available in Magento. In magento anything that present in Mage::registry() will be globally accessible for us.

I need your concentration at this point.

if (!self::registry($registryKey)) {

            $helperClass = self::getConfig()->getHelperClassName($name);

            self::register($registryKey, new $helperClass);

        }

Here it first check whether our helper is registered or not. If not it will call Mage_Core_Model_Config::getHelperClassName() method. Most probably it will return an instance of helper to us and then later it will registered in magento registry. So let us have a look on this method.

public function getHelperClassName($helperName)

    {

        if (strpos($helperName, '/') === false) {

            $helperName .= '/data';

        }

        return $this->getGroupedClassName('helper', $helperName);

    }

The purpose of this funciton is so simple, it actually checks whether passed value hold class name for helper. If not it will set helper class name as data. Then it invokes an another method getGroupedClassName() to return the proper class name.

Importance of the function :

The specularity of this function is very important to understand. This is where the mystery term Data came for helper classes. I will explain it little bit. Suppose we are calling our helper class somewhere in magento as like this

Mage::helper('my_helper_alias/helpername');

So magento looks for this alias name in the config tree. Magento generates a huge xml node tree by grabing content in all config.xml files that are available through all available Magento modules. This is what known as config tree. In that config tree, it will look for this helper_alias_name and find out that class that is defined for this alias is Namespace_Module_Helper. So magento got the location. It decides which file to grab from that location according to the second parameter passed. In this case it is helpername. So it will look for this file app/code/Namespace/Module/Helper/Helpername.php. What would be if we didn't specify the helper name via our method?

Mage::helper('my_helper_alias'); 

If the calss is not specified it will assume that class name is Data and hence look for that file. This

*ASSUMPTION

takes place actually in this place.

Now let us have a look on what would getGroupedClassName('helper', 'sales/data') return.

public function getGroupedClassName($groupType, $classId, $groupRootNode=null)

    {

        if (empty($groupRootNode)) {

            $groupRootNode = 'global/'.$groupType.'s';

        }

        $classArr = explode('/', trim($classId));

        $group = $classArr[0];

        $class = !empty($classArr[1]) ? $classArr[1] : null;

        if (isset($this->_classNameCache[$groupRootNode][$group][$class])) {

            return $this->_classNameCache[$groupRootNode][$group][$class];

        }

        $config = $this->_xml->global->{$groupType.'s'}->{$group};

        // First - check maybe the entity class was rewritten

        $className = null;

        if (isset($config->rewrite->$class)) {

            $className = (string)$config->rewrite->$class;

        } else {

            /**

             * Backwards compatibility for pre-MMDB extensions.

             * In MMDB release resource nodes <..._mysql4> were renamed to <..._resource>. So is left

             * to keep name of previously used nodes, that still may be used by non-updated extensions.

             */

            if (isset($config->deprecatedNode)) {

                $deprecatedNode = $config->deprecatedNode;

                $configOld = $this->_xml->global->{$groupType.'s'}->$deprecatedNode;

                if (isset($configOld->rewrite->$class)) {

                    $className = (string) $configOld->rewrite->$class;

                }

            }

        }

        // Second - if entity is not rewritten then use class prefix to form class name

        if (empty($className)) {

            if (!empty($config)) {

                $className = $config->getClassName();

            }

            if (empty($className)) {

                $className = 'mage_'.$group.'_'.$groupType;

            }

            if (!empty($class)) {

                $className .= '_'.$class;

            }

            $className = uc_words($className);

        }

        $this->_classNameCache[$groupRootNode][$group][$class] = $className;

        return $className;

    }

First this function generates 3 values depend upon the values provided. They are group root node, groupname and class name.

group root node :-

This will be the reference to a child node that comes under global node in configuration tree. For helper value will be global/helpers. For models it will be globals/models and so on

Group name :-

This will be a reference to a node that comes under a group root node in configuratio tree.

class name :-

this would be reference to the class name that comes under group in configuratio tree
So after generating those values, this function mainly do 3 things

1. Check whether an entry exist based on group root, group, class in cache of the system. If yes it will retrieve data from there and returns it. This is what happens here
    if (isset($this->_classNameCache[$groupRootNode][$group][$class])) {

            return $this->_classNameCache[$groupRootNode][$group][$class];

        }
2. Check whether any rewrite exist for the class and if it is there, then return that class. In our case, this is not relevant. So I skip this step. < br /> 3. If both of above step fails, now function tries to genearate a class name and then return that class name. This is the important section for us. Because in this section where magento generates helper class name for core modules. So let us look on that portion of code.

    if (empty($className)) {

            if (!empty($config)) {

                $className = $config->getClassName();

            }

            if (empty($className)) {

                $className = 'mage_'.$group.'_'.$groupType;

            }

            if (!empty($class)) {

                $className .= '_'.$class;

            }

            $className = uc_words($className);

        }

        $this->_classNameCache[$groupRootNode][$group][$class] = $className;

        return $className;

Here first it checks is there any class name defintion for the helper requested in configuration tree. For core modules, this condtion fails. That is because core modules do not possess helper class definition in config gile. Now you can see magento generates our class name. ie

            if (empty($className)) {

                $className = 'mage_'.$group.'_'.$groupType; //mage_sales_helper

            }

            if (!empty($class)) {

                $className .= '_'.$class; //mage_sales_helper_data

            }

            $className = uc_words($className); //Mage_Sales_Helper_Data

Now this value passes to helper() in Mage.php. Then it will create an instane of this class and also register this class in registry. Hence on the other side, we will get a helper instace Mage_Sales_Helper_Data.

RESULT
----------


So remember the

helper alias for a core module in Magento will be its module name itself.


For Mage_Sales => sales
For Mage_Catalog => catalog
For Mage_Customer => customer

and so on.

Hope you enjoy this !

Monday, 28 July 2014

Here in this tutorial, I would like to share you how can we set page size for a particular category.

The Page Size is actually a property of a toolbar block that present in prodouct list block. So basically what you need to do is to set page size to a particuar value for toolbar block, when your category get loaded.

For this you can use an observer method. In order to implement it, you need to know the id of category. Here I am taking my reference id as 23. Also for this let us set up a custom module, Programmerrkt_PageSizeSetter.

Code Begins


So first let us activate our module

Location : app/etc/modules/Programmerrkt_PageSizeSetter.xml

 
     
      
             true
             local
         
     
 

Next, Let us configure our module,

Location: app/code/local/Programmerrkt/PageSizeSetter/etc/config.xml

  
     
      
          1.0.0
      
     
     
         
             
                 
                     
                         singleton
                         programmerrkt_pagesizesetter/observer
                         setPageSizeForCategory
                     
                 
             
         
     
     
         
             
                 Programmerrkt_PageSizeSetter_Model
             
         
     
 

As you can see, through our module, we are planning to observe an event controller_action_layout_generate_blocks_after. If we need to change some block properties, then this event would be the perfect and coolest event to listen to. Also you can see we are planning to define a custom method setPageSizeForCategory in our observer. At last, we defined model section for our module inside global node. This is because observer is going to define in our model.

So it is the time to define our observer. So let us do that

Location: app/code/local/Programmerrkt/PageSizeSetter/Model/Observer.php

getAction();
   $fullActionName = $controller->getFullActionName();
   $id = (int)$controller->getRequest()->getParam('id');

         //check whether current page is correspond to our special category. If not, returns
   if($fullActionName == "catalog_category_view" && $id == $this->_categoryId)
   {
    //check whether toolbar block exist or not
    $toolbar =  $controller->getLayout()->getBlock('product_list_toolbar');
    if($toolbar)
    {
     //sets page size to corresponding list mode
     $listMode = $toolbar->getCurrentMode();
           $toolbar = $toolbar->addPagerLimit($listMode , $this->_pageSize);
    }
    
   }

   return;
  }
 }
 

So here in setPageSizeForCategory method, we ensures that we are standing in particular category page. If it is not, the control returns. If the page is the category that we are talking about, we will set page size to the toolbar block of that category.

The method that we use here to set page size is addPagerLimit. It has 3 parameters to pass. First one is list mode. It will be either grid/list. We are passing the current mode of toolbar block to this method, so that we dont need to really worry on list mode confusion. Next parameter is the size of page. Last one is label. Here we dont want to pass any labels. Hence not using it.

You can also see that our observer class has two properties $_categoryId and $_pageSize. First one holds the id of our special category and second one holds page size values. So you need to set these two properties according to your need. This will allow us setPageSizeForCategory untouched.

Drawback
:- There is a small drawback for this approach. The event we are observing here will trigger in every page load. Since the scope our module is limitted to a particular category, most of the time our module will return without doing anythong. I feel it as awkward and that's why I mentioned it here. But common !!! this is also a solution. Isn't it?

Output



If anyone solve this issue in better way, let me know and please share it. :)

Tuesday, 22 July 2014

On 18:56 by Unknown in , ,    1 comment
Some times, it would be great if we can add a collapse/expand functionality in CMS Pages in Magento. A perfect example would be a read more option. When we display very long documents through CMS pages, it would be nice if we shrink the content part to a small section and add a read more link bottom of it. When user clicks on that link, the content will be expanded.

We can achieve this functionality by implementing a custom extension. I will show you the steps that we need to take to achieve this. So our extension should have a name Programmerrkt_AdvancedCms.

First step is activating our module. For this add this file in etc/modules

Location : app/etc/modules/Programmerrkt_AdvancedCms.xml


Next step is to define configuration of our module. Let us do that.

Location : app/code/local/Programmerrkt/AdvancedCms/etc/config.xml


This file tells to Magento that our module has a layout file for frontend section. That's it. Our layout file is the heart of our module. It is going to hold the important parts of our module. Let us define our layout file.

Location : app/design/frontend/<.your_package>/<.your_theme>/layout/programmerrkt_advancedcms.xml


So here we defined layout for cms_page handler. Magento will look for this layout handler whenever a request for CMS page is made. So this handler would be a perfect handler for us. Next we added jquery and readmore.js in header section. Then at last we defined a template file script.phtml for holding our custom javascript code. Note that we included this template at the bottom of page.This is achieved by using the block before_body_end. This will ensure that, our custom code will invoked perfectly.

Now our script.phtml should look like this.

Location : app/design/frontend/<.yourpackag>/<.your_theme>/template/programmerrkt/advancedcms/readmore/script.phtml

	

As you can see readmore() method is called on an element with an id readomore-demo. So it is required that, you need to enclose all the content your cms page inside this id. Demo is shown here.
We are done. Our output will look like this.

So only thing you need to change is edit script.phtml file according to your needs.

Note: Remember you need to add jquery for proper working of this module. If you have jquery installed, then remove the code that add jquery in layout file. Also you need to download readmore.js and has to include it in skin/frontend/<.your_package/<.your_theme>/js/customjs/readmore.js. Similarly add css file in skin/frontend/<.your_package/<.your_theme>/css/customcss/readmore.css. You can use this css file to decorate links appear in your cms page.

Additional Note: readmore.js requires jquery greater than 1.7.0

Sunday, 13 July 2014

On 21:52 by Unknown in ,    2 comments
In this post, I would like to show you how can we get product option values.

For this, let us start with creating a sample product that holds some custom options. Our Product has name Test Product With Option and below I am showing you its custom option that we set through admin



Now its frontend view will look like this.



This is how we can obtain each option value



Here what we have done is, first we load our product that holds custom options. We load the product through its id. So $product holds our product. Now we assigns all options of product to $option. getOptions() will return an array of object that of class Mage_Catalog_Model_Product_Option. Then we are looping through each options and then loads option values by invoking getValues(). This method will also return an array that holds objects of class Mage_Catalog_Model_Product_Option_Value. Each of this object holds complete information of a particular value.

So the output will look like this
Option :custom checkbox
Values:
Array
(
[option_type_id] => 7
[option_id] => 4
[sku] =>
[sort_order] => 0
[default_title] => custom checkbox value 1
[store_title] =>
[title] => custom checkbox value 1
[default_price] => 0.0000
[default_price_type] => fixed
[store_price] =>
[store_price_type] =>
[price] => 0.0000
[price_type] => fixed
)
Array
(
[option_type_id] => 8
[option_id] => 4
[sku] =>
[sort_order] => 0
[default_title] => custom checkbox value 2
[store_title] =>
[title] => custom checkbox value 2
[default_price] => 0.0000
[default_price_type] => fixed
[store_price] =>
[store_price_type] =>
[price] => 0.0000
[price_type] => fixed
)
Option :custom dropdown
Values:
Array
(
[option_type_id] => 9
[option_id] => 5
[sku] =>
[sort_order] => 0
[default_title] => custom option value 1
[store_title] =>
[title] => custom option value 1
[default_price] => 0.0000
[default_price_type] => fixed
[store_price] =>
[store_price_type] =>
[price] => 0.0000
[price_type] => fixed
)
Array
( [option_type_id] => 10
[option_id] => 5
[sku] =>
[sort_order] => 0
[default_title] => custom option value 2
[store_title] =>
[title] => custom option value 2
[default_price] => 0.0000
[default_price_type] => fixed
[store_price] =>
[store_price_type] =>
[price] => 0.0000
[price_type] => fixed
)
Array
(
[option_type_id] => 11
[option_id] => 5
[sku] =>
[sort_order] => 0
[default_title] => custom option value 3
[store_title] =>
[title] => custom option value 3
[default_price] => 0.0000
[default_price_type] => fixed
[store_price] =>
[store_price_type] =>
[price] => 0.0000
[price_type] => fixed
)
Option :Custom Text Field
Values:

Thus getting option values of custom option of a product is pretty straight forward and easy to understand.

Thursday, 26 June 2014

On 12:06 by Unknown in , ,    No comments
The main reason, in fact the inspiration behind this blog is this THREAD. In this thread, the user want to add layout that render by controller B by using controller A along with its layouts. For those who didn't understand the thread, I will explain the situation little bit.
So I have a module Programmerrkt_Checkout, which is used to provide two checkout views for my site. So in my module there are two controllers. Let us call them as Controller A and Controller B. Whenever url request these two controllers, it will show its own layouts in frontend. Let us create our module. Our config file should look like this

config.xml

Location:app/code/local/Programmerrkt/Checkout/etc/config.xml

As you can see, our config file do mainly 2 things here
  • Controller Rewrite
  • Layout updation
Controller Rewrite :-
It rewrites Mage_Checkout's CartController. This is because Our module is intended to provide two views for checkout/cart
Layout Updation :-
We defined our layout xml file here. It is using to hold the layouts of our controllers.

programmerrkt_checkout.xml

Location:app/code/design/frontend/base/default/layout/programmerrkt_checkout.xml

Here checkout_a_index is the layout handler for controller A. Similarly checkout_b_index is the layout handler for controller B. When www.yoursite.com/index.php/checkout/a loads it will loads content inside in checkout_a_index handler. When www.yoursite.com/index.php/checkout/b loads, it will load content that reside under checkout_b_index handler.
Now how these handlers are loading for A and B view. This is achieved through controllers. Let us look code inside our controllers

Controller B

Location:app/code/local/Programmerrkt/Checkout/controllers/BController.php

This is our controller B. As you can see it call load and render layouts. This will render content comes inside in checkout_b_index handler in its content section. (There are other layout handlers that are loading while do this. An example is default handler).

Controller A

Location:app/code/local/Programmerrkt/Checkout/controllers/AController.php
As I already stated it will render blocks that comes under checkout_a_index handler.

View

From the layout files, you can see that we are setting two templates to set view for A and B. Let me show the content in this file

a.phtml

Location:app/design/frontend/base/default/template/programmerrkt/checkout/a.phtml

b.phtml

Location:app/design/frontend/base/default/template/programmerrkt/checkout/b.phtml
Now load the url www.yourdomain.com/index.php/checkout/a, you will see following output

I am Controller A

I am content of A controller and I lies is layout definition for A controller
Similarly load the url www.yourdomain.com/index.php/checkout/b, you will see following output

I am Controller B

I am content of B controller and I lies is layout definition for B controller

The Real Problem

Now everything is perfect. Here the problem begins. Now we want to load the layout content of controller B, when url request for controller A. ie when www.yourdomain.com/index.php/checkout/a is requested, it should provide following output

I am Controller A

I am content of A controller and I lies is layout definition for A controller

I am Controller B

I am content of B controller and I lies is layout definition for B controller
How can we accomplish that ? Well, in my mind there are two awesome methods that we can depend on. They are
  • Use an event Observer to add layout handler of controller B to controller A
  • Make changes to codes in Controller A in such a way that it will load layout of controller B also
Using Controller
We can achieve what we want here by changing our controller A code like this

Controller A

Location:app/code/local/Programmerrkt/Checkout/controllers/AController.php
Here what we are done is `manual coding` and replacing of loadLayout() . Why we are doing loading of layout manually? The answer is, if we add $update->addHandle('checkout_b_index') before or after of loadLayout(), it does not make any effect on output. The reason is well explained by alanstorm in this THREAD.
Take a look on loadLayout() definition.

Action.php

Location:app/code/core/Mage/Core/controller/Varien/Action.php
This code reveals the fact that, in your controller we are actually using the same method that is used by default loadLayout() method,except for the addition of of a custom handle before calling loadLayoutUpdates(). (!important).
That's it. We achieved the desired result through controller. Now let us move on to our second approach. That is observer method
Event Observation
This is the most understandable method. Here we are observing for an event `controller_action_layout_load_before`. Add this code to config.xml

Config.xml

Location:app/code/local/Programmerrkt/Checkout/etc/config.xml
Add this code to observer

Observer.php

Location:app/code/local/Programmerrkt/Checkout/Model/Observer.php
when this event is listening, according to my understanding, magento resides in between `$this->addActionLayoutHandles()` and `$this->loadLayoutUpdates()`. That means it already loaded default, store, theme and action handles. So we are in the perfect position. We can add layout handle of controller B, if the current action is `checkout_a_index` (I used `if` condition for check that.). Note: This is a good tutorial that depicts this observer calling LINK
Now go and load url www.yourdomain.com/index.php/checkout/a. You can see the desired output in your page. :)