你现在的位置:首页 > PHP网站建设知识库 > magento > 正文

magento开发教程

Magento是现在用的最广泛而且功能强大的online eCommerce平台,正在改变着eEcommerce的现状。Magento同样也是一个面向对象的PHP框架(an object-oriented PHP Framework)(can be used to develop modern,dynamic web applications that tap into Magento's powerful eCommerce features.)

在本节中,将介绍Magento编程框架的特性。

Code Organized in Modules
Configuration-Based MVC
Controllers
Context-Based URI Model Loading
Models
Helpers
Layouts
Oberrvers
Class Overrides
Wrap Up


1.Code Organized in Modules  
Magento通过将代码放入独立的模块进行组织。在一个典型的PHP模型-视图-控制器(Model-View-Controller,MVC)的应用程序,所有的控制器将 放在一个文件夹中,所有的模型放在另一个文件夹中。在Magento中,文件都是基于功能进行分组的,这种分组后的代码块就称之为模块(Module).

Magento代码

Magento的结账功能,你可以在下面代码中的文件夹中看到,同时可以找到Controllers,Models,Helpers,Blocks,等等
app/code/core/Mage/Checkout

Magento的谷歌的Checkout功能,你也仅需找到下列代码中的文件夹,就能获取Controllers,Models,Helpers,Blocks,等等
app/code/core/Mage/GoogleCheckout

你的代码:
如果你想定制或者是扩展Magento,不要直接编辑核心文件,也无需把你新的Controllers,Models,Helpers,Blocks,等放在Magento代码旁,你可以创建属于你自己的模块(Modules),即所有对于Magento的扩展,都将在local文件夹中进行。
app/code/local/Package/Modulename

Package (也常叫做Namespace),是一个识别你的公司或者组织的独一无二的字串符。通过Package,世界范围内的Magento社区在创建模块扩展时,能够使用他们自己的Package名称,以避免与其它开发者命名有冲突。

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

此文件夹中包含2个文件,第一个文件用来一个单独的模块,命名模式为:Packagename_Modulename.xml
第二个文件从Package/Namespace中开启多个模块,命名模式为:Packagename_All.xml 
大家注意的是,它仅用于该文件 Mage_All.xml的core team。这里不建议激活在一个单独的文件中激活多个模块,因为这样就打破了你的模块的模块化Modularity。

2.Configuration-Based MVC
Magento是一个基于配置的MVC(Configuration-Based MVC)系统,另外一种MVC系统则是大部分PHP框架使用的,约定性MVC(Convertion-Based MVC)

如果你想在约定性MVC系统中添加一个新的Controller(控制器)或者是一个新的Model(模型),你仅需要创建一个文件或类,系统会自动识别。

在基于配置的系统中,例如Magento,除了在代码库中添加新的文件或者类之外,你还需要告诉系统这个新类或者这组新类。在Magento中,每个模块都有一个config.xml 文件 。这个文件包含一个Magento模块中的所有相关配置。在运行时,所有的文件都会被加载到一个巨大的配置树中。

例如,想要在定制模块中使用模型,你需要在config.xml中添加下列代码,来告诉magento你想使用这个模型。
<models>
         <packagename>
                  <class>Packagename_Modulename_Model</class>
        <packagename>
</models>

这种配置不仅限于模型,同样适用于 Controllers,Helpers,Blocks,Routes等等,都可以在config.xml中进行相关的配置。

3.Controllers
在任何PHP系统中,核心文件自然是PHP文件。Magento中也不例外,Magento的核心文件是:index.php

但是,永远不要编辑index.php的代码。在MVC系统中,index.php包括下面:
    1.检查URL地址
    2.根据一些规则集,将该URL到一个控制器类和一个动作方法(称为路由)
    3.控制器类实例化并调用Action方法(称为调度)
这意味着 Magento(或任何MVC系统)每一个有效的entry point都是控制器文件中的一个方法。看下下面的URL:
http://example.com/catalog/category/view/id/25

每个部分的路径解析后的服务器名称,如下所示。

Front Name-catalog
URL的第一部分称为 front name。它用来指示Magento应该在哪个模块中寻找URL中的控制器。在这个例子中,catalog就是Front Name,对应于catalog模块。
app/code/core/Mage/Catalog

Controller Name-category
URL的第二部分指示Magento中应该配置的控制器Controller。每个控制器的模块都有一个包含'controllers'的文件夹,用来存放该模块下的所有控制器。在上诉例子中,匹配了下面的控制器文件:
app/code/core/Mage/Catalog/controllers/CategoryController.php

4.Context-Based URI Model Loading
现在我们已经知道了Action方法的切入点,接下来应该开始使用实例化类来做些什么了。Magento提供了一个特殊的方式去实例化模型,Helpers和Blocks,即使用Mage全局类提供的静态工厂方法。例如:
Mage::getModel('catalog/product');
Mage::helper('catalog/product');

字串符‘catalog/product’称作 Grouped Class Name,通常叫做 URI。Grouped Class Name的第一部分(本例中为:catalog)用来查询该类存在在哪个模块当中。第二部分(本例为product)用来决定哪个类应该被调用(读取)。

所以,上述例子中,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

这些规则都受每个模块配置文件所约束。当你创建你个人定制的模块时,你将会拥有属于你自己的分组类名( classgroups)与Mage::getModel('myspecialprefix/modelname');.
其实,你没有必要使用Groupd Class Names去实例化你的类。但是在接下来的学习中,这一好处你就会体验得到。

5.Magento Models
Magento像现在需要的框架一样,提供一个Object Relational Mapping(ORM)系统。ORMs把你从单纯的写SQL语句中解救出来,并允许你通过纯粹的PHP代码操作数据库。例如:
$model = Mage::getModel('catalog/product')->load(27);
$price = $model->getPrice();
$price += 5;
$model->setPrice($price)->setSku('SK83293432');
$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('SK83293432');

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

Magento的ORM系统中还包含一种通过Collections接口查询多个对象的方式。下例会读取系统中所有5美元的产品。
$products_collection = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('*')
->addFieldToFilter('price','5.00');

这里我们又一次看到了链接调用方法的形式。Collections使用PHP标准程序库来面向对象去获得数组如属性。
foreach($products_collection as $product)
{
    echo $product->getName();
}

你现在也许会在想"addAttributeToSelect" 方法是干嘛的。Magento有两种形式的模型对象。一个是传统意义上的“一个对象,一张表”的Active Record模型,即一个实例对应表中的一行记录。当你实例化这些模型时,所有属性都会被自动选择。一个是实体-属性-值Entity Attribute Value (EAV)模型。实体(entiry)表示所描述的数据项,例如一个产品或汽车。属性(attribute)表示描述实体的数据,例如一个产品将有价格,重量和许多其他属性。值(value)是属性的值,例如产品可能有一个9.99英镑的价格属性。此外值可以基于数据类型进行分割,所以可将EAV表分为字符串、整数、日期和长文本(long text)表。依据数据类型分割是为了支持索引,使得数据库执行可能的类型检查验证。传统的关系表模型将所有的属性组成为一张表的字段,这样的优点在于,数据的读取简单,然而,一旦出现业务的变更,属性的增加和删除,就需要对相应的数据表进行更改,不仅操作繁琐,并且有一定的风险。EAV数据库模型将模型的属性以记录的形式存放在数据表中,即使需要新增和移除属性,仅仅需要删除数据表中相应的记录即可,而无需修改数据库结构。便于业务的扩展与灵活性的需要。如果你想创建一个EAV对象时,你只需要使用addAttributeToSelect就可以得到你想要的columns,或者addAttributeToSelect('*')得到所有的columns。

5.Hepers
Magento Helper类包含一些实用的方法,这些方法可以使你对对象和变量进行日常任务的操作。例如:
$helper = Mage::helper('catalog');
这里你发现了我们停止使用了 grouped class name的第二部分。每个模块都有一个默认的数据Helper类。下面这个就同等与上面的例子:
$helper = Mage::helper('catalog/data');

大多数Helpers继承自 Mage_Core_Helper_Abstract,默认的提供给你很多的有用方法。
$translated_output =  $helper-

6.Layouts
我们已经介绍了Controllers,Models和Helpers。在典型的PHP MVC系统中,在操作过模型之后,我们会:
        1.设置变量到视图中
        2.系统会自动读取一个默认的“outer”HTML布局>
        3.接着系统会将视图读取到外部布局中
但是,如果你看到一个典型Magento Contoller action,你会看到下列:
/**
* View product gallery action
*/
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();
}

所以,V在Magento MVC中明显与你经常使用的不同,你需要在控制器中明确的输出布局。

Magento布局本身也是不同的。Magento布局是一个包含嵌套式或树状的Block对象的对象。每个Block对象输出一个特定的HTML,输出HTML的环节包含两个部分,PHP代码组成的Block以及.phtml模板文件。

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

例如,页面头部的Block app/code/core/Mage/Page/Block/Html/Head.php用的是头部.phtml文件page/html/head.phtml.

换言之,Block 类就像是mini-controllers,而.phtml文件就像是视图

默认情况下,当你调用:
$this->loadLayout();
$this->renderLayout();

Magento会读取该网站布局框架。这些结构Blocks用来输出head,body以及设定单栏或多栏的布局。另外,还有一些内容Blocks负责实际输出像导航,默认欢迎信息等。

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

如果你需要添加一个内容到布局中,你需要告诉Magento如下:
"Hey, Magento, add these additional Blocks under the "content" Block of the skeleton"

或者:
"Hey, Magento, add these additional Blocks under the "left column" Block of the skeleton"

这可以通过Controller action进行编程:
public function indexAction()
{
    $this->loadLayout();
    $block = $this->getLayout()->createBlock('adminhtml/system_account_edit')
    $this->getLayout()->getBlock('content')->append($block);
    $this->renderLayout();
}

