Modules->模块

Controller->控制器

Model->模型

Magento是这个星球上最强大的购物车网店平台。当然,你应该已经对此毫无疑问了。不过,你可能还不知道,Magento同样是一个面向对象的PHP框架。你可以配合Magento购物车程序强大的功能,开发动态WEB应用程序。

这是Magento中文开发手册的开篇,我们会在整个手册中介绍绝大部分Magento的开发框架特性。不要想在这片文章中立刻掌握所有的特性。这仅仅是个开始,但是足够让你在同行中鹤立鸡群了。

在这片文章中,你将了解到:

  • Magento模块(Magento Modules)代码组织形式
  • 配置型MVC架构
  • Magento控制器(Magento Controllers)
  • 基于URI的模型实例化(Context-based URI Model Loading)
  • Magento模型(Magento Models)
  • Magento助手(Magento Helpers)
  • Magento布局(Magento Layouts)
  • 事件监听(Observers)
  • Magento类重写(Class Overrides)
  • 总结

开始之前,你可以试着看下Magento MVC模式的一个图形化直观体现。Magento_MVC.pdf

Magento模块中的代码组织形式

Magento通过将代码放入独立的模块进行组织。在一个典型的PHP MVC应用中,所有的控制器会被放在一个文件夹中,所有的模型会被放在另外一个文件夹里,等等。而在Magento中,文件是基于功能进行分组的,这种分组后的代码块叫做模块。

Magento的代码:

举例来说,如果你想寻找Magento中关于付款的功能,你仅仅需要找到下面代码中的文件夹,就能获取所有的控制器,模型,助手,Blocks等。

app/code/core/Mage/Checkout

如果你想寻找Magento中关于Google Checkout的功能,也仅仅需要找到如下文件夹,即可获取所有你想要的信息。

app/code/core/Mage/GoogleCheckout

你的代码:

如果你想扩展Magento,千万不要想当然的去修改core文件夹中的文件,也不要将你自己的控制器,模型,助手或者Blocks放在Core文件夹中。所有对于Magento的扩展,都将在local文件夹中进行。

app/code/local/<Package>/<Modulename>

Package(也可称为命名空间,当然这不是PHP手册中提到的命名空间)是唯一的命名,通过Package来标识你的公司,组织或个人。通过Package,世界范围内的Magento社区在创建模块扩展时,能够使用他们自己的Package名称,以避免与其他开发者有命名冲突。

创建一个新的模块时,你需要告诉Magento新模块的相关信息。可以通过添加一个XML文件在下面的目录中。

app/etc/modules

在这个目录中有两类xml文件,第一种用来开启独立的模块,以下列方式命名:

Packagename_Modulename.xml

第二种文件用来从一个Package中开启多个模块,以下列方式命名:

Packagename_All.xml

配置型MVC系统

Magento是一个配置型MVC(Configuration-based MVC)系统。另外一种MVC系统则是大部分PHP框架使用的,约定性MVC(convertion-based MVC)。

在约定型MVC系统中,如果你添加一个控制器,或者一个模型,只需要根据约定的内容,创建这个文件以及类即可,系统会自动识别它。

而在配置型MVC系统中,比如Magento,除了需要添加相应的文件及类之外,还需要明确的告诉系统该类的存在。在Magento中,每个模块都有一个config.xml文件。这个文件中包含了一个模块相关的配置信息。在运行时,所有模块的配置文件,都会被加载到一个巨大的配置文件树中(后面的文章会介绍如何查看这个配置树)。

比如,想在模块中使用模型。你需要添加类似下面的代码,来告诉Magento你会在这个模块中使用这个模型。

<models>

<packagename>

<class>Packagename_Modulename_Model</class>

</packagename>

<models>

当然,这种配置不仅限于模型,对于控制器,助手,Blocks,路由,事件句柄等都需要在该模块的config.xml中进行相关的配置。

Magento控制器(Magento Controllers)

在任何PHP系统当中,核心文件肯定是PHP文件。Magento也不例外,index.php是Magento的核心文件。

