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.