标签归档:页面构建

Magento页面构建:page_html_header分析

<div class="header-container">
    <div class="header">
        <?php if ($this->getIsHomePage()):?>
        <h1 class="logo"><strong><?php echo $this->getLogoAlt() ?></strong><a href="<?php echo $this->getUrl('') ?>" title="<?php echo $this->getLogoAlt() ?>" class="logo"><img src="<?php echo $this->getLogoSrc() ?>" alt="<?php echo $this->getLogoAlt() ?>" /></a></h1>
        <?php else:?>
        <a href="<?php echo $this->getUrl('') ?>" title="<?php echo $this->getLogoAlt() ?>" class="logo"><strong><?php echo $this->getLogoAlt() ?></strong><img src="<?php echo $this->getLogoSrc() ?>" alt="<?php echo $this->getLogoAlt() ?>" /></a>
        <?php endif?>
        <div class="quick-access">
            <?php echo $this->getChildHtml('topSearch') ?>
            <p class="welcome-msg"><?php echo $this->getWelcome() ?> <?php echo $this->getAdditionalHtml() ?></p>
            <?php echo $this->getChildHtml('topLinks') ?>
            <?php echo $this->getChildHtml('store_language') ?>
        </div>
        <?php echo $this->getChildHtml('topContainer'); ?>
    </div>
</div>
<?php echo $this->getChildHtml('topMenu') ?>

这个模板上涉及到的方法:

class Mage_Page_Block_Html_Header extends Mage_Core_Block_Template
{
    public function _construct()
    {
        $this->setTemplate('page/html/header.phtml');
    }

    /**
     * Check if current url is url for home page
     *
     * @return true
     */
    public function getIsHomePage()
{
		//getUrl('')返回首页连接
        return $this->getUrl('') == $this->getUrl('*/*/*', array('_current'=>true, '_use_rewrite'=>true));
    }

    public function setLogo($logo_src, $logo_alt)
{
		//PHP的奇葩,_data['logo_src']= $logo_src
        $this->setLogoSrc($logo_src);
        $this->setLogoAlt($logo_alt);
        return $this;
    }

    public function getLogoSrc()
    {
        if (empty($this->_data['logo_src'])) {
            $this->_data['logo_src'] = Mage::getStoreConfig('design/header/logo_src');
        }
		//获取配置名,然后到皮肤文件夹中寻找
        return $this->getSkinUrl($this->_data['logo_src']);
    }

    public function getLogoAlt()
    {
        if (empty($this->_data['logo_alt'])) {
            $this->_data['logo_alt'] = Mage::getStoreConfig('design/header/logo_alt');
        }
        return $this->_data['logo_alt'];
    }

    public function getWelcome()
    {
        if (empty($this->_data['welcome'])) {
			//如果已经登录
            if (Mage::isInstalled() && Mage::getSingleton('customer/session')->isLoggedIn()) {
                $this->_data['welcome'] = $this->__('Welcome, %s!', $this->escapeHtml(Mage::getSingleton('customer/session')->getCustomer()->getName()));
            } else {
                $this->_data['welcome'] = Mage::getStoreConfig('design/header/welcome');
            }
        }

        return $this->_data['welcome'];
    }
}

这里对应了后台的三个设置:(System->Configuration->Design->Header)
Magento头部设置

可以给定Logo图片,图片的Alt属性,已经显示的Welcome文本。

getUrl(”)是从父类继承过来的,它可以获取首页连接。

1 子块topSearch

<reference name="header">
<block type="core/template" name="top.search" as="topSearch" template="catalogsearch/form.mini.phtml"/>
</reference>

这个搜索块的类型是core/template,那不就是指Mage_Core_Block_Template吗,而这个类一般作为块的父类,但是不一定都去继承它。这个toSearch模板事实是使用到Mage_Catalogsearch_Helper_Data助手类,但是模板是必须对应一个块的,所有就有了这个core/template作为基础类型。

2 子块topLinks

<default>
   <reference name="top.links">
      <action method="addLink" translate="label title" module="customer">
         <label>My Account</label>
         <url helper="customer/getAccountUrl"/>
         <title>My Account</title>
         <prepare/>
         <urlParams/>
         <position>10</position>
      </action>
   </reference>
</default>

