深入理解Magento – 第十章 – Magento系统覆盖和升级

2,630次阅读
没有评论

共计 8870 个字符,预计需要花费 23 分钟才能阅读完成。

An oft-touted and often overused feature of The Magento Ecommerce System is the ability to override core system behavior. Another oft-discussed topic for Magento developers is upgradability, and how overrides get in the way of that. Today we’re going to look at the various ways overrides make switching versions difficult.

Before we begin, it’s important to point out we’re talking about changing the core “Business Logic” of Magento. Changes to phtmltemplates are both expected and common in all but the simplest of stores.

Hacking the Source

The “least upgradable” way to change the behavior of Magento (or any PHP based system) is to alter the source code directly. If you want to change the behavior of the the Product Model, you edit the Product Model file

1 app/code/core/Mage/Catalog/Model/Product.php

When you do this, you’ve forked the Magento code base. Anytime you upgrade the system you’ll need to do a file by file merge with your forked version. This rarely goes well. Also, your run the risk of changing expected behavior of methods by having them return different values, not taking actions that the system may depend on, or alter data in unexpected, (rather than expected), ways. We’ll talk more about this below. Unfortunately, despite its inadvisability, this is the easiest and most understandable way for many PHP developers to start working with Magento. Before starting any new project I always download a clean version of the source and run a diff against both lib and app/code/coreto see what sort of changes have been made to the core.

Including Different Classes

Magento, or more accurately PHP, searches for class files in the following Magento folders.

lib/* app/code/core/* app/code/community/* app/code/local/* 

Because of this, and because of the order Magento constructs PHP’s include paths, placing a copy of a core file in the app/code/localfolder means PHP will include it first. So if you wanted to change the functionality of the Product Model, you’d add your own copy

YOURS: app/code/local/Mage/Catalog/Model/Product.php ORIGINAL: app/code/core/Mage/Catalog/Model/Product.php 

Your file defines the class instead of the core file, and therefore the core file never needs to be included. This avoids the problem of merging files that hacking the source creates, and also centralizes all your customizations in one directory structure. However, this is still only marginally better, and a solution you should avoid. Similar to hacking the core system files, you’re risking a changing the behavior of vital class methods. For example, consider the getName method on the afformentioned Product Model

/** * Get product name * * @return string */ public function getName() { return $this->_getData('name'); } 

While overriding this method, you might inadvertently add a code path in your override where this method returns null

