Magento – Going from Admin Panel to Source Code and finding out what is running under the hood

codecode analysisPHP

I have a simple example. Say my system is messed up and I want to disable the cache
manually because my admin panel is not working. How do I go from figuring out what
component is run, and what php I should manually run to figure out the task.

What are the steps, from A to Z?

Thanks.

Best Answer

Here are the steps, in order, to follow through the process of a Magento route to internals:

  • The admin panel runs from a module called Mage_Adminhtml. You'll probably want to start there.
  • In Mage/Adminhtml/etc/config.xml there is a route definition. That route has a frontName. This frontName is the equivalent to the first part of your URL in Magento. For instance, /customer/account/login references the customer route, the AccountController.php and the method in that controller called loginAction.
  • Take a look in your URL bar - you'll see this for the cache page: /admin/cache/index/key/2030239fjaosjdg0a9sjd09guad0gua0s9dgu0a9sudg. Admin is the route (as we discovered in the last step). Cache is the next part, so we'll look in the CacheController.php file. And then we see index, which means we should look in the indexAction method.

Here's what the indexAction looks like (1.8CE/1.13EE):

/**
 * Display cache management grid
 */
public function indexAction()
{
    $this->_title($this->__('System'))->_title($this->__('Cache Management'));

    $this->loadLayout()
        ->_setActiveMenu('system/cache')
        ->renderLayout();
}

So it's loading a layout and setting system/cache as the active menu. Let's find where the layout is being rendered. Unfortunately - and you'll just have to know this - originally Magento had some of the admin concerns in Mage_Admin, which defines a file called admin.xml in the adminhtml layout folder:

└── app
    └── design
        └── adminhtml
            └── default
                └── default
                    └── layout
                        └── admin.xml

Therein you'll see the layout definition for the route in question. All future changes to layouts will be defined within the adminhtml route or in a custom module adding the admin layout files. Here is that definition:

<adminhtml_cache_index>
    <reference name="content">
        <block type="adminhtml/cache" name="cache"></block>
        <block type="adminhtml/cache_additional" name="cache.additional" template="system/cache/additional.phtml"></block>
    </reference>
</adminhtml_cache_index>

So now we see two block types to review and a phtml template file to take a look at.

Firstly the adminhtml/cache. The shortcode for the block was set in the etc/config.xml under <global> and that maps to Mage_Adminhtml_Block. That means we should look at Mage_Adminhtml_Block_Cache:

class Mage_Adminhtml_Block_Cache extends Mage_Adminhtml_Block_Widget_Grid_Container
{
    /**
     * Class constructor
     */
    public function __construct()
    {
        $this->_controller = 'cache';
        $this->_headerText = Mage::helper('core')->__('Cache Storage Management');
        parent::__construct();
        $this->_removeButton('add');
        $this->_addButton('flush_magento', array(
            'label'     => Mage::helper('core')->__('Flush Magento Cache'),
            'onclick'   => 'setLocation(\'' . $this->getFlushSystemUrl() .'\')',
            'class'     => 'delete',
        ));

        $message = Mage::helper('core')->__('Cache storage may contain additional data. Are you sure that you want flush it?');
        $this->_addButton('flush_system', array(
            'label'     => Mage::helper('core')->__('Flush Cache Storage'),
            'onclick'   => 'confirmSetLocation(\''.$message.'\', \'' . $this->getFlushStorageUrl() .'\')',
            'class'     => 'delete',
        ));
    }

    /**
     * Get url for clean cache storage
     */
    public function getFlushStorageUrl()
    {
        return $this->getUrl('*/*/flushAll');
    }

    /**
     * Get url for clean cache storage
     */
    public function getFlushSystemUrl()
    {
        return $this->getUrl('*/*/flushSystem');
    }
}

Bingo. That's information that we can work with. Things are being set like page title. Notice this little tidbit:

    $this->_controller = 'cache';

That is Magento's way of saying "I have a grid that should be rendered and it's located in the cache folder under the block definitions. I know. It's strange. They throw together the grid container on the fly and set it as a child block. They expect it to always be called Grid.php. This is then located within Mage_Adminhtml_Block_Cache_Grid:

class Mage_Adminhtml_Block_Cache_Grid extends Mage_Adminhtml_Block_Widget_Grid
{
    protected $_invalidatedTypes = array();
    /**
     * Class constructor
     */
    public function __construct()
    {
        parent::__construct();
        $this->setId('cache_grid');
        $this->_filterVisibility = false;
        $this->_pagerVisibility  = false;
        $this->_invalidatedTypes = Mage::app()->getCacheInstance()->getInvalidatedTypes();
    }