<customer_logged_in>
   <reference name="top.links">
      <action method="addLink" translate="label title" module="customer">
         <label>Log Out</label>
         <url helper="customer/getLogoutUrl"/>
         <title>Log Out</title>
         <prepare/>
         <urlParams/>
         <position>100</position>
      </action>
   </reference>
</customer_logged_in>

<customer_logged_out>
   <reference name="top.links">
      <action method="addLink" translate="label title" module="customer">
         <label>Log In</label>
         <url helper="customer/getLoginUrl"/>
         <title>Log In</title>
         <prepare/>
         <urlParams/>
         <position>100</position>
      </action>
   </reference>
   <remove name="reorder"/>
</customer_logged_out>

<default>
   <reference name="top.links">
      <block type="checkout/links" name="checkout_cart_link">
         <action method="addCartLink"/>
         <action method="addCheckoutLink"/>
      </block>
   </reference>
</default>

<default>
   <reference name="top.links">
      <block type="wishlist/links" name="wishlist_link"/>
      <action method="addLinkBlock">
         <blockName>wishlist_link</blockName>
      </action>
   </reference>
</default>

以上是从包布局中找出来的针对top.links的操作。可以看到,在default句柄中,执行了四个方法。还有customer_logged_in 和 customer_logged_out就让人困惑了。可以说,这两个句柄完全是为了配合客户是否已经登录对布局做不同的修改而添加的,如果客户已经登录,句柄customer_logged_in总是会被添加,如果没有登录customer_logged_out就总是被添加。

根据这个状态,应用不同的布局。

这个块的类型是page/template_links,对应的类是Mage_Page_Templete_Links,看看构造函数:

    protected function _construct()
    {
        $this->setTemplate('page/template/links.phtml');
} 

它使用了page/template/links.phtml作为模板文件:

<?php $_links = $this->getLinks(); ?>
<?php if(count($_links)>0): ?>
<ul class="links"<?php if($this->getName()): ?> id="<?php echo $this->getName() ?>"<?php endif;?>>
    <?php foreach($_links as $_link): ?>
        <?php if ($_link instanceof Mage_Core_Block_Abstract):?>
            <?php echo $_link->toHtml() ?>
        <?php else: ?>
            <li<?php if($_link->getIsFirst()||$_link->getIsLast()): ?> class="<?php if($_link->getIsFirst()): ?>first<?php endif; ?><?php if($_link->getIsLast()): ?> last<?php endif; ?>"<?php endif; ?> <?php echo $_link->getLiParams() ?>><?php echo $_link->getBeforeText() ?><a href="<?php echo $_link->getUrl() ?>" title="<?php echo $_link->getTitle() ?>" <?php echo $_link->getAParams() ?>><?php echo $_link->getLabel() ?></a><?php echo $_link->getAfterText() ?></li>
        <?php endif;?>
    <?php endforeach; ?>
</ul>
<?php endif; ?>

这段代码很简单,获取所有连接,然后循环。对于每个_link,都可以是Mage_Core_Block_Abstract类型的Block。 仔细看下,有些是执行了addLink有些是执行了addLinkBlock,如果是addLinkBlock就是添加了一个Mage_Core_Block_Abstract类型的Block,那么输出时只要直接调用toHtml()就可以。

不过这里的addLink可比较有学问了:

    public function addLink($label, $url='', $title='', $prepare=false, $urlParams=array(),
        $position=null, $liParams=null, $aParams=null, $beforeText='', $afterText='')
    {
        if (is_null($label) || false===$label) {
            return $this;
        }
        $link = new Varien_Object(array(
            'label'         => $label,
            'url'           => ($prepare ? $this->getUrl($url, (is_array($urlParams) ? $urlParams : array())) : $url),
            'title'         => $title,
            'li_params'     => $this->_prepareParams($liParams),
            'a_params'      => $this->_prepareParams($aParams),
            'before_text'   => $beforeText,
            'after_text'    => $afterText,
        ));

        $this->_links[$this->_getNewPosition($position)] = $link;
        if (intval($position) > 0) {
             ksort($this->_links);
        }

        return $this;
    }

仔细对照如下代码就能明白这里的参数的作用:

<li<?php if($_link->getIsFirst()||$_link->getIsLast()): ?> class="<?php if($_link->getIsFirst()): ?>first<?php endif; ?><?php if($_link->getIsLast()): ?> last<?php endif; ?>"<?php endif; ?> <?php echo $_link->getLiParams() ?>><?php echo $_link->getBeforeText() ?><a href="<?php echo $_link->getUrl() ?>" title="<?php echo $_link->getTitle() ?>" <?php echo $_link->getAParams() ?>><?php echo $_link->getLabel() ?></a><?php echo $_link->getAfterText() ?></li>

LiParams表示在li标签中添加的属性。BeforeText表示在a标签之前输出的内容。Url资源就是a标签的链接了。Title对应a标签的title属性。AParams对应a标签的属性。Label就是a标签的描文本(必须)。AfterText就是a标签之后要输出的内容。
3 store_language

4 catalog.topnav

<?php $_menu = $this->getHtml('level-top')?>
<?php if($_menu): ?>
<div class="nav-container">
    <ul id="nav">
        <?php echo $_menu ?>
    </ul>
</div>
<?php endif ?>

这个目录的输出看起来非常简单,但是如果看看它的实现就比较复杂了。

5 top.container
这个块作为header的最后一个内容,实际上它是头部的一个容器,我们可以把内容放到这里面。
作为一个例子:

    <default>
		<reference name="top.container">
            <block type="customer/form_register" name="customer_form_register" template="customer/form/register.phtml"/>
        </reference>	
    </default>

那么头部最后将输出注册表单。

永久链接:http://blog.ifeeline.com/518.html
原创文章,转载务必保留出处。

Magento页面构建page_html_notices详解

Magento布局模板XML

class Mage_Page_Block_Html_Notices extends Mage_Core_Block_Template
{
    public function displayNoscriptNotice()
    {
        return Mage::getStoreConfig('web/browser_capabilities/javascript');
    }

    public function displayDemoNotice()
    {
        return Mage::getStoreConfig('design/head/demonotice');
    }

    public function getPrivacyPolicyLink()
    {
        return Mage::getUrl('privacy-policy-cookie-restriction-mode');
    }
}

displayNoscriptNotice获取的配置在后台System-> Configuration->Web-> Browser Capabilities Detection

Magento后台设置JS禁用时的行为

当浏览器不支持JS时将获取提示:

浏览器不支持JS时将获取提示

displayDemoNotice设置在后台System->Configuration->Design->HTML Head-> Display Demo Store Notice

Magento Demo Store提示

getPrivacyPolicyLink根据给定的路由获取链接,在Browser Capabilities Detection下面还有一个叫Redirect to CMS-page if Cookies are Disabled的设置,意思是当客户端禁用Cookies时是否重定向到CMS页面。

永久链接:http://blog.ifeeline.com/506.html
原创文章,转载务必保留出处。

Magento页面构建源码分析

Magento块继承层次

大部分的块都是从Mage_Core_Block_Template继承的,部分块会加入自己的抽象类(如上所示),间接继承自Mage_Core_Block_Template。

对应一个输出块,对有一个output属性,它的值应该总是toHtml,它是视图渲染的进入点。一般每个页面只有一个块有output属性,有多个块具有output属性也是允许的,这样它会把多个块分别渲染后合并在一起。如果指定了template,那么块的调用的第一个方法将是:

    public function setTemplate($template)
    {
        $this->_template = $template;
        return $this;
    }

而对应没有给出template的块,一般会实现_construct函数,里面调用setTemplate函数:

    protected function _construct()
    {
        $this->setTemplate('page/html/head.phtml');
    }

在Mage_Core_Block_Template中实现的构造函数会执行_construct()函数。

对应给定了output=toHtml的块,接下来调用它的toHtml()方法:

//Mage_Core_Block_Abstract
    final public function toHtml()
    {
        Mage::dispatchEvent('core_block_abstract_to_html_before', array('block' => $this));
		// 模块状态
        if (Mage::getStoreConfig('advanced/modules_disable_output/' . $this->getModuleName())) {
            return '';
        }
		//缓存
        $html = $this->_loadCache();
        if ($html === false) {
            $translate = Mage::getSingleton('core/translate');
            /** @var $translate Mage_Core_Model_Translate */
            if ($this->hasData('translate_inline')) {
                $translate->setTranslateInline($this->getData('translate_inline'));
            }

            $this->_beforeToHtml();
            $html = $this->_toHtml();
            $this->_saveCache($html); //保存缓存

            if ($this->hasData('translate_inline')) {
                $translate->setTranslateInline(true);
            }
        }
        $html = $this->_afterToHtml($html);

        /**
         * Check framing options
         */
        if ($this->_frameOpenTag) {
            $html = '<'.$this->_frameOpenTag.'>'.$html.'<'.$this->_frameCloseTag.'>';
        }

        /**
         * Use single transport object instance for all blocks
         */
        if (self::$_transportObject === null) {
            self::$_transportObject = new Varien_Object;
        }
        self::$_transportObject->setHtml($html); //_data[‘html’]= $html
        Mage::dispatchEvent('core_block_abstract_to_html_after',
                array('block' => $this, 'transport' => self::$_transportObject));
        $html = self::$_transportObject->getHtml();

        return $html;
    }

看起来很复杂,实际上它是间接调用了_toHtml(),注意toHtml是final方法,它不允许被覆盖,所以,自定义的实现应该放在_toHtml()方法中:

// Mage_Core_Block_Template
    protected function _toHtml()
{
		//如果没有模板,直接返回了
        if (!$this->getTemplate()) {
            return '';
        }
        $html = $this->renderView();
        return $html;
}

    public function renderView()
{
		// Mage::getBaseDir('design')获得/…/app/design
		// $this->_viewDir = /…/app/design;
        $this->setScriptPath(Mage::getBaseDir('design'));
		//$this->getTemplateFile()获取模板文件,如果找不到会逐层返回
		//frontend\base\default\template\page/3columns.phtml
        $html = $this->fetchView($this->getTemplateFile());
        return $html;
    }

这个fetchView看起来复杂,其实就是把模板文件include进来,如此而已。

    public function fetchView($fileName)
    {
        // EXTR_SKIP protects from overriding
        // already defined variables
        extract ($this->_viewVars, EXTR_SKIP); //把视图变量释放进入
        //$do = $this->getDirectOutput(); //直接输出,不缓存,总是false
		try{
            $includeFilePath = realpath($this->_viewDir . DS . $fileName);
            if (strpos($includeFilePath, realpath($this->_viewDir)) === 0 || $this->_getAllowSymlinks()) {
                include $includeFilePath;
            }
		}
	}

从这个作为入口,层层深入(一般都是一个整体模板文件,比如1列2列3列布局等):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $this->getLang() ?>" lang="<?php echo $this->getLang() ?>">
<head>
<?php echo $this->getChildHtml('head') ?>
</head>
<body<?php echo $this->getBodyClass()?' class="'.$this->getBodyClass().'"':'' ?>>
<?php echo $this->getChildHtml('after_body_start') ?>
<div class="wrapper">
    <?php echo $this->getChildHtml('global_notices') ?>
    <div class="page">
        <?php echo $this->getChildHtml('header') ?>
        <div class="main-container col3-layout">
            <div class="main">
                <?php echo $this->getChildHtml('breadcrumbs') ?>
                <div class="col-wrapper">
                    <div class="col-main">
                        <?php echo $this->getChildHtml('global_messages') ?>
                        <?php echo $this->getChildHtml('content') ?>
                    </div>
                    <div class="col-left sidebar"><?php echo $this->getChildHtml('left') ?></div>
                </div>
                <div class="col-right sidebar"><?php echo $this->getChildHtml('right') ?></div>
            </div>
        </div>
        <?php echo $this->getChildHtml('footer') ?>
        <?php echo $this->getChildHtml('before_body_end') ?>
    </div>
</div>
<?php echo $this->getAbsoluteFooter() ?>
</body>
</html>

由于模板本身就是include进来的,那么$this当然就是指当前的块了。getChildHtml()自然也是当前块的方法,这个方法可以调用子块,继续进行下去。页面就是如此构建的。

    public function getChildHtml($name = '', $useCache = true, $sorted = false)
    {
        if ($name === '') {
        } else {
            return $this->_getChildHtml($name, $useCache);
        }
}
    protected function _getChildHtml($name, $useCache = true)
    {
        $child = $this->getChild($name);

        if (!$child) {
            $html = '';
        } else {
            $this->_beforeChildToHtml($name, $child);
            $html = $child->toHtml();
        }

        $this->_childrenHtmlCache[$name] = $html;
        return $html;
    }

看到了吧,getChildHtml()最终还是调用了块的toHtml()方法加载模板,而这个方法就是include模板文件。(当然了,务必记住,一个块是否能使用getChildHtml()把子块调进来,关键在于这个块已经定义了这个子块,否则不工作)

永久链接:http://blog.ifeeline.com/495.html
原创文章,转载务必保留出处。