/** * LOCAL OVERRIDE! Get product name * * @return string */ public function getName($param=false) { if($param == self:NICKNAME) { return $this->_getData('nickname'); } else if($param == self::BAR) { return $this->_getData('name') } //forgot a return because we're working too hard } 

If other parts of the system rely on this method to return a string, your customizations might break those other parts of the system. This gets even worse when methods are returning objects, as trying to call a method on null will result in a fatal error (this is part of what Java and C# programmers are harping on about w/r/t type safety in PHP) Next, consider the validate method in the same Model.

public function validate() { Mage::dispatchEvent($this->_eventPrefix.'_validate_before', array($this->_eventObject=>$this)); $this->_getResource()->validate($this); Mage::dispatchEvent($this->_eventPrefix.'_validate_after', array($this->_eventObject=>$this)); return $this; } 

Here, you might inadvertently remove the dispatching events.

//My local override! public function validate() { $this->_getResource()->validate($this); $this->myCustomValidation($this); return $this; } 

Other parts of the system that rely on these events being fired would stop working. Finally, you’re still not out of the woods during an upgrade. If the methods of any class change during an upgrade, Magento will still be including your old, outdated class with the old, outdated methods. Practically speaking, this means you still need to perform a manual merge during your upgrade.

Using the Override/Rewrite System

Magento’s class override/Rewrite system relies on the use of a factory patternfor creating Models, Helpers, and Blocks. When you say

Mage::getModel('catalog/product'); 

you’re telling Magento

“Hey, go lookup the class to use for a “catalog/product” and instantiate it for me.

In turn, Magento then consults its system configuration files and says

“Hey, config.xml tree! What class am I supposed to use for a “catalog/product”?

Magento then instantiates and returns the Model for you. When you override a class in Magentoo, you’re changing the configuration files to say

“hey, if a “catalog/product” Model is instantiated, use my class (Myp_Mym_Model_Product) instead of the core class

Then, when you define your class, you have it extend the original class

class Myp_Mym_Model_Product extends Mage_Catalog_Model_Product { } 

This way, your new class has all the old functionality of the original class. Here you avoid the problem of merging files during an upgrade andthe problem of your class containing outdated methods after an upgrade. However, there’s still the matter of changing method behavior. Consider, again, the getName and validate methods. Your new methods could just as easily forget/change-the-type-of a return value, or leave out a critical piece of functionality in the original methods.

class Myp_Mym_Model_Product extends Mage_Catalog_Model_Product { public function validate() { $this->_getResource()->validate($this); $this->myCustomValidation($this); return $this; } public function getName($param=false) { if($param == self:NICKNAME) { return $this->_getData('nickname'); } else if($param == self::BAR) { return $this->_getData('name') } //forgot a return because we're working too hard } } 

The override/rewrite system won’t protect you from this, but it will give you ways to avoid it. Because we’re actually extending the original class, we can call the original method using PHP’s parent::construct

class Myp_Mym_Model_Product extends Mage_Catalog_Model_Product { public function validate() { //put your custom validation up here return parent::validate(); } public function getName($param=false) { $original_return = parent::getName(); if($param == self::SOMECONST) { $original_return = $this->getSomethingElse(); } return $original_return; } } 

By calling the original methods, you’ve ensured that any actions that need to take place will take place. Also, by getting the original return value, you’ve reduced the chances that your method will return something unexpected. That’s reduced, not eliminated. It’s still up to you as the developer to ensure that your custom code returns objects or primitives that are the same as the original method’s. That means even if you use the provided override system, it’s still possible to break the system. Because of this, when I have control of the architecture of a solution I try to keep my overrides to a minimum. When I do have to override I always try to end my methods with a

return parent::originalMethod(); 

If my overrides needthat original method to run first, I used a construct something like

public function someMethod() { $original_return = Mage::getModel('mymodule/immutable') ->setValue('this is a test'); //my custom code here return $original_return->getValue(); } 

The 'mymodule/immutable'URI points to a simple immutable object implementation.

class Alanstormdotcom_Mymodule_Model_Immutable { protected $_value=null; public function setValue($thing) { if(is_null($this->_value)) { $this->_value = $thing; return $this; } //if we try to set the value again, throw an exception. throw new Exception('Already Set'); } public function getValue() { return $this->_value; } } 

This doesn’t prevent someone (including me) from overwriting $original_return with something else, but it does discourage it and makes it obvious when someone has. If there’s a method in my override class that doesn’t end in either $original_return->getValue(); or parent::method my eyes are immediately drawn to it as a possible culprit for whatever problem I’m debugging. Finally, there are times where you want, (or think you want) to change the return value of a core method. When the need for this arrises, I find it’s much safer to define a newmethod that calls the original, and then change your theme to call this new method.

class Mage_Catalog_Model_Original extends Mage_Core_Model_Abstract { protected function getSomeCollectionOriginal() { return Mage::getModel('foo/bar') ->getCollection()->addFieldToFilter('some_field', '42'); } } class Myp_Mym_Model_New extends Mage_Catalog_Model_Original { public function getSomeCollectionWithAdditionalItems() { $collection = $this->getSomeCollectionOriginal(); //now, alter or override the $collection with //your custom code return $collection; } } 

This ensures any additional side effects created by the original method still occur, the original return type/result is the same, and you can still add your custom logic to specific parts of the system.

Wrap up

Upgradability to any major, or even minor, version of a software package you don’t control is always going to be a bumpy ride. Apple and Microsoft spend millions of dollars testing their upgrade paths during new OS releases, and the Internet is still filled with horror stories of the edge cases they miss. Even insideorganizations where everyone’s working towards the same goal new versions often bring unspoken assumptions to the surface quickly as builds break and developers are forced to acknowledge the reality that they work with other human beings. As a user of the Magento Ecommerce System, your job is to ensure that changes to the core system are kept to a minimum, and that when those changes are needed they’re done in a clean, easily diagnosable matter. Leveraging the Magento override/rewrite system is a powerful tool towards this end.

Originally published June 14, 2010
正文完
 0
评论(没有评论)

空瓶子部落

文章搜索
推荐阅读
合格管理者的条件:就藏在孔子的“子绝四”里!

合格管理者的条件:就藏在孔子的“子绝四”里!

导语: 常看到一些管理者,明明能力出众,却总在管理中表现的“不及格”: 是主观臆断拍脑袋决策还是调研数据?是固...
华为业务流程L1-L6体系详解【附L1-L5流程清单】

华为业务流程L1-L6体系详解【附L1-L5流程清单】

在当今这么激烈的商业环境里,企业要想成功,光有好产品和服务可不够,还得把内部运营管理得井井有条。 华为,它的业...
名词解释 | 模态

名词解释 | 模态

前言 模态,从百度百科目前能检索到的两个词条来看,它是指结构系统的固有振动特性,包括模态频率、模态阻尼和模态振...
SPC控制图的两种错误以及解决办法

SPC控制图的两种错误以及解决办法

利用SPC控制图对过程进行监控,不可避免地要面对两种错误。那么这两种错误是什么?相应的解决办法是什么?本文将一...
跨越舒适区,拥抱无限成长

跨越舒适区,拥抱无限成长

在人生的广阔舞台上,每个人都是独一无二的演员,而我们的舞台,则是由无数个“圈”构成的——舒适圈、恐惧圈、学习圈...
最新文章
群晖 Let’s Encrypt 泛域名证书自动更新

群晖 Let’s Encrypt 泛域名证书自动更新

目前acme协议版本更新,开始支持泛域名(wildcard),也就是说,可以申请一个类似*.domain.co...
可以卸载TV Box 了,这款支持「绅士模式」的影视神器你值得拥有

可以卸载TV Box 了,这款支持「绅士模式」的影视神器你值得拥有

还在为找优秀片源难、广告多、平台会员太贵而烦恼?今天给大家挖到一款真正的影视宝藏工具——小猫影视! 作为开源免...
【收藏】一次性解决TV点播/直播自由

【收藏】一次性解决TV点播/直播自由

很多时候,资源就在面前,但是我们视而不见,因为长久的安逸,已经让人失去动手的兴趣。但是每次我需要挨个切换APP...
OpenWrt 存储空间扩容的两种方案

OpenWrt 存储空间扩容的两种方案

说明:当我们通过群晖 VMM 虚拟机安装 Open­Wrt 时,默认会分配一个 10GB 的存储空间,而实际情...
OpenWrt修改IP地址两种方法(直接命令修改跟后台修改)

OpenWrt修改IP地址两种方法(直接命令修改跟后台修改)

OpenWrt是什么?OpenWrt一般常见于无线路由器(软路由)第三方固件,它是一个高效、可靠、功能多的路由...
热门文章
提高过程能力指数(CP/CPK)的途径

提高过程能力指数(CP/CPK)的途径

编者按:过程能力指数(CP/CPK)想必各位质量人都耳熟能详、运用自如,质量工程师之家前期也共享过数篇关于过程...
SPC控制图的八种模式分析

SPC控制图的八种模式分析

SPC控制图有八种模式,即八种判断异常的检验准则,每一种检验准则代表一种异常现象,应用SPC控制图进行过程评估...
测量高手放大招:圆跳动测量技巧总结

测量高手放大招:圆跳动测量技巧总结

01. 前言 在五金机加工厂实际的测量工作中,经常碰到要求测量两个要素的圆跳动问题, 利用不同的测量辅件及夹具...
过程能力分析(CP&cpk)

过程能力分析(CP&cpk)

引入过程能力分析的目的? 在我们现有的管理过程中,我们经常会遇到有些具体指标总是不尽人意,存在许多需要改进的地...
新能源汽车 “两会”精神宣贯会

新能源汽车 “两会”精神宣贯会

此次和大家分享新能源汽车相关政策: [embeddoc url=”https://www.ctro...
最新评论
多乐士 多乐士 通过摸索发现ssh拉取会报错,直接网页访问下载会报404错误,不知道原因;但是可以通过群晖CM注册表访问下载,其方法如下: Container Manager-注册表-设置-新增-注册表名称随便写,注册表URL填你的加速地址,勾选信任的SSL自我签署证书,登录信息不填-应用-使用你的地址,这是注册表会显示了,在搜索栏中输入映像名称,搜索结果在每一页的最后一个,你需要划到最后一个进行下载,实测可正常下载安装。 以上供网友参考。
多乐士 多乐士 还有一个比较简单的方法,只是需要一些外部工具。 1、讲损毁硬盘取出,装入外部移动硬盘 2、打开Diskgenius,定位到硬盘 3、格式化系统分区 4、重新插入硬盘 5、存储池->修复存储池即可
多乐士 多乐士 写的不错的文章
辞了老衲 辞了老衲 这个确实有帮助。
渋驀 渋驀 当然任何时候都可以用curl命令和crontab来实现动态更新DDNS的ip地址: 1、安装crontab之后为root用户创建文件/var/spool/cron/root 2、创建并配置ddnsupdate.sh,放到/usr/bin/文件下,文件内容(以he.net为例): Autodetect my IPv4/IPv6 address: IPV4:curl -4 "http://dyn.example.com:password@dyn.dns.he.net/nic/update?hostname=dyn.example.com" IPV6:curl -6 "http://dyn.example.com:password@dyn.dns.he.net/nic/update?hostname=dyn.example.com" 3、添加执行权限chomod +x /usr/bin/ddnsupdate.sh 4、编辑root用户的crontab:*/10 * * * * /usr/binddnsupdate.sh,每10分钟执行一次。好了,可以享受你的DDNS了
21410 21410 请问下载链接在那里?
madkylin madkylin 不错,不错,谢谢分享了,好东西啊 :lol:
feilung feilung 求方法
zengsuyi zengsuyi 应该挺不错的
zise zise 看看是怎么操作的。。 :oops: