第八章 深入理解Magento – 修改、扩展、重写Magento代码

作为一个开发者的你,肯定要修改Magento代码去适应你的业务需求,但是在很多时候 我们不希望修改Magento的核心代码,这里有很多原因,例如将来还希望升级Magento、还想使用更多的Magento代码。如果你正在寻找修改 Magento代码的最佳方式,那么就要好好学习一下本章内容。

创建自己的Magento模块(Module)

第一步,你需要创建属于你自己代码的命名空间,例如Infinity,App等,为了方便理解,我将空间命名为Infinity。

app/

   code/

      core/

      community/

      local/

         Infinity/

假如你现在打算修改Mage/Catalog/Block/Breadcrumbs.php这个文件,你可以在你的命名空间,Infinity里添 加一个新的模块“Catalog”。接下来创建块(Block)目录,并复制Breadcrumbs.php到你的新目录中。这里还需要你创建一个 config.xml配置文件。

app/

   code/

      core/

      community/

      local/

         Infinity/

            Catalog/

                  Block/

                      Breadcrumbs.php

                  etc/

                      config.xml

修改Breadcrumbs.php的类名为Infinity_Catalog_Block_Breadcrumbs,并继承原类名Mage_Catalog_Block_Breadcrumbs。
现在,你需要激活你的新模块,这样magento才能够知道你的新模块。

创建文件app/etc/modules/Infinity_All.xml,添加如下代码.

< ?xml version="1.0"?>

<config>

     <modules>

        <Infinity_Catalog>

            <active>true</active>

            <codePool>local</codePool>

        </Infinity_Catalog>

     </modules>

</config>

下面我们需要一个特殊的标签来复写掉Breadcrumbs,下面我们通过模块的配置文件来实现。

8.1 重写Magento区块(Blocks)

编辑文件“app/code/local/Infinity/Catalog/etc/config.xml”

 

<?xml version="1.0" encoding="UTF-8"?>

<config>

    <modules>

        <Infinity_Catalog>

            <version>0.1.0</version>

        </Infinity_Catalog>

    </modules>

    <global>

        <blocks>

            <catalog>

                <rewrite>

                        <breadcrumbs>Infinity_Catalog_Block_Breadcrumbs</breadcrumbs>

                </rewrite>

            </catalog>

        </blocks>

    </global>

</config>

 

我们需要添加一个“blocks” 标签,或者在已经存在的“blocks”标签中添加内容。然后在模块名后面添加rewrite标签,在这个例子中模块名是“catalog”。然后我们看 “breadcrumbs”标签,这个标签帮助magento找到我们我们想修改的块。
在我们的列子中,breadcrumbs是Magento核心代码中的类名: app/code/core/Mage/Catalog/Block/Breadcrumbs.php。如果你有更多的目录层级,可以用下滑线来分隔。例如:

 <blocks>

          <catalog>

              <rewrite>

                      <category_view>Infinity_Catalog_Block_Category_View</category_view>

              </rewrite>

          </catalog>

      </blocks>

在这个例子中,我们重写了app/code/core/Mage/Catalog/Block/Category/View.php。

在breadcrumbs标签中的值是你的类名,这样Magento就可以获取你的类,因为类名与你的目录名一致。用过zend framework的人都知道,自动加载auto loader这个东西,它会跟你类名中的下滑线去你的目录中需要对应的类文件。记住一点,下滑线代表下一级别的文件夹,如果你的类名与你的文件目录名不一 致,那么Magento根本不会理睬你。
举例来说:

Infinity_Catalog_Block_Breadcrumbs → /app/code/local/Infinity/Catalog/Block/Breadcrumbs.php

Infinity_Catalog_Block_Category_View → /app/code/local/Infinity/Catalog/Block/Category/View.php

8.2 重写Magento控制器(Controller)

重写Magento控制器我将介绍三种重写Controller的方法。

8.2.1 第一种方法:正则表达式匹

我以重写Checkout Cart为例介绍如下。

1、首先在App下创建新的模块,依次创建如下文件:

/app/code/local/Infinity/Shopping

/app/code/local/Infinity/Shopping/etc

/app/code/local/Infinity/Shopping/etc/config.xml

/app/code/local/Infinity/Shopping/controllers

/app/code/local/Infinity/Shopping/controllers/CartController.php