不过,永远不要编辑index.php中的任何代码。在MVC系统中,index.php的左右大概有以下几项:

  • 检测URL地址。
  • 根据路由规则,将访问的URL地址分发到控制器类中的方法。
  • 初始化控制器,并调用相应的动作方法。这一步骤叫做分发。Dispatching。

这意味着Magento(或任何MVC系统)每一个有效的entry point都是控制器文件中的一个方法。一起来看下面这个URL:

http://example.com/catalog/category/view/id/25

上述域名后URL地址可以被分拆为以下几个部分。

Front Name – catalog

该URL的第一部分被称为Front Name。它用来指示Magento应该在哪个模块中寻找URL中的控制器。在这个例子中,catalog就是Front Name,对应于catalog模块。

Controller Name – Category

第二部分指示Magento应该匹配的控制器。每个拥有控制器的模块都包含一个‘controllers’的文件夹,用来存放该模块下的所有控制器。上述URL地址,匹配了下面这个控制器文件。

app/code/core/Mage/Catalog/controllers/CategoryController.php

其中的类定义格式大概为:

class Mage_Catalog_CategoryControllerextends Mage_Core_Controller_Front_Action

{

}

在Magento中,所有的控制器都继承自Mage_Core_controller_Front_Action类。

Action Name – view

第三部分是一个action方法的名称。在此URL中,view便是一个action方法的名字。

class Mage_Catalog_CategoryControllerextends Mage_Core_Controller_Front_Action {

public function viewAction() {

}

}

Paramater/Value – id/25

任何位于action方法名之后的路径,都会被认为是key/value形式传递的GET变量。那么在我们的例子当中,’id/25′表示有一个值为25的$_GET['id']变量。

如前所述,如果你想让自定义模块使用控制器,你必须对它进行配置。下面是在模块中开启控制器的代码。

<frontend>

<routers>

<catalog>

<use>standard</use>

<args>

<module>Mage_Catalog</module>

<frontName>catalog</frontName>

</args>

</catalog>

</routers>

</frontend>

现在不清楚上述内容都是什么意思还没关系,但是注意<frontName>catalog</frontName>。这是用来关联模块与URL地址中frontname的。Magento核心代码选择将一个模块的名字与frontname一致,但这不是强制规定的。

Multiple Routers

上面提到的路由规则主要是针对Magento购物车程序(即你所能看到的前端)。如果Magento在URL中无法匹配到正确的控制器/动作,它会尝试使用针对Admin程序(后台管理端)的另一套路由规则。如果依旧无法正确匹配,它会使用一个特殊的控制器Mage_Cms_IndexController。

CMS控制器会检查Magento内容管理系统中是否有内容需要输出,如果有内容输出,则读取该内容,如果找不到,则输出404页面。

例如,Magento默认的首页就是在使用CMS控制器。

Context-Based URI 模型读取

目前为止,我们已经建立了一个控制器以及一个方法,到实例化一个类做点什么的时候了。Magento提供了一种特殊的方式去实例化模型,助手以及Blocks,即使用Mage全局类提供的静态工厂方法。例如,

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

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

‘catalog/product’字符串被称为Grouped Class Name。通常叫做URI。Grouped Class Name的第一部分用来指示该类存在于哪个模块当中。第二部分用来决定哪个类将被调用。

那么,上述例子中,‘catalog’对应于app/code/core/Mage/Catalog模块,也就意味着我们的类名将以Mage_Catalog开头,然后根据调用的类型,将product类名加入到最后一部分。即,

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

Mage_Catalog_Model_Product;

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

Mage_Catalog_Helper_Product;

Magento 模型

和现在的多数框架一样,Magento也提供ORM支持。ORM让你能够专注于数据,而非无尽的SQL语句。例如,

$model = Mage::getModel('catalog/product')->load(27);

$price =$model->getPrice();

$price += 5;

$model->setPrice($price)->setSku('SK1231414');

$model->save();

在上面这个例子中,我们调用了“getPrice”和“setPrice”方法。然而,在Mage_Catalog_Model_Product类中并没有此方法。那为什么上面这个例子能够使用这些方法呢?因为Magento的ORM系统中使用了PHP的_get和_set魔术方法。