但是更常用的是(至少在前端购物车应用中)XML布局系统。

在每个控制器基础,一个主题的布局XML文件允许你删除正常输出的Blocks,活添加一个默认skeleton区域Block。例如,下面的布局XML文件:
<catalog_category_default>
      <reference name="left">
            <block type="catalog/navigation" name="catalog.leftnav" after="currency" template="catalog/navigation/left.phtml"/>

这是说在目录模块,控制器分类,默认Action,把目录/导航Block嵌入到左侧的structure Block中,使用catalog/navigation/left.phtml模版。

关于Blocks最重要的一点是,你会看到很多类似下面的模版代码:
$this->getChildHtml('order_items')

这是Block如何输出一个嵌入Block。但是,如果布局XML文件中的嵌入Block包含子Block,一个Block只能输出一个子Block。在上诉例子中,目录/导航Block没有嵌入Blocks。这就意味着调用任何$this->getChildHtml() in left.phtml将会输出空白。

但是如果我们遇到:
<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>

从目录/导航Block,我们可以调用$this->getChildHtml('foobar');

7.Observers

跟很多优秀的面向对象系统,Magento通过实现Event/Observer模式来留住用户。在页面请求时一个特定的工作的发生(模型保存,用户登录等),Magento将会生成一个事件信号。

当你在创建你自己的模块时,你可以收听到这些事件。也就是说,如果你想在客人登录商店,都获得一个邮件,你可以通过用户登录事件中收听到(在config.xml中设置)
<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
    }
}

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

这里有个例子可以更好的帮助你理解。一个产品的模型类是Mage_Catalog_Model_Product。无论何时下面的代码被调用,就会一个Mage_Catalog_Model_Product对象
$product = Mage::getModel('catalog/product');

这是工厂模式factory pattern。

Magento类重写系统允许你告诉系统:
"Hey, whenever anyone asks for a catalog/product, instead of giving them a Mage_Catalog_Model_Product,
give them a Packagename_Modulename_Model_Foobazproduct instead".

如果你想,你的Packagename_Modulename_Model_Foobazproduct类可以扩展成原始的产品类。
class Packagename_Modulename_Model_Foobazproduct extends Mage_Catalog_Model_Product
{
}

你可以改变类的方法的行为,但是要keep住现有方法的功能性。
class Packagename_Modulename_Model_Foobazproduct extends Mage_Catalog_Model_Product
{
    public function validate()
    {
        //add custom validation functionality here
        return $this;
    }

}

复写需要在config.xml文件中设置。
<models>
    <!-- does the override for catalog/product-->
    <catalog>
        <rewrite>
            <product>Packagename_Modulename_Model_Foobazproduct</product>
        </rewrite>
    </catalog>
</models>

Note:你的模块中的个别类要复写别的模块的个别类。你是不允许复写全部模块的。允许改变特定方法行为,无需受其余的模块影响。