2、编辑/app/code/local/Infinity/Shopping/etc/config.xml文件,加入如下代码:

<?xml version="1.0"?>

<config>

    <modules>

        <Infinity_Shopping>

            <version>0.1.0</version>

        </Infinity_Shopping>

    </modules>

    <global>

        <!-- This rewrite rule could be added to the database instead -->

        <rewrite>

            <!-- This is an identifier for your rewrite that should be unique -->

            <!-- THIS IS THE CLASSNAME IN YOUR OWN CONTROLLER -->

            <Infinity_Shopping_cart>

                <from><![CDATA[#^/checkout/cart/#]]></from>

                <!--

                    - Shopping module matches the router frontname below - checkout_cart

                    matches the path to your controller Considering the router below,

                    "/shopping/cart/" will be "translated" to

                    "/Infinity/Shopping/controllers/CartController.php" (?)

                -->

                <to>/shopping/cart/</to>

            </Infinity_Shopping_cart>

        </rewrite>

    </global>

    <!--

        If you want to overload an admin-controller this tag should be <admin>

        instead, or <adminhtml> if youre overloading such stuff (?)

    -->

    <frontend>

        <routers>

            <Infinity_Shopping>

                <!-- should be set to "admin" when overloading admin stuff (?) -->

                <use>standard</use>

                <args>

                    <module>Infinity_Shopping</module>

                    <!-- This is used when "catching" the rewrite above -->

                    <frontName>shopping</frontName>

                </args>

            </Infinity_Shopping>

        </routers>

    </frontend>

</config>

3、改写你自己的控制器
/app/code/local/Infinity/Shopping/controllers/CartController.php
请将下面的代码添加到你的控制器中,我们唯一修改的地方是在index动作中添加一个error_log();

< ?php

# 控制器不会自动加载,所以我们需要包含文件,这里与区块(Block)不一样

require_once 'Mage/Checkout/controllers/CartController.php';

class Infinity_Shopping_CartController extends Mage_Checkout_CartController

{

    #Override indexAction

    public function indexAction()

    {

        # Just to make sure

        error_log('Override Success.');

        parent::indexAction();

    }

}

 

在这段代码中,首先是类名,跟前面讲到的区块(Block)一样,我们自己的类名是Infinity_Shopping_CartController继承原先Mage_Checkout_CartController.在indexAction中我们记录了一段信息。

4、修改Infinity_All.xml,激活我们新的Shopping模块

<?xml version="1.0"?>

<config>

     <modules>

        <Infinity_Catalog>

            <active>true</active>

            <codePool>local</codePool>

        </Infinity_Catalog>

        <Infinity_Shopping>

            <active>true</active>

            <codePool>local</codePool>

        </Infinity_Shopping>

     </modules>

</config>

到这里,清除缓存后,你已经可以看到error_log成功记录了我们的信息,打开页面http://www.my.com/checkout/cart/,显示的是购物车页面,一切正常,但如果你访问http://www.my.com/shopping/cart/,你会发现是首页,我们期望的购物车视图还没有出现,如何解决呢?让我们接下来往下看。

5、修改视图文件app/design/frontend/[myinterface]/[mytheme]/layout/checkout.xml

在layout标签中,添加下面内容:

 

    < infinity _shopping_cart_index>

        <update handle="checkout_cart_index"/>

    </infinity _shopping_cart_index>

 

注意,这里的大小写敏感。

到这里基本大功告成,但是,我建议你学习下正则表达式,因为刚刚的代码中,有这么一段:

<from>< ![CDATA[#^/checkout/cart/#]]></from>

这里是使用正则表达式进行匹配的。

8.2.2 第二种方法:同名模块整个覆盖

经过尝试,这里可以支持同模块名覆盖。
例如Magento代码中商品详情页是Mage_Catalog_ProductController::viewAction(),如果我们想重写这个Controller,我们可以这样做:

1、建立新的目录/app/code/local/Infinity/Catalog/controllers/ProductController.php
代码如下:

require_once 'Mage/Catalog/controllers/ProductController.php';

/**

 * Product controller

 *

 * @category   Mage

 * @package    Mage_Catalog

 */

class Infinity_Catalog_ProductController extends Mage_Catalog_ProductController

{

    /**

     * View product action

     */

    public function viewAction()

    {

            echo '覆盖过的....';

        parent::viewAction();

    }

}

2、编辑/app/code/local/Infinity/Catalog/etc/config.xml
代码如下:

<?xml version="1.0" encoding="UTF-8"?>

<config>

    <modules>

        <Infinity_Catalog>

            <version>0.1.0</version>

        </Infinity_Catalog>

    </modules>

    <global>

        <!-- This rewrite rule could be added to the database instead -->

          <rewrite>

            <!-- This is an identifier for your rewrite that should be unique -->

            <!-- THIS IS THE CLASSNAME IN YOUR OWN CONTROLLER -->

            <Infinity_Shopping_cart>

                <from><![CDATA[#^/catalog/product/#]]></from>

                <!--

                    - Shopping module matches the router frontname below - checkout_cart

                    matches the path to your controller Considering the router below,

                    "/shopping/cart/" will be "translated" to

                    "/Infinity/Shopping/controllers/CartController.php" (?)

                -->

                <to>/catalog/product/</to>

            </Infinity_Shopping_cart>

          </rewrite>

        <blocks>

            <catalog>

                <rewrite>

                        <breadcrumbs>Infinity_Catalog_Block_Breadcrumbs</breadcrumbs>

                </rewrite>

            </catalog>

        </blocks>

    </global>

        <frontend>

        <routers>

            <catalog>

                <use>standard</use>

                <args>

                    <module>Infinity_Catalog</module>

                    <frontName>catalog</frontName>

                </args>

            </catalog>

        </routers>

        </frontend>

</config>

 

清空缓存,刷新你的商品详情页,看是不是变了,呵呵。但是这个方法有个弊病,你需要把这个模块的所有Controller都复写掉,不然你会遇到比较大的麻烦。

8.2.3 第三种方法:单独重写指定的Action

仔细看配置文件的写法:

<?xml version="1.0"?>

<config>

     <modules>

        <Infinity_Mycms>

            <version>0.1.0</version>

        </Infinity_Mycms>

    </modules>

    <frontend>

        <routers>

            <mycms>

                <use>standard</use>

                <args>

                    <module>Infinity_Mycms</module>

                    <frontName>mycms</frontName>

                </args>

            </mycms>

        </routers>

    </frontend> 

    <global>

        <routers>

            <cms>

                <rewrite>

                    <index>

                        <to>Infinity_Mycms/index</to>

                        <override_actions>true</override_actions>

                        <actions>

                           <noroute><to>Infinity_Mycms/index/noroute</to></noroute>

                        </actions>

                    </index>

                </rewrite>

            </cms>

        </routers>  

    </global>

</config>

 

综上所述,三种重写方法都各有千秋,关键看你用在什么地方。另外我们在实践中发现,Magento好像不建议你自己的模块名与现有系统中的模块名一致,例如Mage_Customer是已有的,它的模块名叫Customer,如果你想复写它,那么最好你再建一个Infinity_Customers之类的。

8.3 重写Magento模型 (Model)

我们在改写Magento的过程中,为了实现自己的业务逻辑,难免要改它的业务模型。你可以尝试用模块下的配置文件配置你自己的类,继承你想重写的模型或者助手,然后调用自己的类。现在我们以用户模型为例深入讲解。

首先创建自己的模块目录结构

app/code/local/Infinity/Customer

app/code/local/Infinity/Customer/etc/config.xml

app/code/local/Infinity/Customer/Model

app/code/local/Infinity/Customer/Model/Customer.php

8.3.1 重写Model

1、修改app/etc/modules/Infinity_All.xml

<Infinity_Customer>

            <active>true</active>

            <codePool>local</codePool>

        </Infinity_Customer>

2、修改自己的模块配置文件app/code/local/Infinity/Customer/etc/config.xml

<?xml version="1.0" encoding="UTF-8"?>

<config>

    <modules>

        <Infinity_Customer>

            <version>0.1.0</version>

        </Infinity_Customer>

    </modules>

 

    <global>

        <models>

            <customer>

                <rewrite>

                    <customer>Infinity_Customer_Model_Customer</customer>

                </rewrite>

            </customer>

        </models>

    </global>

</config>

3、编辑自己的Model文件app/code/local/Infinity/Customer/Model/Customer.php
在此文件中新建类Infinity_Customer_Model_Cutomer

class Infinity_Customer_Model_Customer extends Mage_Customer_Model_Customer {

    // 重写已存在的方法

    public function validate() {

        // Define new validate rules. From now magento call this validate method instead of existing method

        //return $errors;

        return true;

    }

 

    // 你还可以创建新的方法

    public function newMethod() {

        // function logic

    }

}

4、我们再重写一个类,以加深理解。

接下来我们重写Customer Address Model。
跟重写Customer Model一样,我们先编辑模块的配置文件app/code/local/Infinity/Customer/etc/config.xml。

<?xml version="1.0" encoding="UTF-8"?>

<config>

    <modules>

        <Infinity_Customer>

            <version>0.1.0</version>

        </Infinity_Customer>

    </modules>

 

    <global>

        <models>

            <customer>

                <rewrite>

                    <customer>Infinity_Customer_Model_Customer</customer>

                    <address>Infinity_Customer_Model_Address</address>

                </rewrite>

            </customer>

        </models>

    </global>

</config>

上面看出来了吗,rewrite标签内的customer和address其实就是你要覆写的Magento Model。
接下来创建model class

Infinity_Customer_Model_Address,并写你要覆盖和新增的方法。

 

class Infinity_Customer_Model_Address extends Mage_Customer_Model_Address {

    // 重写已存在的方法

    public function validate() {

        // Define new validate rules. From now magento call this validate method instead of existing method

        //return $errors;

        return true;

    }

 

    // 你还可以创建新的方法

    public function newMethod() {

        // function logic

    }

}

8.3.2 重写资源模型Entity Model

这里以复写Address Entity Model class为例。
我们先来修改模块的配置文件app/code/local/Infinity/Customer/etc/config.xml。

<?xml version="1.0" encoding="UTF-8"?>

<config>

    <modules>

        <Infinity_Customer>

            <version>0.1.0</version>

        </Infinity_Customer>

    </modules>

 

    <global>

        <models>

            <customer>

                <rewrite>

                    <customer>Infinity_Customer_Model_Customer</customer>

                    <address>Infinity_Customer_Model_Address</address>

                </rewrite>

            </customer>

            <customer_entity>

                <rewrite>

                    <address>Infinity_Customer_Model_Entity_Address</address>

                </rewrite>

            </customer_entity>

        </models>

    </global>

</config>

 

接下来创建类文件。

 

class Infinity_Customer_Model_Entity_Address extends Mage_Customer_Model_Entity_Address {

    protected function _afterSave(Varien_Object $address) {

        // Write your code

    }

}

8.4 重写Helper

这里以复写Mage Customer Helper Data为例。
我们先来修改模块的配置文件app/code/local/Infinity/Customer/etc/config.xml。

<?xml version="1.0" encoding="UTF-8"?>

<config>

    <modules>

        <Infinity_Customer>

            <version>0.1.0</version>

        </Infinity_Customer>

    </modules>

 

    <global>

        <models>

            <customer>

                <rewrite>

                    <customer>Infinity_Customer_Model_Customer</customer>

                    <address>Infinity_Customer_Model_Address</address>

                </rewrite>

            </customer>

            <customer_entity>

                <rewrite>

                    <address>Infinity_Customer_Model_Entity_Address</address>

                </rewrite>

            </customer_entity>

        </models>

        <helpers>

         <customer>

              <rewrite>

                 <data>Infinity_Customer_Helper_Customer_Data</data>

              </rewrite>

         </customer>

     </helpers>

    </global>

</config>

 

接下来创建Helper类文件:

/app/code/local/Infinity/Helper/Customer/Data.php

class Infinity_Customer_Helper_Customer_Data extends Mage_Customer_Helper_Data {

    protected function getDetail ($orgDetail) {

        return nl2br(Mage::helper(‘core/string’)->truncate($origDetail, 100));

    }

}

8.5 事件的监听(dispatchEvent)

当你需要扩展Magento的核心的功能时有一般有两个选择:

 •用以上几节的方法重写(override)Magento核心代码Core classes。

 •使用Magento的event-driven 机制。

 

由于你只能重写一次Magento的core classes(如果你需要在多个module中重写相同core class就会出现错误),所以使用Magento的Event就大显神威了,它使得你的工作简便的简易方便。

 

Magento的event-driven机制中有两个非常重要的概念:

 •分发(dispatch)一个事件(event),我们自己经常称之为监听点。

 •捕捉(catch)一个事件(event)

 

分发事件(Dispatch Event)

Magento中你需要调用 Mage::dispatchEvent(…) 就可以设置一个Event监听点, 我们在任意类中先插入一个custom_event监听点:

Mage::dispatchEvent('custom_event', array('object'=>$this));

 

该方法有两个参数,一个是事件的标识符(在所有事件中必须唯一)和一个关联数组。该数组的键值是一个data(Varien_Event_Observer 的一个实例),这个data将被执行事件的函数(即observer)进行处理并返回结果。

 

看下面这两个事件监听代码:

Mage::dispatchEvent('newsletter_customer_subscribed', array('customer'=>$customer, 'email'=>$email));

Mage::dispatchEvent('sales_order_status_change_after', array('order' => $order, 'status_before' => $status_before, 'status_after' => $status_after));

大家发现没有,可以在array中传递多个data(关联数组就是这样用的)。

捕捉事件(Catching events)
捕捉事件比分发复杂一些,你必须使用一个已有的module或者新建一个module。我们创建一个新的Module,目录结构如下:

app/code/local/Infinity/Myevents

app/code/local/Infinity/Myevents/etc/config.xml

app/code/local/Infinity/Myevents/Model

app/code/local/Infinity/Myevents/Model/Observer.php

 

在该目录的config.xml文件中,我们新定义一个event observer,就使用上面我们定义的custom_event监听点,下面是参考示例:

 <events>

      <custom_event> <!-- identifier of the event we want to catch -->

        <observers>

          <custom_event_handler> <!-- identifier of the event handler -->

            <type>model</type> <!-- class method call type; valid are model, object and singleton -->

            <class>myevents/observer</class> <!-- observers class alias -->

            <method>customObserverAction</method>  <!-- observer's method to be called -->

            <args></args> <!-- additional arguments passed to observer -->

          </custom_event_handler>

        </observers>

      </custom_event>

</events>

 

上面的xml的配置有一个要注意的地方,就是标签里的值可以是model,object,singleton,其中model和object是等效 的且它们都将使用Mage::getModel(….)方法来进行初始化;而singleton将使用Mage::getSingleton(….)来初 始化。此外,在Observer.php中就是相关的observer类的定义,该类既不需要继承其他类也不需要继承其他接口。就需要在 customObserverAction方法的参数中传入Varien_Event_Observer类的一个实例,正是这个实例对象把 dispatcher和event handler联系到一起。同时该实例对象也是继承于Varient_Object,所以拥有get魔术方法,例:

 class Infinity_Myevents_Model_Observer

{

  public function customObserverAction(Varien_Event_Observer $observer)

  {

    $object = $observer->getEvent()->getObject(); // we are taking the item with 'object' key from array passed to dispatcher

    $object->doSomething();

 

    return $this;

}

 

其实event的原理很简单,就是它在你保存或读取数据之前对一些数据进行处理,这就是event的最终目的。所以必须得在dispatcher和 event handler之间传递数据,同时该数据是以Varien_Event_Observer类形式存在,底层的实现Magento已经良好的封装,更多细节 参考Mage_Core_Model_Abstract类中的一些方法(如protected _beforeSave() 和 protected

8.6 同名覆盖重写

以上所描述的重写方法基本都已经可以满足所有重写的要求,但是都需要配置config.xml中的rewrite才可以执行,也就是说必须要新建一 个Module才可以实现,其实Magento还提供了另外一种重写方法,就是把核心类文件按相同的目录结构和类名称,复制到local目录下,这样也会 覆盖核心的代码,因为Magento加载是有优先级的,Local目录的优先级要高于core。
例如:将/app/code/core/Mage/Checkout/Model/Cart.php复制到
/app/code/local/Mage/Checkout/Model/Cart.php
这样新的这个local里的文件就会覆盖core里的,达到重写的目的。

注意事项:
1、必须保持类名和目录一模一样。
2、大小写敏感。
3、这种方法只能重写Block、Model,不支持controller、helper的重写。

8.7 总结

在本章中我们学习了如何重写模块、重写控制器、重写区块、重写模型、重写助手Helper,以及如何使用事件Event的分发和捕捉,基本重写Magento代码对你来说已经不是难事了。文章至此,要恭喜你,你已经掌握了大部分修改Magento的技能。

 

转载于:https://my.oschina.net/ganfanghua/blog/1549054

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值