Magento has unique functionality that will allow you to configure a package and theme for your store. Magento checks these values configured when firstly running the preDispatch()
method.
public function preDispatch() { if (!$this->getFlag('', self::FLAG_NO_CHECK_INSTALLATION)) { if (!Mage::isInstalled()) { $this->setFlag('', self::FLAG_NO_DISPATCH, true); $this->_redirect('install'); return; } } // Prohibit disabled store actions if (Mage::isInstalled() && !Mage::app()->getStore()->getIsActive()) { Mage::app()->throwStoreException(); } if ($this->_rewrite()) { return; } if (!$this->getFlag('', self::FLAG_NO_START_SESSION)) { $checkCookie = in_array($this->getRequest()->getActionName(), $this->_cookieCheckActions) && !$this->getRequest()->getParam('nocookie', false); $cookies = Mage::getSingleton('core/cookie')->get(); /** @var $session Mage_Core_Model_Session */ $session = Mage::getSingleton('core/session', array('name' => $this->_sessionNamespace))->start(); if (empty($cookies)) { if ($session->getCookieShouldBeReceived()) { $this->setFlag('', self::FLAG_NO_COOKIES_REDIRECT, true); $session->unsCookieShouldBeReceived(); $session->setSkipSessionIdFlag(true); } elseif ($checkCookie) { if (isset($_GET[$session->getSessionIdQueryParam()]) && Mage::app()->getUseSessionInUrl() && $this->_sessionNamespace != Mage_Adminhtml_Controller_Action::SESSION_NAMESPACE ) { $session->setCookieShouldBeReceived(true); } else { $this->setFlag('', self::FLAG_NO_COOKIES_REDIRECT, true); } } } } Mage::app()->loadArea($this->getLayout()->getArea()); .... }
The key line to look at here is:
Mage::app()->loadArea($this->getLayout()->getArea());
The loadArea()
method can be seen below.
public function loadArea($code) { $this->getArea($code)->load(); return $this; }
The load()
method will, if no argument has been specified, load the config, events, design and translate parts of the system via the _loadPart()
method.
public function load($part=null) { if (is_null($part)) { $this->_loadPart(self::PART_CONFIG) ->_loadPart(self::PART_EVENTS) ->_loadPart(self::PART_DESIGN) ->_loadPart(self::PART_TRANSLATE); } else { $this->_loadPart($part); } return $this; }
protected function _loadPart($part) { if (isset($this->_loadedParts[$part])) { return $this; } Varien_Profiler::start('mage::dispatch::controller::action::predispatch::load_area::'.$this->_code.'::'.$part); switch ($part) { case self::PART_CONFIG: $this->_initConfig(); break; case self::PART_EVENTS: $this->_initEvents(); break; case self::PART_TRANSLATE: $this->_initTranslate(); break; case self::PART_DESIGN: $this->_initDesign(); break; } $this->_loadedParts[$part] = true; Varien_Profiler::stop('mage::dispatch::controller::action::predispatch::load_area::'.$this->_code.'::'.$part); return $this; }
The _initDesign()
method instantiates a singleton of the Mage_Core_Model_Design_Package
class.
protected function _initDesign() { if (Mage::app()->getRequest()->isStraight()) { return $this; } $designPackage = Mage::getSingleton('core/design_package'); if ($designPackage->getArea() != self::AREA_FRONTEND) return; $currentStore = Mage::app()->getStore()->getStoreId(); $designChange = Mage::getSingleton('core/design') ->loadChange($currentStore); if ($designChange->getData()) { $designPackage->setPackageName($designChange->getPackage()) ->setTheme($designChange->getTheme()); } }
The __construct()
magic method of the Mage_Core_Model_Design
config checks to see if there is a theme.xml
file present in a theme’s etc
directory.
If there is, Magento loads the contents of the file into a new config object. This will come in use for later when Magento checks the fallback scheme.
public function __construct(array $params = array()) { if (isset($params['designRoot'])) { if (!is_dir($params['designRoot'])) { throw new Mage_Core_Exception("Design root '{$params['designRoot']}' isn't a directory."); } $this->_designRoot = $params['designRoot']; } else { $this->_designRoot = Mage::getBaseDir('design'); } $this->_cacheChecksum = null; $this->setCacheId('config_theme'); $this->setCache(Mage::app()->getCache()); if (!$this->loadCache()) { $this->loadString('<theme />'); $path = str_replace('/', DS, $this->_designRoot . '/*/*/*/etc/theme.xml'); $files = glob($path); foreach ($files as $file) { $config = new Varien_Simplexml_Config(); $config->loadFile($file); list($area, $package, $theme) = $this->_getThemePathSegments($file); $this->setNode($area . '/' . $package . '/' . $theme, null); $this->getNode($area . '/' . $package . '/' . $theme)->extend($config->getNode()); } $this->saveCache(); } }
When Magento gets the list of layout and template files to render, it runs through the getFileName()
method.
public function getFilename($file, array $params) { Varien_Profiler::start(__METHOD__); $this->updateParamDefaults($params); $result = $this->_fallback( $file, $params, $this->_fallback->getFallbackScheme( $params['_area'], $params['_package'], $params['_theme'] ) ); Varien_Profiler::stop(__METHOD__); return $result; }
Here we are storing the result of the _fallBack()
method into a $result
variable. _fallBack()
takes three arguments.
getFallbackScheme()
method.The getFallbackScheme()
method is as follows.
public function getFallbackScheme($area, $package, $theme) { $cacheKey = $area . '/' . $package . '/' . $theme; if (!isset($this->_cachedSchemes[$cacheKey])) { if ($this->_isInheritanceDefined($area, $package, $theme)) { $scheme = $this->_getFallbackScheme($area, $package, $theme); } else { $scheme = $this->_getLegacyFallbackScheme(); } $this->_cachedSchemes[$cacheKey] = $scheme; } return $this->_cachedSchemes[$cacheKey]; }
So there is an if statement using the _isInheritanceDefined()
method. If it returns true, we return a $scheme
using the _getFallbackScheme()
method. If it returns false, we return a $scheme
of _getLegacyFallbackScheme()
.
The difference between the two schemes is that the legacy fallback scheme was the scheme prior to Magento Comunity/Open Source version 1.9 and Enterprise/Commerce version 1.13 whereby the fallback scheme would follow the convention below.
We can see this in the _isInheritanceDefined()
method.
protected function _isInheritanceDefined($area, $package, $theme) { $path = $area . '/' . $package . '/' . $theme . '/parent'; return $this->_config->getNode($path) !== false; }
So it’s here where the nodes are checked within the theme.xml
file.
protected function _getFallbackScheme($area, $package, $theme) { $scheme = array(array()); $this->_visited = array(); while ($parent = (string)$this->_config->getNode($area . '/' . $package . '/' . $theme . '/parent')) { $this->_checkVisited($area, $package, $theme); $parts = explode('/', $parent); if (count($parts) !== 2) { throw new Mage_Core_Exception('Parent node should be defined as "package/theme"'); } list($package, $theme) = $parts; $scheme[] = array('_package' => $package, '_theme' => $theme); } return $scheme; }
protected function _getLegacyFallbackScheme() { return array( array(), array('_theme' => $this->_getFallbackTheme()), array('_theme' => Mage_Core_Model_Design_Package::DEFAULT_THEME),
The _getFallbackTheme()
method will fetch the value saved in the admin area under System -> Configuration -> Design
, under the Theme
section in the default
field.
protected function _getFallbackTheme() { return $this->getStore()->getConfig('design/theme/default'); }
And the _fallBack()
method validates the file and sets the package and theme to base
and default
respectively.
protected function _fallback($file, array &$params, array $fallbackScheme = array(array())) { if ($this->_shouldFallback) { foreach ($fallbackScheme as $try) { $params = array_merge($params, $try); $filename = $this->validateFile($file, $params); if ($filename) { return $filename; } } $params['_package'] = self::BASE_PACKAGE; $params['_theme'] = self::DEFAULT_THEME; } return $this->_renderFilename($file, $params); }
Note: This article is based on Magento Community/Open Source version 1.9.