CentOS 7迁移Magento 1.9.x到Mangento 2

Magento 2安装页面

Magento 2出来也挺久时间了,市场上的反应有部分人觉得它很慢,这可能与硬件设备的要求有关,也有部分人觉得不好用。不管怎样,事物总是向前发展的,Magento 2必将逐步取代Magento 1.9.x,这只是时间问题,也有消息称从今年11月起官方将可能不再对1.9.x的版本支持。Alan已经很久没有对Magento进行研究了,今天抽空对一个站点作迁移的尝试,在这里记录下来过程和问题和大家分享。

Read More

Magento 2 命令行

在Magento 2中可通过命令行来进行相当一部分的操作,如清除缓存、重新索引页面等。进入安装路径的bin目录下,输入php magento –list即可看到相对应的命令:

Usage:
 command [options] [arguments]

Options:
 --help (-h)           Display this help message
 --quiet (-q)          Do not output any message
 --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
 --version (-V)        Display this application version
 --ansi                Force ANSI output
 --no-ansi             Disable ANSI output
 --no-interaction (-n) Do not ask any interactive question

Available commands:
 help                                      Displays help for a command
 list                                      Lists commands
admin
 admin:user:create                         Creates an administrator
 admin:user:unlock                         Unlock Admin Account
cache
 cache:clean                               Cleans cache type(s)
 cache:disable                             Disables cache type(s)
 cache:enable                              Enables cache type(s)
 cache:flush                               Flushes cache storage used by cache type(s)
 cache:status                              Checks cache status
catalog
 catalog:images:resize                     Creates resized product images
cron
 cron:run                                  Runs jobs by schedule
customer
 customer:hash:upgrade                     Upgrade customer's hash according to the latest algorithm
deploy
 deploy:mode:set                           Set application mode.
 deploy:mode:show                          Displays current application mode.
dev
 dev:source-theme:deploy                   Collects and publishes source files for theme.
 dev:tests:run                             Runs tests
 dev:urn-catalog:generate                  Generates the catalog of URNs to *.xsd mappings for the IDE to highlight xml.
 dev:xml:convert                           Converts XML file using XSL style sheets
i18n
 i18n:collect-phrases                      Discovers phrases in the codebase
 i18n:pack                                 Saves language package
 i18n:uninstall                            Uninstalls language packages
indexer
 indexer:info                              Shows allowed Indexers
 indexer:reindex                           Reindexes Data
 indexer:set-mode                          Sets index mode type
 indexer:show-mode                         Shows Index Mode
 indexer:status                            Shows status of Indexer
info
 info:adminuri                             Displays the Magento Admin URI
 info:backups:list                         Prints list of available backup files
 info:currency:list                        Displays the list of available currencies
 info:dependencies:show-framework          Shows number of dependencies on Magento framework
 info:dependencies:show-modules            Shows number of dependencies between modules
 info:dependencies:show-modules-circular   Shows number of circular dependencies between modules
 info:language:list                        Displays the list of available language locales
 info:timezone:list                        Displays the list of available timezones
maintenance
 maintenance:allow-ips                     Sets maintenance mode exempt IPs
 maintenance:disable                       Disables maintenance mode
 maintenance:enable                        Enables maintenance mode
 maintenance:status                        Displays maintenance mode status
module
 module:disable                            Disables specified modules
 module:enable                             Enables specified modules
 module:status                             Displays status of modules
 module:uninstall                          Uninstalls modules installed by composer
sampledata
 sampledata:deploy                         Deploy sample data modules
 sampledata:remove                         Remove all sample data packages from composer.json
 sampledata:reset                          Reset all sample data modules for re-installation
setup
 setup:backup                              Takes backup of Magento Application code base, media and database
 setup:config:set                          Creates or modifies the deployment configuration
 setup:cron:run                            Runs cron job scheduled for setup application
 setup:db-data:upgrade                     Installs and upgrades data in the DB
 setup:db-schema:upgrade                   Installs and upgrades the DB schema
 setup:db:status                           Checks if DB schema or data requires upgrade
 setup:di:compile                          Generates DI configuration and all non-existing interceptors and factories
 setup:di:compile-multi-tenant             Generates all non-existing proxies and factories, and pre-compile class definitions, inheritance information and plugin definitions
 setup:install                             Installs the Magento application
 setup:performance:generate-fixtures       Generates fixtures
 setup:rollback                            Rolls back Magento Application codebase, media and database
 setup:static-content:deploy               Deploys static view files
 setup:store-config:set                    Installs the store configuration
 setup:uninstall                           Uninstalls the Magento application
 setup:upgrade                             Upgrades the Magento application, DB data, and schema
theme
 theme:uninstall                           Uninstalls theme

如查看缓存状态,仅需输入php magento cache:status

php magento cache:status

Magento中如何自定义后台登录页面

很多人在使用Magento开发网站时除了会修改默认的后台登录路径外还会希望对后台登录页面甚至是后台进行修改。Alan使用Inchoo的插件并参照Mastering Magento Theme Design一书拼接了一个简单的代码,实现在后台登录页的自定义修改以及在后台页面中放置自己logo的功能。

这里我使用了Pinterest的logo在进行测试,以下是后台登录页面:

Magento自定义后台登录页面

以下是管理员找回密码页面:

Magento自定义找回密码页面

以下是后台替换了logo后的效果:

Magento后台自定义

显然在设计上还有很大的提升空间,所以这里贡献出源代码,供大家修改使用:

下载地址:http://pan.baidu.com/s/1eQKksca

使用方法:

1.解压后将app和skin目录拷贝到Magento的安装根目录下,然后登录后台。访问System>Configuration>Design,会看到下面多出一个Admin Theme的版块,在后面填写alanhou保存

Magento自定义Admin Theme

2.修改logo文件,我们的logo文件保存在<skin/adminhtml/default/alanhou/images/目录下,后台使用的是Pinterest-logo.png,登录页面使用的是Pinterest-logo-login.png,不想要修改源代码的朋友可以直接进行替换。

此外,也可以修改app/design/adminhtml/default/alanhou/template/下login.phtml和forgotpassword.phtml文件中的如下代码部分修改登录页面logo

$this->getSkinUrl('../../default/alanhou/images/Pinterest-logo-login.png') ?>"

修改app/design/adminhtml/default/alanhou/template/page/header.phtml文件中如下代码部分修改后台页面显示logo