    /**
     * Prepare grid collection
     */
    protected function _prepareCollection()
    {
        $collection = new Varien_Data_Collection();
        foreach (Mage::app()->getCacheInstance()->getTypes() as $type) {
            $collection->addItem($type);
        }
        $this->setCollection($collection);
        return parent::_prepareCollection();
    }

    /**
     * Add name and description to collection elements
     */
    protected function _afterLoadCollection()
    {
        foreach ($this->_collection as $item) {
        }
        return $this;
    }

    /**
     * Prepare grid columns
     */
    protected function _prepareColumns()
    {
        $baseUrl = $this->getUrl();
        $this->addColumn('cache_type', array(
            'header'    => $this->__('Cache Type'),
            'width'     => '180',
            'align'     => 'left',
            'index'     => 'cache_type',
            'sortable'  => false,
        ));

        $this->addColumn('description', array(
            'header'    => $this->__('Description'),
            'align'     => 'left',
            'index'     => 'description',
            'sortable'  => false,
        ));

        $this->addColumn('tags', array(
            'header'    => $this->__('Associated Tags'),
            'align'     => 'left',
            'index'     => 'tags',
            'width'     => '180',
            'sortable'  => false,
        ));

        $this->addColumn('status', array(
            'header'    => $this->__('Status'),
            'width'     => '120',
            'align'     => 'left',
            'index'     => 'status',
            'type'      => 'options',
            'options'   => array(0 => $this->__('Disabled'), 1 => $this->__('Enabled')),
            'frame_callback' => array($this, 'decorateStatus')
        ));

//        $this->addColumn('action',
//            array(
//                'header'    =>  $this->__('Action'),
//                'width'     => '100',
//                'type'      => 'action',
//                'getter'    => 'getId',
//                'actions'   => array(
//                    array(
//                        'caption'   => $this->__('Refresh'),
//                        'url'       => array('base'=> '*/*/refresh'),
//                        'field'     => 'type'
//                    ),
//                ),
//                'filter'    => false,
//                'sortable'  => false,
//                'is_system' => true,
//        ));

        return parent::_prepareColumns();
    }

    /**
     * Decorate status column values
     *
     * @return string
     */
    public function decorateStatus($value, $row, $column, $isExport)
    {
        $class = '';
        if (isset($this->_invalidatedTypes[$row->getId()])) {
            $cell = '<span class="grid-severity-minor"><span>'.$this->__('Invalidated').'</span></span>';
        } else {
            if ($row->getStatus()) {
                $cell = '<span class="grid-severity-notice"><span>'.$value.'</span></span>';
            } else {
                $cell = '<span class="grid-severity-critical"><span>'.$value.'</span></span>';
            }
        }
        return $cell;
    }

    /**
     * Get row edit url
     *
     * @return string
     */
    public function getRowUrl($row)
    {
        return false;
        //return $this->getUrl('*/*/edit', array('type'=>$row->getId()));
    }

    /**
     * Add mass-actions to grid
     */
    protected function _prepareMassaction()
    {
        $this->setMassactionIdField('id');
        $this->getMassactionBlock()->setFormFieldName('types');

        $modeOptions = Mage::getModel('index/process')->getModesOptions();

        $this->getMassactionBlock()->addItem('enable', array(
            'label'         => Mage::helper('index')->__('Enable'),
            'url'           => $this->getUrl('*/*/massEnable'),
        ));
        $this->getMassactionBlock()->addItem('disable', array(
            'label'    => Mage::helper('index')->__('Disable'),
            'url'      => $this->getUrl('*/*/massDisable'),
        ));
        $this->getMassactionBlock()->addItem('refresh', array(
            'label'    => Mage::helper('index')->__('Refresh'),
            'url'      => $this->getUrl('*/*/massRefresh'),
            'selected' => true,
        ));

        return $this;
    }
}

If you've made it this far, congratulations. You deserve a medal. In that grid block we see a few things happening:

  • Set up of the mass action dropdown and what should happen when they're submitted
  • Set up of the collection for the grid
  • Set up of the columns for the grid to display from the collection

And that's it! As homework, explore what the layout definition for admin/cache_additional!

Related Topic