调用$product->getPrice()会获取模型属性price,而调用$product->setPrice()会设置price属性。当然,所有的这些都假设模型类没有getPrice和setPrice方法。如果它们存在于模型类中,PHP魔术方法会被忽略。如果你有兴趣知道这是如何实现的,可以参考Varien_Object类,所有的模型类都继承自该类。

如果你想获取模型当中所有的数据,可以直接调用$product->getData()方法,它会返回包含所有字段的一个数组。

你可能已经注意到上例中的方法存在使用->符号链接的形式:

$model->setPrice($price)->setSku(‘SK12312542′);

能够使用这种方式调用方法,最主要的原因是所有的set方法都会返回一个模型的实例。你会经常在Magento的核心代码中看到此类调用方法的形式。

Magento的ORM系统中还包含一种通过Collections接口查询多个对象的方式。下例会读取系统中所有5美元的产品。

$product_collection = Mage::getModel('catalog/product')

->getCollection()

->addAttributeToSelect('*')

->addFieldToFilter('price','5.00');

这里我们又一次看到了链接调用方法的形式。Collections use the PHP Standard Library to implement Objects that have array like properties.(这句超出理解范围)。

foreach($products_collection as $product)

{

echo $product->getName();

}

在上面的一个例子当中,你可能注意到了addAttributeToSelect方法。这里单独提到此方法,是因为它代表了Magento模型中的一个类别。Magento拥有两种形式的模型对象。一种是传统的“一个对象,一张表”的Active Record模型。当你实例化这些模型的时候,所有的属性都会被自动选取。

Magento中第二种模型叫做Entity Attribute Value(EAV)模型。这种模型会按照一定的规律将数据分散存储在数据库不同的表中。EAV模型的高级特性,让Magento不用在增加一种产品属性的时候改变数据库模型(一般的购物车系统在增加新的属性时,有两种方式,一种是增加数据库字段,一种是使用预留的空字段。),从而保证了Magento系统的高度扩展性。当创建一个EAV模型的collection时,Magento会conservative in它会查询的字段数,所有你可以使用addAttributeToSelect来制定你想获取的列,或者使用addAttributeToSelect(*)来获取所有列。

Magento Helpers 助手

Magento的助手类包含一系列实用的方法,通过这些方法可以对对象及变量做日常性的操作。例如,

$helper = Mage::helper('catalog');

是否注意到这里舍弃了Grouped Class Name的第二部分?每个模块都有一个默认的data助手类。下面的语句与上面的作用是相同的,即默认使用模块下的data助手类。

$helper = Mage::helper('catalog/data');

大部分的助手类继承自Mage_Core_Helper_Abstract,默认提供了很多使用的方法。

$translated_output =$helper->__('Magento is Great');

if ($helper->isModuleOutputEnabled()) {

}

Magento Layout 布局

目前为止,我们已经介绍了控制器,模型以及助手。在典型的PHP MVC系统当中,在操作模型之后,一般会

  • 传递变量到视图中。
  • 系统会自动读取默认的外层布局
  • 接着将视图读取到外层布局中

不过,如果你仔细观察Magento 控制器动作方法,你不会看到这些步骤,

public function galleryAction() {

if (!$this->_initProduct()) {

if (isset($_GET['store']) && !$this->getResponse()->isRedirect()) {

$this->redirect('');

}elseif (!$this->getResponse()->isRedirect()) {

$this->_forward('noRoute');

}

return;

}

$this->loadLayout();

$this->renderLayout();

}

不同于典型PHP MVC形式的是,控制器动作方法,以两个输出布局的方法结束。所以说,Magento MVC系统中的V视图部分可能与你经常使用的大相径庭。因为,你必须在控制器中明确的输出布局。

并且,Magento的布局本身也区别与你经常使用的MVC系统。Magento布局是一个包含嵌套或者树状的Block对象的对象。每一个Block对象输出一部分HTML,输出HTML的环节包含两个部分,PHP代码组成的Block以及.phtml模板文件。

Blocks对象负责与Magento系统交互并从模型中获取数据,而phtml模板文件则为页面生成必须的HTML代码。

例如,页面头部Block文件app/code/core/Mage/Page/Block/Html/Head.php使用与其对应的page/html/head.phtml模板文件。

换种方式说的话,Blocks类就像迷你控制器,而.phtml文件就是视图文件。

默认的,当你调用,

1

2

$this->loadLayout();

$this->renderLayout();

Magento will load up a Layout with a skeleton site structure(此段能够理解,但想不到最佳翻译,大概意思是Magento会读取网站的布局框架)。这些结构Blocks用来输出head,body以及设定单栏或多栏的布局。另外,还有一些内容Blocks负责实际输出像导航,产品分类等。

“结构”和“内容”Blocks在布局系统中是随意设置的。一般不会在代码中刻意添加代码,从而区分一个Block是结构还是内容,但是Blocks要么属于“结构”,要么属于“内容”。

为了添加一个内容Blocks到布局中,你需要告诉Magento系统

“Magento,快把这几个Blocks添加到内容Block 里”

或者

“Magento,把这边几个Blocks放到“左边栏”结构Block里”

这些可以通过控制器中的代码进行控制,

public function indexAction() {

$block =$this->getLayout()->createBlock('adminhtml/system_account_edit');

$this->getLayout()->getBlock('content')->append($block);

}

但是更常用的方式(至少在前台购物车应用中)是使用基于XML文件的布局系统。

在一款风格中,基于XML文件的布局 允许你删除正常输出的Blocks或者添加默认的skeleton区域(即Structure Blocks)。例如下面这个XML布局文件,

<catalog_category_default>

<reference name="left">

<block type="catalog/navigation" name="catalog.leftnav" after="currency" template="catalog/navigation/left.phtml" />

</reference>

</catalog_category_default>

上面这段代码的作用是,在catalog模块的category控制器的默认动作方法中,将catalog/navigation Block插入到左边栏结构Block中,并使用catalog/navigation/left.phtml模板文件。

关于Blocks还有一个比较重要的特性。在模板文件中,你会看到很多类似下面的代码,

1

$this->getChildHtml('order_items')

这是Block输出套嵌Block的方式。但是,只有在XML布局文件中明确声明一个Block包含另一个子Block时,才能在模板文件中通过getChildHtml()方法调用子Block的模板文件。

例如,在XML布局文件中,

<catalog_category_default>

<reference name="left">

<block type="catalog/navigation" name="catalog.leftnav" after="currency" template="catalog/navigation/left.phtml">

<block type="core/template" name="foobar" template="foo/baz/bar.phtml" />

</block>

</reference>

</catalog_category_default>

那么从catalog/navigation Block中,我们才可以调用$this->getChildHtml(‘foobar’);

Observers 观察者

和许多优秀的面向对象系统一样,Magento通过实现观察者模式给用户作为钩子。对于在页面请求时(模型存储,用户登录等)调用的特定动作方法,Magento会生成一个事件信号。

当创建新的模块时,你可以“监听”这些事件。比如说,你想在特定用户登录商店的时候发一封邮件到管理员信箱里,可以通过“监听”customer_login事件做到。

<events>

<customer_login>

<observers>

<unique_name>

<type>singleton</type>

<class>mymodule/observer</class>

<method>iSpyWithMyLittleEye</method>

</unique_name>

</observers>

</customer_login>

</events>

接下来是当用户登录时应该运行的代码:

class Packagename_Mymodule_Model_Observer

{

public function iSpyWithMyLittleEye($observer)

{

$data =$observer->getData();

//code to check observer data for out user,

//and take some action goes here

}

}

Class Overrides 类的复写

最后,Magento系统还提供了类的复写功能,你可以通过自己的代码覆盖核心代码里的模型,助手,和Blocks类。

下面举些例子帮助你更容易理解这个功能。产品的模型类是Mage_Catalog_Model_Product.无论何时,下面的代码被调用时,就会生成一个Mage_Catalog_Model_Product对象。

1

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

这是工厂模式。Magento的类复写系统运作模式大概是这样,

“Magento! 如果有请求对应catalog/product,不要实例化Mage_Catalog_Model_Product,让Packagename_Modulename_Model_Foobazproduct接手”

在模型中,类的覆盖方式及命名规则如下,

class Packagename_Modulename_Model_Foobazproductextends Mage_Catalog_Model_Product

{

}

通过这种方式,你可以改变父类中方法的行为,并且完全继承父类的所有功能。

class Packagename_Modulename_Model_Foobazproductextends Mage_Catalog_Model_Product

{

public function validate() {

//添加一些自定义的验证功能

return $this;

}

和之前所说的一样,复写同样需要在config.xml配置文件中进行配置。

<models>

<!-- tells the system this module has models -->

<modulename>

<class>Packagename_Modulename_Model</class>

</modulename>

<!-- does the override for catalog/product-->

<catalog>

<rewrite>

<product>Packagename_Modulename_Model_Foobazproduct</product>

</rewrite>

</catalog>

</models>

Magento 使用心得的更多相关文章

  1. 我的MYSQL学习心得(一) 简单语法

    我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...

  2. NoSql数据库使用半年后在设计上面的一些心得

    NoSql数据库这个概念听闻许久了,也陆续看到很多公司和产品都在使用,优缺点似乎都被分析的清清楚楚.但我心里一直存有一个疑惑,它的出现究竟是为了解决什么问题? 这个疑惑非常大,为此我看了很多分析文章, ...

  3. 我的MYSQL学习心得(二) 数据类型宽度

    我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...

  4. 我的MYSQL学习心得(三) 查看字段长度

    我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...

  5. 我的MYSQL学习心得(四) 数据类型

    我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(五) 运 ...

  6. 我的MYSQL学习心得(五) 运算符

    我的MYSQL学习心得(五) 运算符 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...

  7. 我的MYSQL学习心得(六) 函数

    我的MYSQL学习心得(六) 函数 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类 ...

  8. 我的MYSQL学习心得(七) 查询

    我的MYSQL学习心得(七) 查询 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类 ...

  9. 我的MYSQL学习心得(八) 插入 更新 删除

    我的MYSQL学习心得(八) 插入 更新 删除 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得( ...

随机推荐

  1. BZOJ_1008_[HNOI2008]_越狱_(简单组合数学+快速幂)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1008 监狱有连续编号为1...N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰 ...

  2. -_-#【减少 DOM 访问】“离线”更新节点,再将它们添加到树中

    Minimize DOM Access javascript 之 DOM 优化 <!DOCTYPE html> <html> <head> <meta cha ...

  3. Rose

    <html> <head> <title>JS小尝试</title> <meta http-equiv="Content-Type&qu ...

  4. spoj 1812 LCS2(SAM+DP)

    [题目链接] http://www.spoj.com/problems/LCS2/en/ [题意] 求若干个串的最长公共子串. [思路] SAM+DP 先拿个串建个SAM,然后用后面的串匹配,每次将所 ...

  5. 如何使用C#和VB发送和接收MSMQ消息

    在这篇博客中,我们将就如何实现System.Messaging类发送和接收的XML消息发送从MSMQ队列,你可能会遇到接收的XML消息的一些问题. 我们将首先加入参考System.Messaging ...

  6. 【Java基础】Java类及成员和修饰符的关系

    修饰符的分类 权限修饰符:private,默认的,protected,public 状态修饰符:static,final 抽象修饰符:abstract 类的修饰符 权限修饰符:默认修饰符,public ...

  7. HDOJ-ACM1006(JAVA)

    题意:输入一个角度degree,求出一天中时针分针秒针之间的角度大于这个角度degree的时间占一天总时间的比例 因此输入是0-120度, 输出比例,保留三位小数,-1为终止 暂时没想出来如何做这道题 ...

  8. 问题-Delphi记忆工程打开的单元(XE2设置项)

    问题情况:每次在delphi中打开了N个单元的窗口,关闭delphi后,第二天还得一个一个打开单元窗口.问题原因:这是因为delphi的记忆功能未打开.问题处理:Tools->Options.. ...

  9. 问题-RZ安装后报错“RzBorder.pas”

    错误象现:[Error] RzBorder.pas(1429): Number of elements differs from declaration [Fatal Error] RzEdit.pa ...

  10. IOS网络多线程-GCD

    Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main queue,通过dispat ...