echo $this->getSkinUrl('images/Pinterest-logo.png')

所有自定义css代码请在skin/adminhtml/default/alanhou/custom.css文件中进行修改

Magento 2常见问题

安装过程问题

../vendor/magento/framework/Code/Generator.php on line 103报错

这一报错通常出现在安装或升级Magento 2的时候,这时请尝试为根目录下的var文件夹设置 可写权限。以下为通过Mac的XAMPP安装时的完整报错信息:

Fatal error: Uncaught exception 'Magento\Framework\Exception\LocalizedException' with message 'Can't create directory /Applications/XAMPP/xamppfiles/htdocs/magento2/var/generation/Magento/Framework/App/ResourceConnection/.' in /Applications/XAMPP/xamppfiles/htdocs/magento2/vendor/magento/framework/Code/Generator.php:103 Stack trace: #0 /Applications/XAMPP/xamppfiles/htdocs/magento2/vendor/magento/framework/Code/Generator/Autoloader.php(35): Magento\Framework\Code\Generator->generateClass('Magento\Framewo...') #1 [internal function]: Magento\Framework\Code\Generator\Autoloader->load('Magento\Framewo...') #2 [internal function]: spl_autoload_call('Magento\Framewo...') #3 /Applications/XAMPP/xamppfiles/htdocs/magento2/vendor/magento/framework/Code/Reader/ClassReader.php(19): ReflectionClass->__construct('Magento\Framewo...') #4 /Applications/XAMPP/xamppfiles/htdocs/magento2/vendor/magento/framework/ObjectManager/Definition/Runtime.php(44): Magento\Framework\Code\Reader\ClassReader->getConstructor('Magento\Framewo...') #5 /Ap in /Applications/XAMPP/xamppfiles/htdocs/magento2/vendor/magento/framework/Code/Generator.php on line 103

missing PHP extensions intl.

很明显是缺失PHP组件,那么如何安装呢,先执行如下指令

cd ~ && curl -O http://ftp.gnu.org/gnu/autoconf/autoconf-latest.tar.gz 
tar -zxvf autoconf-latest.tar.gz 
cd autoconf-2.69 
./configure 
make 
sudo make install

安装过程可能会提示安装XCode或M4相关软件,请确认安装即可。下一步就是安装intl组件

sudo pecl install intl

安装后还需要在php.ini中加入组件的引用

sudo nano /Applications/XAMPP/xamppfiles/etc/php.ini
extension=intl.so //加入并保存

重启XAMPP中的Apache再次执行检查报错应该就会消失了

 

missing PHP extensions intl.

 

登录错误

You did not sign in correctly or your account is temporarily disabled.

开发者模式

 

30天备战Magento认证考试二

Day 2 Continued

Explain how Magento loads and manipulates configuration information

Magento的配置基本上散布在很多个.xml文件中,那么很自然地就有一个疑问,Magento是如何操作这些文件又从而为每个插件找到相应的配置文件的呢?下面我们先来梳理一下Magento结构的核心要点:

• Magento是一个模块化系统,功能分布在各个模块中;

• 有三个代码池,分别是local, community和core;

• 每个模块中包含app/code/[codePool]/Namespace/Modulename/config.xml (该文件 中存放基本模块配置)和app/etc/modules/Namespace_Modulename.xml (该文件存放代码池信息以及插件激活标记);

• Magento安装时的全局配置(包括数据库连接系统和后台地址)存放在app/etc/config.xml和app/etc/local.xml文件中;

如是我们从index.php开始追踪代码的话,会看到如下内容(在1.9.*的index.php中未找到这部分内容,使用的是Mage::run($mageRunCode, $mageRunType)):

Mage::run()
        self::$_config = new Mage_Core_Model_Config($options); (in CE 1.6.* and previous versions)
        self::_setConfigModel($options); (in СE 1.7.*)
        self::$_app    = new Mage_Core_Model_App();
        self::$_app->run(…);

或者

Mage::app()
        self::$_app    = new Mage_Core_Model_App();
        self::$_app-> init (…);

之后在两个版本中都有相似的类方法加载顺序。在Mage::run()中所有处理配置加载的内容都在Mage_Core_Model_App中并引用Mage_Core_Model_Config方法。调用Mage::app()会立即调用Mage_Core_Model_Config::init(),其中包含配置加载的进程。
最后会到达Mage_Core_Model_Config, 继承自Mage_Core_Model_Config_Base和Varien_Simplexml_Config。在这个层面调用方法无关紧要,让我们一起来看看Mage_Core_Model_Config:: init()方法(app/code/core/Mage/Core/Model/Config.php):

<?php /** * Initialization of core configuration * * @return Mage_Core_Model_Config */ public function init($options=array()) { // lets skip cache init and non-standard options stuff $this->loadBase();
        $cacheLoad = $this->loadModulesCache();
        if ($cacheLoad) {
            return $this;
        }
        $this->loadModules();
        $this->loadDb();
        $this->saveCache();
        return $this;
    }

让我们来逐行分析一下这个方法吧

$this->loadBase();

    /**
     * Load base system configuration (config.xml and local.xml files)
     *
     * @return Mage_Core_Model_Config
     */
    public function loadBase()
    {
        $etcDir = $this->getOptions()->getEtcDir();
        $files = glob($etcDir.DS.'*.xml');
        $this->loadFile(current($files));
        while ($file = next($files)) {
            $merge = clone $this->_prototype;
            $merge->loadFile($file);
            $this->extend($merge);
        }
        if (in_array($etcDir.DS.'local.xml', $files)) {
            $this->_isLocalConfigLoaded = true;
        }
        return $this;
    }

在最前面定义了一具app/etc目录的绝对路径,然后获取了此目录下的.xml文件列表,读取其中的内容并并入一个simpleXmlElement对象中。如果local.xml文件被载入了的话(也就意味着Magento已经完成了安装),则设置$this->_isLocalConfigLoaded = true;这会在后面用于实例化店铺并加载模块设置脚本。

只要这个方法不限制名称和载入文件数量,我们就可以在需要时载入app/etc中的自定义.xml文件。在开发服务器上不通过local.xml文件来指定数据库连接信息会比较有帮助,只使用该文件包含生产服务器的信息

<?php $cacheLoad = $this->loadModulesCache();
        if ($cacheLoad) {
            return $this;
        }

这部分代码不言自明,如果配置中激活了缓存并包含所要求的内容,就加载整个配置。我们还会用这一配置替换app/etc中已载入的配置,从缓存中载入(Mage_Core_Model_Config:: loadCache())并返回到Mage_Core_Model_App。

您可能会问如果所有的配置都能从缓存中载入,为什么不在扫描app/etc目录前就进和这一验证呢?因为Magento的缓存不仅可作为文件存放在var/cache中,也可以存放在apc, memcached和xcache中。从app/etc中载入的步骤允许设定用于存储缓存的类型和配置。

接下来是加载最为广泛的配置部分-模块配置。

$this->loadModules();

    /**
     * Load modules configuration
     *
     * @return Mage_Core_Model_Config
     */
    public function loadModules()
    {
        Varien_Profiler::start('config/load-modules');
        $this->_loadDeclaredModules();
 
        $resourceConfig = sprintf('config.%s.xml', $this->_getResourceConnectionModel('core'));
        $this->loadModulesConfiguration(array('config.xml',$resourceConfig), $this);
 
        /**
         * Prevent local.xml directives overwriting
         */
        $mergeConfig = clone $this->_prototype;
        $this->_isLocalConfigLoaded = $mergeConfig->loadFile($this->getOptions()->getEtcDir().DS.'local.xml');
        if ($this->_isLocalConfigLoaded) {
            $this->extend($mergeConfig);
        }
 
        $this->applyExtends();
        Varien_Profiler::stop('config/load-modules');
        return $this;
    }

Day 3

$this->_loadDeclaredModules();

_getDeclaredModuleFiles(): 首先扫描app/etc/modules目录获取指向系统所有模块的.xml文件路径列表。建立一个以base, mage和custom为键名的关联数组,仅有Mage_All.xml路径指向base版块,而Magento的基础包(core代码池app/code/core/Mage下)指向mage版块,custom版块涵盖其它的模块。最终会将所有内容并入一个数组,由于前面按键名拆分,最终数据的存储顺序是: Mage_All.xml, Mage命名空间里的模块,其它所有模块。

如果还没有明白Mage_All.xml的重要性的话,现在应该查看其中的内部结构了。Mage_All.xml包含了所有保证系统正常运行需要加载的模块。

然后collected.xml文件会加载到Mage_Core_Model_Config_Base $unsortedConfig。就会获得一个$moduleDepends数组,它是基于<depends>, <active>标记和模块名称。

$this->_sortModuleDepends($moduleDepends)在查看现存模块间的依赖性时会用到,在验证后会形成一个按照模块依赖关系排序的新的数组。

在_loadDeclaredModules()的最后会再次创建一个simpleXmlElement对象,并合并入前面创建的app/etc/*.xml中。

下面我们再加到loadModules()方法中:

<?php $resourceConfig = sprintf('config.%s.xml', $this->_getResourceConnectionModel('core'));
        $this->loadModulesConfiguration(array('config.xml',$resourceConfig), $this);

$resourceConfig中包含 config.mysql4.xml内容,因而下一步会载入config.mysql4.xml的配置以及config.mysql4.xml文件本身,目前尚不清楚config.mysql4.xml具体有什么作用。

这个方法首先会检测是否可以从local代码池中加入模块(注意app/etc/local.xml中false的值是否为true)。如果只允许使用community和core中的模块,Magento也会相应地修改include_path()。

<?php
        if ($disableLocalModules && !defined('COMPILER_INCLUDE_PATH')) {
            set_include_path(
                // excluded '/app/code/local'
                BP . DS . 'app' . DS . 'code' . DS . 'community' . PS .
                BP . DS . 'app' . DS . 'code' . DS . 'core' . PS .
                BP . DS . 'lib' . PS .
                Mage::registry('original_include_path')
            );
        }

通过这个简短的验证,Magento确保有一些simpleXmlElement设置(没有的话Magento会自动创建),然后会删除载入的配置为false和local的模块。
接着config.xml中剩余的模块以及config.mysql4.xml文件会被加载,然后loaded .xml会被加入到现有的文件中。如果最后加载的文件包含老的xpath, 系统会使用最后的值。

以前用$this->applyExtends();的调用配合自定义插件来改变/重载配置中的数据,但后续版本中并未采用。如果您已看到这部分的话,请仔细阅读一下下面的代码吧:

<?php /** * Prevent local.xml directives overwriting */ $mergeConfig = clone $this->_prototype;
        $this->_isLocalConfigLoaded = $mergeConfig->loadFile($this->getOptions()->getEtcDir().DS.'local.xml');
        if ($this->_isLocalConfigLoaded) {
            $this->extend($mergeConfig);
        }

不操作模块的话可以通过修改app/etc/local.xml中的数据,最后,调用$this->loadDb();方法

<?php /** * Load config data from DB * * @return Mage_Core_Model_Config */ public function loadDb() { if ($this->_isLocalConfigLoaded && Mage::isInstalled()) {
            Varien_Profiler::start('config/load-db');
            $dbConf = $this->getResourceModel();
            $dbConf->loadToXml($this);
            Varien_Profiler::stop('config/load-db');
        }
        return $this;
    }

这个方法使用了和core_config_data table相关的Mage_Core_Model_Resource_Config model模型资源,在这步我们从core_config_data载入数据到配置中:

1. 向站点中加入数据(查看core_website table)
2. 为现有站点加入各商店(store)数据(查看core_store table)
3. 根据域(scope)向core_config_data中加入数据
a. 先创建<default>块
b. 再创建<websites>块
c. 最后创建<stores>块
d. 每次迭代把域中的基本数据替换成更具针对性的数据 (还记得后台中的«use default», «use website»和«configuration scope» 吗?)
4. 自身配置 (如果遇到与现有网站不相关的数据,会进行删除)。

紧接着如果使用了缓存的话就会向缓存中写入配置。现在配置已经生成,但除了编译.xml文件外,我们还经常要从中读取数据,那么让我们回到Mage.php中查看相关方法:

<?php /** * Retrieve config value for store by path * * @param string $path * @param mixed $store * @return mixed */ public static function getStoreConfig($path, $store = null) { return self::app()->getStore($store)->getConfig($path);
    }
 
    /**
     * Retrieve config flag for store by path
     *
     * @param string $path
     * @param mixed $store
     * @return bool
     */
    public static function getStoreConfigFlag($path, $store = null)
    {
        $flag = strtolower(self::getStoreConfig($path, $store));
        if (!empty($flag) && 'false' !== $flag) {
            return true;
        } else {
            return false;
        }
    }

两个方法的唯一差别是 getStoreConfig() 会返回具体的值,而getStoreConfigFlag() 仅返回布尔值true或false。两个方法最终都调用Mage_Core_Model_Store::getConfig()方法:

<?php /** * Retrieve store configuration data * * @param string $path * @return string|null */ public function getConfig($path) { if (isset($this->_configCache[$path])) {
            return $this->_configCache[$path];
        }
 
        $config = Mage::getConfig();
 
        $fullPath = 'stores/' . $this->getCode() . '/' . $path;
        $data = $config->getNode($fullPath);
        if (!$data && !Mage::isInstalled()) {
            $data = $config->getNode('default/' . $path);
        }
        if (!$data) {
            return null;
        }
        return $this->_processConfigValue($fullPath, $path, $data);
    }

如果请求的信息无法在本地缓存中找到,该方法会使用stores/[store code]/[requested path]路径查找,如果依然没有结果,会使用另一个路径default/[requested path]在加载的配置中搜索。如果未查找到,则返回null。

查找到的数据由_processConfigValue()方法来进一步处理:

• 如果返回的是子节点,数据会循环存入本地缓存中。用户在当前会话再次调用时无需重新到配置中查找数据。
• 如果节点包含backend_model, 这个模型应按照指定格式获取数据。
• {{unsecure_base_url}}, {{unsecure_base_url}}, {{base_url}}的变量将由对应的数据进行替换。

最后总结一下

1. 所有.xml文件会汇集到一个大的simpleXmlElement 对象中

2. 数据首先从app/etc/*.xml中载入然后是app/etc/modules/*.xml。基于需加载模块的信息,会载入各模块etc目录下的config.xml。如果载入后台检测ACL权限并创建菜单元素,同时还会加载adminhtml.xmlsystem.xml。最后一步才是从数据库中载入配置数据。

3. 除开app/etc/local.xml中的其它所有参数都可以在自建模块的config.xml中进行重载。

P.S. 虽然Magento中提供了便捷的Mage:: getStoreConfig()Mage:: getStoreConfigFlag()方法,我们还是可以通过Mage::getConfig()->getNode($path, $scope, $scopeCode);来获取配置树中的任意元素。

可通过在根目录下运行如下代码来查看Magento安装后的最终配置信息:

<?php header("Content-Type: text/xml"); define('MAGENTO_ROOT', getcwd()); $mageFilename = MAGENTO_ROOT . '/app/Mage.php'; require_once $mageFilename; umask(0); /* Store or website code */ $mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : ''; /* Run store or run website */ $mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store'; Mage::app($mageRunCode, $mageRunType); $config = Mage::getConfig()->getNode()->asXml();
//file_put_contents('config.xml', $config);
echo $config;

Describe class group configuration and use in factory methods

Magento通过工厂方法来实例化..

 

注:考虑到时间的紧迫性,决定内容在后续补入,目前仅录入学习进度,上班族伤不起啊!!!

Describe the process and configuration of class overrides in Magento

Register an Observer

Identify the function and proper use of automatically available events, including *_load_after, etc.

Day 4

Set up a cron job

Describe how to plan for internationalization of a Magento site

Describe the use of Magento translate classes and translate files

Describe the advantages and disadvantages of using subdomains and subdirectories in internationalization

Day 5

Describe the steps for application initialization

Describe the role of the system entrypoint, index.php

Describe the role of the front controller

Identify uses for events fired in the front controller

Day 6

Describe URL structure/processing in Magento

Describe the URL rewrite process

Describe request routing/request flow in Magento

Describe how Magento determines which controller to use and how to customize route-to-controller resolution

Day 7

Describe the steps needed to create and register a new module

Describe the effect of module dependencies

Describe different types of configuration files and the priorities of their loading

Identify the steps in the request flow in which

1. Design data is populated

2. Layout configuration files are parsed

3. Layout is compiled

4. Output is rendered

30天备战Magento认证考试一

Day 1

使用Magento有挺长一段时间,但一直都限于泛泛的一些见招拆招的小功能,之前也参照Alan Storm的博客写过有关Magento开发的系列教程,不过在开发上实在没有什么更进一步的发展。于是决定通过备考Magento认证考试来进一步了解Magento的结构,从业余向专业过渡。

Magento开发系列之一 基础知识

Magento开发系列之二 配置文件

Magento开发系列之三 控制器

Magento开发系列之四 布局、块和模板

Magento开发系列之五 模型和ORM基础

Magento开发系列之六 安装、升级脚本

Magento开发系列之七 EAV-更高级的ORM

Magento开发系列之八 后台配置开发

Magento开发系列之九 后台开发进阶

Magento开发系列之十 Varien数据集合

Magento开发系列之十一 数据重载和升级

Magento开发系列之十二 默认系统配置

这次的计划是在年底前参加考试,大约一个月的准备时间,于是有了30天备战Magento认证考试的标题,难度还是挺大的,不管成功或者失败,都希望在本文中进行真实的记录为更多参加Magento认证考试的人们所参考。

由于一直没有机会接触Magento的Enterprise版,所以本次计划参加的考试为Magento Certified Developer Exam而不是Plus,官方的大纲如下:
链接: http://pan.baidu.com/s/1jGCnR8a 密码: sw6u

Basics

Describe Magento codepools

Describe Magento codepools

代码池在Magento根目录的app/code目录下,通常有系统自带的core代码池(可以拷贝到local文件夹中再进行相应的更改),通常不建议直接修改core代码池中的代码;第三方开发、共享插件(如Magento Connect)等使用的community代码池以及本地开发的local代码池。

那么系统是如何与各代码池进行交互的呢?

可以查看一下app/Mage.php文件中如下代码:

/**
 * Set include path
 */
$paths = array();
$paths[] = BP . DS . 'app' . DS . 'code' . DS . 'local';
$paths[] = BP . DS . 'app' . DS . 'code' . DS . 'community';
$paths[] = BP . DS . 'app' . DS . 'code' . DS . 'core';
$paths[] = BP . DS . 'lib';

$appPath = implode(PS, $paths);
set_include_path($appPath . PS . Mage::registry('original_include_path'));
include_once "Mage/Core/functions.php";
include_once "Varien/Autoload.php";

从这段代码中我们可以看出Magento中的加载顺序,即先是local代码池,然后community代码池,最后才是core代码池,也正是因为如此,我们可以在开发时通过重载来修改core中的类。

Describe typical Magento module structure

在Magento中通常一个模块会包含 Controller, Model, Helper, Block等目录,比如app/code/core/Mage/Checkout模板下的文件夹结构:

Magento Checkout模块下面我就来一一了解Magento中的模块组成

Block
Block文件夹对应MVC中的View,块文件通过模板文件调整模型,该目录下的文件载入数据库的数据并传给主题中的.phtml模板文件。
Controllers
控制器代表给定请求(如dispatch(), preDispatch(), postDispatch()等方法)的所有业务逻辑动作,并向系统其它部分传递命令。

etc
etc文件夹中包含所有声明和配置模块行为的xml文件,每个模块都至少要包含一个config.xml文件,在这里可以声明所有的model, router, block和helper等,该文件类似:

<config>
	<modules>
		<Namespace_Modulename>
			<version>0.1.0</version>
		</Namespace_Modulename>
	</modules>
</config>

Helper
Helper包含一些在整个系统常用到的一些工具方法,在helper中声明的方法,可以在模板文件或block, model, controller类中通过如下方式调用

Mage::helper('modulename/helpername')->methodName();

每个模块都有一个默认的Data帮助类(Modulename/Helper/Data.php),可以通过如下方式调用:

Mage::helper('modulename')->methodName();

 

Model
在传统的MVC中,Model用于连接数据库并从中读取或写入数据。在Magento中却并非如此,刚开始用时可能会不明就里,官方如是说:

Most Magento Models can categorized in one of two ways. There’s a basic, ActiveRecord-like/one-object-one-table Model, and there’s also an Entity Attribute Value (EAV) Model. Each Model also gets a Model Collection. Collections are PHP objects used to hold a number of individual Magento Model instances. The Magento team has implemented the PHP Standard Library interfaces of IteratorAggregate and Countable to allow each Model type to have it’s own collection type. If you’re not familiar with the PHP Standard Library, think of Model Collections as arrays that also have methods attached. Magento Models don’t contain any code for connecting to the database. Instead, each Model uses two modelResource classes, (one read, one write), that are used to communicate with the database server (via read and write adapter objects).

sql
处理所有在模板中使用的自定义数据表以及所有插件的升级。

etc/modules/Namespace_Modulename.xml

想要知道应使用哪些模块以及它们的位置,需要在所有xml文件的etc/modules/文件夹中创建一个xml文件

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

还可以在模块config.xml中指定depends, version和platform等参数:

Depends

<depends>
    <Mage_Catalog />
</depends>

Version

<version>0.1.0</version>

Platform

<platform>сe</platform> <!-- (ce=community edition,pe=professional etc.) -->

Day 2

Describe Magento templates and layout files location

一个主题通常包含如下元素

layout文件夹中包含用于定义主题布局的XML文件,这些文件就如同连接module(app/code目录下)和模板文件的胶水一样。

由于Magento的模块特性,默认主题的所有XML文件都存储在以模块名命名的单独文件夹中。而非默认主题,则会存放在所有布局更新所使用的local.xml文件中。

template文件夹中存放显示在前台的各Magento block用到的.phtml文件,该文件中包含php和html代码

locale文件夹中按语言存放.csv文件,命名规则为languagecode_COUNTRYCODE/translate.csv,如en_US或en_UK/translate.csv

Describe Magento skin and JavaScript files location

skin文件夹中包含模板文件夹下.phtml文件所使用到的Javascript, CSS和图片文件,也就是说这个文件夹下的内容每个主题都是不同的。从下图中可以看到其中包含三个文件夹,CSS文件夹中存放主题用到的层叠样式表,images文件中存放主题使用的媒体文件,JS文件夹中存放模板用到的Javascript文件。

Magento下skin文件夹内容结构

js文件夹存放在前台和后台中使用的js文件,库文件和框架文件。如果要添加一个新的Javascript/AJAX库或者local代码池中要用到一些特殊的脚本,那么就都放到这里。

Magento根目录js文件夹下文档结构

Identify and explain the main Magento design areas (adminhtml and frontend)

Magento模板系统都由三个组件构成,有存储在叫做Block的模块文件夹中的php类,它们从数据库中加载数据并传输到主题PHP/HTML模板文件中(.phtml)。也有构建页面结构和组成部分的XML配置文件。

Magento的design目录结构

所有的前台文件都存放在图示design目录下的三个的文件夹中。

install 此文件夹存放安装过程中需使用到的内容

adminhtml 登录admin后台所看到的所有内容都放在这里。也就说如果模块需要用到任何与admin面板有关的内容,都可以在这里找到对应的模板和布局文件。

frontend 所有访客在前台看到内容都存放在这里。

 

那么完整的主题结构是什么样的呢?

Package

安装完Magento社区版后,会有两个包:base和default(后续版本中还有rwd),企业版中的名称为base和enterprise。

Themes

主题由一系列的layout, template, locale和skin文件组成,也就是网站的对外展示部分。Magento中可以加载多个主题,而主题可以分类两大类:

  • Default theme

每个包都会有一个叫做default的主题,Magento会自动搜索default主题并载入到前台中使用。要想要改变前台,可以个性化default主题或开发非默认主题,显然后者更为可取。

  • Non-default theme

非默认主题在default基础上进行修改或者通过CSS, 布局文件等形成完全不同的展现。

Explain class naming conventions and their relationship with the autoloader

Magento是在Zend框架的基础上进行开发的,所以其类的命名规则也取自Zend Framework。Magento中根据文件位置来对类名进行标准化,这样有助于类的自动加载(autoloader),而无需逐一require_once和include_once来包含文件。由于/在类名中属于非法字符,会使用下划线_来进行替代。

比如说Mage_Catalog_Model_Product类就在app/code/core/Mage/Catalog/Model/Product.php文件当中,Magento的autoloader将路径中的斜杠/全部替换成了下划线_,并在下面的文件夹中进行查找

app/code/core

app/code/community

app/code/local

lib/

注:app/code/local可通过将app/etc/local.xml文件中的如下字段设置为true来取消使用:

<disable_local_modules>false</disable_local_modules>

关于分类的搜索顺序,前面有提到是通过app/Mage.php文件中的如下代码来实现的

/**
     * Set include path
     */
    $paths[] = BP . DS . 'app' . DS . 'code' . DS . 'local';
    $paths[] = BP . DS . 'app' . DS . 'code' . DS . 'community';
    $paths[] = BP . DS . 'app' . DS . 'code' . DS . 'core';
    $paths[] = BP . DS . 'lib';
 
    $appPath = implode(PS, $paths);
    set_include_path($appPath . PS . Mage::registry('original_include_path'));
    include_once "Mage/Core/functions.php";
    include_once "Varien/Autoload.php";

Varien_Autoload类用于自动加载(lib/Varien/Autoload.php):

class Varien_Autoload
{
...
/**
     * Register SPL autoload function
     */
static public function register()
{
        spl_autoload_register(array(self::instance(), 'autoload'));
}
  /**
     * Load class source code
     *
     * @param string $class
     */
    public function autoload($class)
    {
...
   $classFile = str_replace(' ', DIRECTORY_SEPARATOR, ucwords(str_replace('_', ' ', $class)));
...
   $classFile.= '.php';
     return include $classFile;
    }

在Magento中创建对象

Magento提供了特别的方法用于创建model, helper和block,使用缩短了类名的Mage全局类。例如,获取Mage_Catalog_Model_Product模型,常会使用Mage :: getModel (‘catalog / product’)。

catalog – Mage_Catalog_Model的缩写
product – 用于决定用到的类

类名与缩写的名称的关联发生在模块的配置文件中(…/etc/config.xml)

<!--?xml version="1.0"?-->
 
                <!-- a shortened name -->
                Mage_Catalog_Model
 
                <!-- a shortened name -->
                Mage_Catalog_Block
 
                <!-- a shortened name -->
                Mage_Catalog_Helper

Describe methods for resolving module conflicts

首先需要确定是什么类型的错误,然后才能找到相对应的方案。一般有三种模块兼容性错误。

1) Conflicts in configuration files
2) Conflicts with the software part
3) Conflicts in a module display

Conflicts in configuration files

我们来讨论一下配置文件错误产生的原因以及解决方法。第一步查看位于app/etc/modules文件夹下的模块定义文件。在该文件中会设置模块是否active以及所属的代码池,另外还可以通过<depends></depends>标签对来设置依赖关系。

比如有两个模块使用同样的类依赖关系的话,可能会由于类构造器的不当使用而产生冲突。这类错误与后面会讲到的软件部分错误有着紧密的关联。如果遇到因<depends>的不当使用产生错误,可以通过设置其中一个依赖于另一个类,并在稍后的类继承中使用该依赖关系。方法为:

a) 将如下代码:

Namespace_OtherModulename.xml

<config>
    <modules>
        <Namespace_OtherModulename>
            <active>true</active>
            <codePool>community</codePool>
    <depends><Mage_Something/></depends>
        </Namespace_OtherModulename>
    </modules>
</config>

Namespace_Modulename.xml

<config>
    <modules>
        <Namespace_Modulename>
            <active>true</active>
            <codePool>community</codePool>
    <depends><Mage_Something/></depends>
        </Namespace_Modulename>
    </modules>
</config>

b)替换为如下代码:
Namespace_OtherModulename.xml

<config>
    <modules>
        <Namespace_OtherModulename>
            <active>true</active>
            <codePool>community</codePool>
    <depends><Mage_Something/></depends>
        </Namespace_OtherModulename>
    </modules>
</config>

Namespace_Modulename.xml

<config>
    <modules>
        <Namespace_Modulename>
            <active>true</active>
            <codePool>community</codePool>
    <depends><Namespace_OtherModulename/></depends>
        </Namespace_Modulename>
    </modules>
</config>

Conflicts with the software part

前面提到过产生模块错误的主要原因能过是依赖关系和类rewrite造成的。这应该查看模块配置文件 (/Namespace_Modulename/etc/ config.xml), 这里可以通过<rewrite></rewrite>标签对来进行类重写,比如:

<customer>
    <rewrite>
         <form_edit>Namespace_Modulename_Block_Rewrite_BlockClass</form_edit>
     </rewrite>
</customer>

Namespace_Modulename/etc/config.xml

<customer>
    <rewrite>
         <form_edit>Namespace_Modulename_Block_Rewrite_BlockClass</form_edit>
     </rewrite>
</customer>

Namespace_OtherModulename/etc/config.xml

<customer>
    <rewrite>
         <form_edit>Namespace_OtherModulename_Block_Rewrite_BlockClass</form_edit>
     </rewrite>
</customer>

1) 第一步删除第一个模块中的重写,代码如下:

<customer>
    <rewrite>
         <form_edit>Namespace_Modulename_Block_Rewrite_BlockClass</form_edit>
     </rewrite>
</customer>

2) 使用第一个类继承第二个类:

Class Namespace_Modulename_Block_Rewrite_BlockClass extends Namespace_OtherModulename_Block_Rewrite_BlockClass

尽管这一方法非常灵活,但却有着一些缺点,比如需要仔细查看这两个类中的相同函数,可能会存在返回值和parent::functionName();方法使用的问题。但这仍是在代码层面解决模块错误的重中之重。

Conflicts in a module display

在使用多个模块时最常见的错误是在前端显示模块的错误,发生这一错误有几个原因:在布局设置中的block重载,对同一block应用不同的模板,删除另一个模块中要内嵌的block。那么让我们来进一步看看会发生模块错误的地方:

主题配置文件:app/design/frontend/default/your_theme/layout

模板文件:app/design/frontend/default/your_theme/template

如果模块在前端不展示或者显示错误,极有可能错误就发生在这些地方。

首先查看你的文件和其它模块用于前台显示设置的模块文件,位于app/design/frontend/default/your_theme/layout。如果多个模块使用相同的block却应用了不同的模板,就需要修改为只使用一个模板。可在这个模板文件中并入其它去除掉的模板的代码。

另一个常见错误是把模块标准block的名称给修改了,这时其它通过<reference></reference>标签对使用这一block的模块就会出错。还有一些情况,模块使用了自己的模板却与其它模块不太兼容,这时就要具体情况具体对待了。

How does the framework interact with the various codepools?

我们在前面已经不止一次的提到,Magento会先包含local代码池,其次community,最后才是core代码池。通过这一顺序可以让开发人员在不直接修改core中的文件的情况下实现系统类的重载。

Core代码池

这个文件夹中存放着所有实现Magento强大、灵活的功能的核心代码,强烈建议不要修改其中的代码。

Community代码池

该文件来为第三方开发者所提供,所有第三方开发的插件代码默认都会存放在app/code/community文件夹中。

Local代码池

如果您想要自己对网站进行调整、想要修改某些逻辑或者重载系统中的类,都可以在local代码池中实现。

What constitutes a namespace and a module?

命名空间或者说工厂名请首字母大写这样有助于Autoloader的加载

What does the structure of a complete theme look like?

主题文件夹结构:

Magento主题文件夹结构

skin文件夹结构

Magento skin文件夹结构

Explain how Magento loads and manipulates configuration information

 

Magento Facebook, Twitter登录插件

社交网站已成功占据网民们越来越多的时间,不论是一个内容站还是电商站,社交分享按钮都早已成为标配。社交分享按钮在很多模板中都已添加,甚至在Magento自带的rwd包默认模板中也已加入了Facebook和Twitter的分享按钮,此外还可以采用AddThis这样的免费代码来集成分享功能。

Magento rwd社交分享按钮

分享以外为了降低获取用户的成本、改善用户体验各大网站也开始纷纷添加了Facebook等账号登录的功能,这样做的目的是一个避免了客户看到冗长的注册表格后产生的较高的跳出率,更深层的目的也是为了能够打入客户的关系链。

关于Magento社交登录有不少付费插件,这里不再赘述。今天介绍的这款免费插件是由做Magento开发比较资深的Inchoo网站发布的,最近一次的更新时间是2014年9月5号:

Inchoo社交登录插件

下载地址: http://pan.baidu.com/s/1e7A3c 密码: ttk2

更新地址:https://github.com/Marko-M/Inchoo_SocialConnect

下载后进行压缩将app和skin两个文件夹中的内容复制到Magento的安装根目录下,登录后台System > Configuration > Customers > Customer Configuration下会出现Social Connect Facebook Options等配置组

社交登录插件后台显示

以Facebook登录为例,若要开启此功能,打开Facebook开发者页面https://developers.facebook.com/,点击导航栏My Apps下的Add a New App,在弹出窗口中选择Website

Facebook中添加网站应用

在新出现的窗口中输入一个标识名称如localtest,点击下面的Create New Facebook App ID按钮

创建Facebook ID

然后在弹出窗口中选择一个分类如Business点击Create App ID

创建新APP ID

在新的页面中输入网址,这里在本地测试使用http://localhost/magento,点击Next

添加网址

此时再点击上面导航中的My Apps就会出现我们所创建的App(这里的名称为localtest),点击进入,就可以获取得App ID和App Secret(点击右边的Show按钮并输入密码验证),将这个两个值分别填入后台中的Facebook App ID和Facebook App Secret然后保存。

获取APP ID和密钥

接下来需要激活这个App,在同一个页面左侧导航中点击Settings然后在Contact Email下输入一个有交的email地址,保存然后点击左侧导航上的Status & Review按钮,此时将Do you want to make this app and all its live features available to the general public?右侧的滑块置为YES便可以在Magento中使用Facebook登录了

激活Facebook APP

 

此时访问http://localhost/magento/index.php/customer/account/login/等页面就会出现Facebook的登录按钮

Facebook登录

常见错误

1.在进行Facebook登录时出现App Not Setup: This app is still in development mode, and you don’t have access to it. Switch to a registered test user or ask an app admin for permissions.报错

这是因为没有激活所创建的App,请参照本节激活步骤

Facebook登录报错

2.集成后出现Unable to Connect to ssl://graph.facebook.com:443. Error #154298408: Unable to find the socket transport “ssl” – did you forget to enable it when you configured PHP?报错

这很明显是由于未开启ssl,以XAMPP为例,打开php.ini文件,并取消如下语句前的分号注释,保存并重启Apache

extension=php_openssl.dll

Linux中为extension=php_openssl.so

 

Magento开发系列之十二 默认系统配置

本节并没有太多新内容,更多的是对前面有关系统后台配置的补充。在我们创建新的系统配置路径时,Magento并没有存储默认值,甚至对于一些系统默认配置也是如此,这点可以通过查看core_config_data表来进行验证。

mysql> select * from core_config_data;
+-----------+---------+---------+----------------------------+--------+
| config_id | scope   | scope_id| path                       | value  |
+-----------+---------+---------+----------------------------+--------+
|         1 | default |        0| general/region/display_all | 1      |
... ...

这张表仅存储在后台或基它程序中明确设置的值,而如果请求一个没有进行这个设置的系统配置值的话,Magento会到全局配置文件中去查看默认值。虽然不要求这么做,但为自添加配置变量设置一个默认值是一个不错的习惯。这样做很简单,也防止在获取到空值时产生一些意想不到的效果。

上面提到默认值存放在全局配置文件中,这可能与很多的想法大相径庭,因为大家可能会认为这默认值会存储在用于配置后台的system.xml文件中。为什么要这么做其实大可不必深究,我们可以理解为配置文件中存储常用的值,而stystem.xml中存储着用于修改这些值的界面的配置。

在模块配置文件config.xml里添加一个<default />代码块(如app/code/local/Packagename/Modulename/etc/config.xml)

<config>
    <!-- ... -->
    <default>
    </default>
    <!-- ... -->
</config>

这就是我们用于存储默认值的最上级节点,接下来将配置路径转化成XML格式的树形节点,比如我们为以下配置路径设置默认值:

design/header/welcome

那么在config.xml的代码就会是这样:

<config>
    <!-- ... -->
    <default>
        <design>

<header>
                <welcome>Default welcome msg!</welcome>
            </header>

        </design>
    </default>
    <!-- ... -->
</config>

应用了这个配置后,请求design/header/welcome时,如果没有设定值,就会返回”Default welcome msg!”。这个例子是基本系统默认的配置,我们业看看design/header/welcome的真实配置(文件地址:app/code/core/Mage/Page/etc/config.xml):

<default>
    <design>
        <head translate="default_description" module="page">
            <default_title>Magento Commerce</default_title>
            <default_description>Default Description</default_description>
            <default_keywords>Magento, Varien, E-commerce</default_keywords>
            <default_robots>*</default_robots>
            <default_media_type>text/html</default_media_type>
            <default_charset>utf-8</default_charset>
        </head>

