自定义Magento导航目录

Magento的默认主题中导航目录是垂直下拉式菜单:鼠标滑过时会显示下级菜单。实际上你如果要把菜单改成水平下拉式的话,最好的方式重写目录导航的block,自定义生成你所需要的导航样式。定义目录导航的文件位于/app/code/core/Mage/Catalog/Block/Navigation.php。主要由这renderCategoriesMenuHtml和_renderCategoryMenuItemHtml两个函数来实现。

函数renderCategoriesMenuHtml取出所有一级目录,然后丢给renderCategoryMenuItemHtml去循环判断是否有子目录,如果子目录是可显示的话,才能在前台显示。其实可以由一个递归函数来实现嘛。为此,我写一个小插件来实现这个功能。

首先添加插件的模块信息app/etc/modules/Cm_Menu.xml

<?xml version="1.0"?>
<config>
  <modules>
    <Cm_Menu>
      <active>true</active>
      <codePool>local</codePool>
      <version>1.0.0</version>
    </Cm_Menu>
  </modules>
</config>

添加模块配置信息,重写了catalog/navigation这个block

<?xml version="1.0"?>
<config>
  <modules>
    <Cm_Menu>
      <version>1.0.0</version>
    </Cm_Menu>
  </modules>
  <global>
    <helpers>
      <menu>
        <class>Cm_Menu_Helper</class>
      </menu>
    </helpers>
    <blocks>
    	<menu>
        	<class>Cm_Menu_Block</class>
        </menu>
    	<catalog>
    		<rewrite>
    			<navigation>Cm_Menu_Block_Navigation</navigation>
    		</rewrite>
    	</catalog>
    </blocks>
  </global>
</config>

自定义renderMenu函数来生成导航。Cm_Menu_Block_Navigation 继承Mage_Catalog_Block_Navigation,这样就可以使用在phtml使用自定义的函数。

<?php
/**
 * Author: Matt
 * URI:	   http://www.xbc.me
 * Time:   2012.7.1
 */
class Cm_Menu_Block_Navigation extends Mage_Catalog_Block_Navigation{
	public function renderMenu($level = 0, $category = '' , $isFirst = false){
		$activeCategories = array();
		if($level == 0 && $category == ''){
			//only get all category on top level
        	foreach ($this->getStoreCategories() as $child) {
            	if ($child->getIsActive()) {
                	$activeCategories[] = $child;
            	}
        	}
		}else{
			if (!$category->getIsActive()) {
            	return '';
        	}	
        	// get all children
        	if (Mage::helper('catalog/category_flat')->isEnabled()) {
            	$children = (array)$category->getChildrenNodes();
        	} else {
            	$children = $category->getChildren();
        	}
			foreach ($children as $child) {
            	if ($child->getIsActive()) {
                	$activeCategories[] = $child;
            	}
        	}
		}
        $activeCategoriesCount = count($activeCategories);
        $hasActiveCategoriesCount = ($activeCategoriesCount > 0);
        //only check parent category 
		if($level == 0 && $category == ''){
        	if (!$hasActiveCategoriesCount) {
            	return '';
        	}
		}
		$_html = '';
        $j = 0;
        if($level == 0 && $category == ''){
        	foreach ($activeCategories as $category) {
            	$_html .= $this->renderMenu( $level, $category , ($j == 0));
            	$j++;
        	}
        	return $_html;
        }else{
        	$_url = $this->getCategoryUrl($category);
        	$_name = $this->escapeHtml($category->getName());
        	//$_child = Mage::getModel( 'catalog/category' )->load($category->getId());
        	//$_description = $_child->getDescription();
        	if(!$_description){
        		$_description = $_name;
        	}
        	$html = array();
        	foreach ($activeCategories as $category) {
            	$_html .= $this->renderMenu( $level + 1, $category , ($j == 0));
            	$j++;
        	}
        	$classes = array();
			if ($isFirst) {
            	$classes[] = 'fore';
        	} 
			if($level == 0){
        		$classes[] = 'item';
        		$attributes = array();
        		if (count($classes) > 0) {
            		$attributes['class'] = implode(' ', $classes);
        		}
        		$htmlLi = '<div';
        		foreach ($attributes as $attrName => $attrValue) {
            		$htmlLi .= ' ' . $attrName . '="' . str_replace('"', '\"', $attrValue) . '"';
        		}
        		$htmlLi .= '>';
        		$html[] = $htmlLi;
 
        		$html[] = '<span>';
        		$html[] = '<h3>';
        		$html[] = '<a href="'.$_url.'" title="'.$_description.'">'.$_name.'</a>';
        		$html[] = '</h3>';
        		$html[] = '</span>';
        		$html[] = '<div class="i-mc">';
	        	$html[] = '<div class="subitem">';
        		$html[] = $_html;	
        		$html[] = '</div>';
	        	$html[] = '</div>';
	        	$html[] = '</div>';
        	}
        	if($level == 1){
        		$attributes = array();
        		if (count($classes) > 0) {
            		$attributes['class'] = implode(' ', $classes);
        		}
        		$htmlLi = '<dl';
        		foreach ($attributes as $attrName => $attrValue) {
            		$htmlLi .= ' ' . $attrName . '="' . str_replace('"', '\"', $attrValue) . '"';
        		}
        		$htmlLi .= '>';
        		$html[] = $htmlLi;
 
	        	$html[] = '<dt>';
	        	$html[] = '<a href="'.$_url.'" title="'.$_description.'">'.$_name.'</a>';
	        	$html[] = '</dt>';
	        	$html[] = '<dd>';
	        	$html[] = $_html;
	        	$html[] = '</dd>';
	        	$html[] = '</dl>';
        	}
        	if($level == 2){
        		$html[] = '<em>';
        		$html[] = '<a href="'.$_url.'" title="'.$_description.'">'.$_name.'</a>';
        		$html[] = '</em>';
        	}
			$_html = implode("\n", $html);
        	return $_html;
        }
	}
}
?>

在renderMenu函数中,函数有3个参数,$level,$category,$j。$level参数是目录的层级,参数$category是子目录的实例。目录的层级限制在3层以内,在第一次循环的的时候,$level=0,会从系统取出全部的一级目录分类,存入$activeCategories数组中,通过计算$activeCategories数组的个数来判断时候是否存在二级目录。如果存在二级目录,再丢给renderMenu函数自己,此时$level=0,$category参数是子目录的实例,根据不同的$level进行循环,得到最后的目录列表。

将block的type换成

<block type="menu/navigation" name="left.category" template="widget/category.phtml" />

在模板中只需要调用renderMenu函数

<?php $_menu = $this->renderMenu();?>
<?php if($_menu): ?>
<div class="nav-box">
   <?php echo $_menu ?>
</div>
<?php endif ?>

本插件在Magento CE 1.6.2 上测试通过

Yii实现Password Repeat Validate Rule

在使用Yii时遇到这样的需求:在一个注册的页面输入两次密码,并验证两次输入是否一致。可是password的repeat的字段在数据库并不存在。问题来了,如何创建一个password_repeat的属性,进行密码验证。最偷懒的方法就是利用yii自带的验证器。在这里记录下实现的方法。

假设项目结构如下

protected/models/User.php

proteced/controllers/SiteController.php

protected/views/site/forgot.php

首先在User.php添加一个public的password_repeat属性,该属性会自动映射到rules中。注意on属性,是决定rule应用的场景,默认的场景是insert。在这里,我应用在forgot场景。

class User extends CActiveRecord
{
    public $password_repeat;
    public function rules()
    {
        // NOTE: you should only define rules for those attributes that
        // will receive user inputs.
        return array(
            ...
            array('password_repeat', 'required' , 'on' => 'forgot'),
            array('password', 'compare', 'compareAttribute'=>'password_repeat' ,'on'=>'forgot'),
 
        );
    }
}

在SiteController.php中的actionForgot方法中,添加一个User的model。

public function actionForgotPassword(){
   $model = new User();
   //set current scenario
   $model->scenario = 'forgot';
   $User = Yii::app()->getRequest()->getParam('User');
   if($User){
      $model->attributes = $User;
      Helper::performAjaxValidation($model);
   ....
   }
   $this->render('forgot',array( 'model' => $model));
}
//Helper的ajax 验证方法,这个在默认生成的controller可以找到
static function performAjaxValidation($model)
    {
        if(isset($_POST['ajax']) && $_POST['ajax']==='user-form')
        {
            echo CActiveForm::validate($model);
            Yii::app()->end();
        }
    }

在视图文件中forgot.php,添加password_repeat字段。

<?php $form=$this->beginWidget('CActiveForm', array(
    'id'=>'user-form',
    'enableAjaxValidation'=>true,
)); ?>
<ul>
<li>
        <?php echo $form->label($model,'password' , array( 'label' => 'Your New Password')); ?>
        <?php echo $form->passwordField($model,'password',array('size'=>32,'maxlength'=>32)); ?>
        <?php echo $form->error($model,'password'); ?>
    </li>
<li>
        <?php echo $form->labelEx($model,'password_repeat' , array( 'label' => 'Repeat Password')); ?>
        <?php echo $form->passwordField($model,'password_repeat',array('size'=>32,'maxlength'=>32)); ?>
        <?php echo $form->error($model,'password_repeat'); ?>
    </li>
</ul>

这样就实现了password的验证了。

参考资料

Understanding Scenarios

在win7下 安装多个版本的ie

之前有写过一篇文章是关于win7下用IE6进行调试,现在的问题是我不仅仅想安装ie6就完事了,还想要ie7,ie8呢?下面介绍一种方法在win7下利用windows xp mode 来模拟不同版本的IE。

创建第二个虚拟机

找你到的Virtual PC的安装目录,找到Windows XP Mode.vhd,也就是我在上一篇文章中安装的Windows XP Mode。

ie-1

选择该文件夹下面的Windows XP Mode.vhd,Ctrl+C复制文件然后Ctrl+V粘贴即可,会自动生成一个当前虚拟机的副本,重命名为Windows XP IE8.vhd。

打开开始菜单->Windows Virtual PC->管理虚拟机->创建虚拟机。步骤如图所示。

ie-2

选择所有程序,找到Windows Virtual PC文件夹

ie-3

选择创建虚拟机

ie-4

填写要创建虚拟机的名称Windows IE8,存放虚拟文件的位置。

ie-5

下一步,设置一个比较小的内存,比如128。

ie-6

 

关键是这一步了,选择使用现有虚拟硬盘,选择刚刚copy的Windows XP IE8.vhd。点击创建。

ie-7

创建成功Windows XP IE8.vmcx。双击打开虚拟机。

ie-9

发现本地链接出错。

ie-10

右键选择Windows IE8.vmcx,选择设置。设置虚拟机的网络适配器为:共享网络。

ie-11

回到虚拟机,出现本地链接已经链接上了。

ie-12

从微软的官方网站下载合适的IE7和IE8版本,直接安装

Internet Explorer 下载中心

你可以直接在虚拟机里按照下面的地址下载安装32版本的IE7和IE8

IE7点这里

IE8点这里

下载完成后,就开始安装了。

ie-22

IE8安装完成了。

ie-23

为你的ie8创建一个快捷方式,把快捷方式拖到All Users 开始菜单里。

ie-24

你也安装按照这个方法来安装IE7。

ie-25

参考网站

Testing Multiple Versions of IE on One PC

Using Windows 7’s Windows XP Mode to Run Multiple Versions of Internet Explorer

Done!