<header translate="welcome" module="page">
            <logo_src>images/logo.gif</logo_src>
            <logo_alt>Magento Commerce</logo_alt>
            <logo_src_small>images/logo.gif</logo_src_small>
            <welcome>Default welcome msg!</welcome>
        </header>

<footer translate="copyright" module="page">
            <copyright>&amp;copy; 2015 Magento Demo Store. All Rights Reserved.</copyright>
        </footer>

    </design>
    <system>
        <media_storage_configuration>
            <allowed_resources>
                <site_favicons>favicon</site_favicons>
            </allowed_resources>
        </media_storage_configuration>
    </system>
</default>

这是所有design/*的默认配置,和我们例子中不同的时这里有一个translate属性:

<head translate="default_description" module="page">

translate和module属性告诉系统哪些节点需要被translate,以及使用哪个模块的Data Helper来进行这一操作。在上例中,welcome节点将被转化为:

Mage::helper('page')->__(...);

前面我们也提到过,如果调用helper类时URI没有传入第二部分,会默认使用data,也就是说下面两句代码是一样的:

Mage::helper('page')->__(...);
Mage::helper('page/data')->__(...);

如果想要转化多个子节点,可以在名称之间用逗号进行分隔,如:

<example translate="foo,baz,bar" module="page">

写在后面

本系列更新到此结束,一共十二篇,大多数代码并非出自笔者之手,而是来自Magento资深大师Alan Storm,笔者只是用自己的理解丰富了一下内容,以有助于国内Magento学习者更为有效地掌握Magento开发相关知识。本系列的终结只是一个阶段性的符号,Magento作为一套强大的电商系统还有很值得探讨和学习的地方,需要大家共同努力去研究和分享,让我们一起努力吧!

Magento开发系列之十一 数据重载和升级

在Magento经常被鼓吹也常被滥用的功能就是重载core中的系统代码,而另一个开发者经常讨论的话题就是升级以及重载对升级的阻碍作用。本节我们就来一起看看重载给版本切换所带来的不便。

需要强调下我们这里的是修改Maento中core的业务逻辑,对于phtml模板文件的修改是非常普遍的。

不论是在Magento中还是其它系统中对升级最不友好的肯定是直接修改源代码,比如想要修改产品模型时,就直接编辑了如下文件:

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

一旦这么做,就直接修改Magento中的基础代码,因而在进行升级时就需要进行逐个文件的合并,这通常都会出问题。此外这种修改还可能返回系统无法识别的数据或者是得到计划外的数据。虽然我们不建议去修改源代码,但还是有很多开发者在刚开始时会去这么做。在开发新项目时可以从官网下载一份全新的系统文件,然后比对下lib和app/code/core文件夹内的代码,看看核心代码有没有被修改。

Magento或者说PHP会在下面的目录中搜索类文件

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

由于这个原因以及PHP构造时包含的顺序,在core/code/local中复制一个core文件系统就会先包含这个文件,所以如果想要修改产品模型的功能,就可以进行如下复制:

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

如果这里定义的是类而不是core文件,就无需包含核心文件,这也就避免了合并文件的麻烦,并且所有修改文件都会放在同一个目录结构中。这样虽然比直接修改核心源代码要好些,但还是有可能会修改掉一些重要方法,比如前面说到的产品模型中的getName方法

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

在对这个方法进行重载时,我们有可能不小心添加了代码导致重载后返回空

/**
* 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
}

系统的其它部分可能会使用到这个方法并等待该方法返回一个字串,而我们的修改则会导致接下来的执行被中断。这里如果返回对象情况还会更糟,因为调用一个空方法会导致一个致命错误(Fatal)。

下面我们来看看同一模型中的validate方法

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;
}

在修改时我们有可能不小心删除了dispatch事件

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

这时系统中其它依靠这一事件的部分都会停止运转。此外,在升级时同样存在着危险,如果升级中更新了类,而我们仍将使用旧的过时了的方法。也就是说我们还是需要在升级时进行手动的代码合并。

Magento中的类重载和系统重写依赖于创建模型、Helper和Block时使用的工厂模式,比如在执行如下代码时:

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

实际是在告诉Magento去查找catalog/product用到的类并进行实例化。接下来,Magento会付出查看系统配置文件,并在config.xml中查看针对catalog/product应该使用哪个类,然后Magento就会对类进行实例化并返回一个模型。

而当我们在Magento中重载一个类时,实际上修改了配置文件,在进行上述操作时,就会告诉系统:如果要实例化catalog/product模型的话,请不要使用core中的类,而使用我们定义的类Myp_Mym_Model_Product

同时,在定义我们自己的类时,需要继承原来的类

class Myp_Mym_Model_Product extends Mage_Catalog_Model_Product
{
}

这样,我们的新类就会包含系统类中的功能,同时也就避免了在升级时还需要合并文件的功能,也不会在升级时包含过时的方法。但是在修改方法时还是存在问题,我们还是以getName和validate方法为例,我们还是会忘记返回值或返回错误的值,甚至是忘记添加方法中重要功能的代码块:

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
    }      
}

重载重写系统无法在这个层面上形成保护,但也提供了一些避免的方式。同于我们是继承了原来的类,因而可以在构造时使用parent::来调用原类中的方法:

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;
    }      
}

通过调用原来的方法,就可以确保该进行的操作都会完成,并且也减少了返回非系统要求值的可能性。当然只能是减少,而非彻底消除,开发者还是需要负责在自建代码中返回和原方法相同的对象或其它值。也就是,即使应用了重载系统,系统还是可能会由于误操作导致崩溃。

鉴于此,我们在进行开时,应使重载最小化,重载时可以在最后添加如下代码:

return parent::originalMethod();

如果重载时要求先运行原方法,可以使用如下方法:

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

mymodule/immutable指向如下类

class Alanhou_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;
    }
}

这样并不能保证$original_return不会被我们自己或其它人重写,但确实会在修改后更容易发现。如果在重载类的方法最后没有使用$original_return->getValue或parent::method,那么在debug时就很容易发现问题。

还有的时候我们会希望修改core方法中的返回值,在有这种需求时,定义一个新方法来调用原方法并在主题中调用新方法往往更为安全:

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;
    }
}

这样可以保证原方法的一些功能依然有用,原来的返回类型保持不变,而我们也可以在系统中的某些地方添加自己的逻辑。