Magento 开发笔记4

原文链接

http://alanstorm.com/magento_models_orm


Magento模型和ORM基础

Models Tier的实现是任何一个MVC模式里重要的部分。它表示了应用的数据部分,并且大部分应用是不可能没有数据的。MagentoModels的作用更大,因为典型上来说他们包含了“Business Logic”,而在其他PHPMVC框架里,这些逻辑通常会被分配给Controller或者Helper。

传统的PHP MVC模型

虽然MVC的定义有些模糊,但是Model的定义更模糊。之前PHP开发者广为接受的MVC模型中,数据的存储通常是raw SQL声明或者SQL抽象层(如AdoDB)。开发者应该写queries,并且不应该对他们的创建的model考虑太多。

在2009,rawSQL是不太受欢迎了,但是很多PHP框架仍旧是以SQL为中心的。Model对象是作为抽象层,但在这个理念后面,开发者仍旧是写着SQL或者调用SQL的抽象方法来存取数据。

另外一些框架隐藏了SQL,采用了Object Relation Mapping(ORM)方法。在这种情况下,一个开发者严格的与对象打交道。我们设置属性,然后调用对象的save方法,数据就会自动写入数据库。一些ORM会试图把对象属性和数据库分开,另一些则要求用户明确他们的关系(例如抽象语言YAML)。这种方式里最有名的一种是ActiveRecord。

ORM的定义应该比较清楚了,但是正如其他计算机科学里的概念一样,ORM的严格定义这么多年来还是没有明确。这些就是本文之外的东西了。

MagentoModels

Magento也采用了ORM模型。当ZendFramework SQL抽象可以利用,大部分的数据存储就可以通过MagentoModels,或者是我们自建models的内建模型来处理。Magento同时有高度的灵活性,抽象性,也带来了一定的迷惑性。

Magento Model剖析

大部分的MagentoModels可以被分为两类。基础的:ActiveRecord-like/one-object-one-table模型;EAV模型:EntityAttribute Value Model。同样还有Model集合。集合是存有一些单独MagentoModel实例的PHP对象。Varian小组实现了IteratorAggregate 和 Countable 的PHPStandard Library 的标准接口,可以让每个Model类型有它自建的collection类型。如果不熟悉PHP Standard Library,可以认为Model集合就是一个数组,同时还有一些方法和它绑定。

Magento模型没有任何SQL代码与数据库相连。相反,每个模型有两个ModelResources,(一个读,一个写),用以和数据库交互(通过读和写的适配器对象)。通过把逻辑Model和数据库交互代码的解耦,理论上来说,就可以为新的databaseplatform写新的resource class时,不必动Models。

创建一个新的基本Model

我们将会创建一个基本的Magento Model。PHP MVC传统来说,我们会创建一个weblogpost。我们将会

1.    创建一个“Weblog” 模块

2.    为模块创建一个对应的数据库表

3.    增加Model—Blogpost config的Model信息

4.    增加Model—Blogpost config的ModelResource 信息

5.    增加Blogpost Model config的ReadAdapter

6.    增加Blogpost Model config的WriteAdapter

7.    增加Blogpost Model的php文件

8.    增加Blogpost Model的php文件

9.    实例化Model

创建Weblog Module

创建过程跟之前类似,略过不再废话。直接进入以“testModel”命名的IndexAction Controller设置route的步骤。

在XStarX/Weblog/etc/config.xml,  设置以下路径

    

        

            

standard            

                

Alanstormdotcom_Weblog                weblog            

        

    

       

然后在Action Controller里增加下面的方法

XStarX/Weblog/controllers/IndexController.php

classAlanstormdotcom_Weblog_IndexController extendsMage_Core_Controller_Front_Action {        

public function testModelAction() {        

echo 'Setup!';    

}

}

清除Magento的缓存,加载下面的URL来保证每个设置的正确

http://localhost/magento/weblog/index/testModel

可以看到“Setup”在白色的背景上。

创建数据库表

Magento在系统上可以自动创建和改变数据库,但是作为说明还是还是手动建立数据表。

在MySQL命令行下,用下面的脚本创建数据库表

CREATE TABLE`blog_posts` (  

`blogpost_id` int(11) NOT NULL auto_increment,  

`title` text,  

`post` text,  

`date` datetime default NULL,  

`timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,          PRIMARY KEY  (`blogpost_id`)

);

然后插入一条数据

INSERT INTO`blog_posts` VALUES (1,'My New Title','This is a blog post','2009-07-0100:00:00','2009-07-02 23:12:30');

全局配置和创建Model

创建Model需要在配置文件中做下面4件事情

1.    在我们的模块中Enabling Models

2.    在我们的模块中Enabling Model Resources

3.    给我们的ModelSource增加实体entity。对简单的Model来说,就是表名。

4.    为我们的specificModel Resource 明确读接口

5.    为我们的specificModel Resource 明确写接口

然后实例化Model时,就可以用

$model =Mage::getModel('weblog/blogpost');

获取model的URI的第一个部分是Model Group Name。因为Magento的_autoloads类,这个是模块的小写名字即可。URI的第二个部分,是Model的名字。

因此在config.xml里增加下面的XML

    

    

        

            

XStarX_Weblog_Model            

            

weblog_mysql4                

    

外部标签是我们的Group Name,应该匹配我们的Module名字。是Base名字,所有的weblog组里的Models都会有这样的名字。标签表明了那个ResourceModel会使用。后续会有更多解释。

虽然我们还没有完成,但是先看看发生了什么。如果清除cache,获取blogpostModel。在testModelAction里,改成下面的代码

public functiontestModelAction() {        

$blogpost = Mage::getModel('weblog/blogpost');        

echo get_class($blogpost);    

}

重新加载页面。会得到一个异常(确保打开了开发者模式)

include(XStarX/Weblog/Model/Blogpost.php)[function.include]: failed to open stream: No such file or directory

要得到weblog/blogpost Model, 你告诉Magento要实例化类

XStarX_Weblog_Model_Blogpost

Magento试图用__autoload加载该模型,但是找不到文件。所以我们来创建函数

XStarX/Weblog/Model/Blogpost.php

class XStarX_Weblog_Model_Blogpostextends Mage_Core_Model_Abstract  {    

protected function _construct()    {        

$this->_init('weblog/blogpost');    

}   

}

重新加载页面,就会出现类的名字。

所有的Model都是继承自Mage_Core_Model_Abstract类。这个类强制你要自己实现_construct(与PHP默认的构造函数不同,__construct).

全局配置和资源                                    

我们设置了Model。下面,我们需要设置Model Resource。ModelResource包含了直接和数据库对话的代码。在上一部分,我们在config里包含了下面的

weblog_mysql4

的之会用来实例化Model Resource。尽管我们不需要自己去调用,但是weblog组中的任何Model都需要和database交互。Magento会用下面的方法去获得Modelresource

Mage::getResourceModel(‘weblog/blogpost’);

Weblog是组名,blogpost是Model。方法是Mage::getResourceModel方法会用weblog/blogpostURI来查找全局config,然后获得里的值(这里是weblog_mysql4)。模型类会通过下面的URI来实例化

Weblog_mysql/blogpost

因此,如果通过这种方式,就意味着,和普通Model一样,resourcemodels是在XML config中同样的部分配置的。这可能会带来混淆。

然后来配置我们的资源,在部分

    

    

                

        

            

Alanstormdotcom_Weblog_Model_Mysql4                          

增加了标签,它是标签的值. 的值是基本名字,所有的resource models都有,而且应该按照下面的方式命名

Packagename_Modulename_Model_Mysql4

一次我们有了配置好的资源,让我们试着加载一些Model data。修改action如下

public functiontestModelAction() {    

$params = $this->getRequest()->getParams();    

$blogpost = Mage::getModel('weblog/blogpost');    

echo("Loading the blogpost with an ID of".$params['id']);    

$blogpost->load($params['id']);         

$data = $blogpost->getData();    

var_dump($data);    

}

清空cache,然后加载下面的url

http://localhost/magento/weblog/index/testModel/id/1

又会遇到下面的异常

Warning: include(Star/Weblog/Model/Mysql4/Blogpost.php)[function.include]: failed to open stream: No such file ....

正如你可能感觉到的,我们需要增加一个resource类到我们的模型。每个model都有自己的resource class。 在下列位置增加类

app/code/local/XStarX/Weblog/Model/Mysql4/Blogpost.php

class XStarX_Weblog_Model_Mysql4_Blogpostextends Mage_Core_Model_Mysql4_Abstract{    

protected function _construct()    {        

$this->_init('weblog/blogpost', 'blogpost_id');    

}   

}

_init方法里的第一个参数是用来定位Model的URL。第二个参数是database的field,这个域用于区分其他column。在大多数情况下,这个应该是primary key。清除cache,重新加载,

Loading theblogpost with an ID of 1 

array

empty

因此我们解决了exception,但是我们仍然没有读到数据。为什么?

每个Model group都有一个读 adapter,和一个写adapter。Magento容许Model使用默认的adapter,或者开发者使用自己的。无论哪种方式,我们需要告诉Magento。我们增加新的叫做的标签到我们module的部分。

     

    

        

            

                

core_write            

        

        

            

                

core_read            

        

                                  

        

我们在增加了两个子部分。一个给写,另一个给读。标签名字( &)是基于我们上面定义的组名字”weblog”.

这些准备好后,清除cache,重新加载页面

Can't retrieveentity config: weblog/blogpost

又发生了一个新的异常。我们用Model URI weblog/blogpost, 我们告诉了Magento我们想要ModelGroup weblog,和blogpost实体。在继承Mage_Core_Model_Mysql4_Abstract的简单Models里,一个实体对应一个表。在这种情况下,我们上面创建的blog_post表。让我们把这个实体增加到XML配置文件里。

        

        

            

Alanstormdotcom_Weblog_Model_Mysql4                             

                    

blog_posts
                

            

                         

    

我们增加新的部分。这也就有了entity后面的这就明确了我们对这个model我们想要使用哪个数据表。

清除缓存,重新加载页面

Loading theblogpost with an ID of 2 

Loading theblogpost with an ID of 1 

array  

'blogpost_id' => string '1' (length=1)  

'title' => string 'My New Title' (length=12)  

'post' => string 'This is a blog post' (length=19)  

'date' => string '2009-07-01 00:00:00' (length=19)  

'timestamp' => string '2009-07-02 16:12:30' (length=19)

 

我们现在能成功的获取了数据。更重要的是,完成了Magento Model的配置。

全局配置和资源                                    

所有的Magento Models继承自Varien_Object类。这个类是Magento系统库的一部分,不是core module。你可以在下面的路径找到它

lib/Varien/Object.php

Magento Models存储数据在一个叫做protected data的属性了。Varian_Object类给了我们几个方法,这样我们可以用来获取data。你已经看到了getData, 它返回一个key/value对的数组。这个方法同样可以传入一个stringkey,来得到这个特定域的值。

$model->getData();

$model->getData(‘title’);

还有一个方法叫getOrigData,它返回对象初始话时的Modeldata(配合_origData方法)

$model->getOrigData();

$model->getOrigData(‘title’);

Varien_Object同样通过PHP的magic __call方法,实现了一些特殊的方法。你可以对任何属性进行get,set,unset,或者check操作。

$model->getBlogpostId();

$model->setBlogpostId(25);

$model->unsetBlogpostId();

if($model->hasBlogpostId()){...}

由于这个原因,我们最好把数据库的列名用小写来命名,并且在单词之间用下划线连接。

$id = $model[‘blogpost_id’];

$model[‘blogpost_id’] = 25;

June 1, 2011  magento可能在ArrayAccess 方法上有bug(但是未经验证)。比较Varien_Object的setData方法(用magic方法)

public function setData($key, $value=null) {    

$this->_hasDataChanges = true;    

if(is_array($key)) {        

$this->_data =$key;    

} else {        

$this->_data[$key] =$value;    

}    

return $this;

}

下面是offsetSet

public function offsetSet($offset, $value) {    

$this->_data[$offset] = $value;

}

在setData方法里,_hasDataChanges属性被置为真。如果这个标志是false,Magento不会执行Update Code。这个可能是性能优化,但是没有考虑到数组的存储。可能好的标志是核心小组成员喜欢的magicmethod。

如果我们创建了自己的Models,你可能要去重写offsetSet,然后再调用父类的,

public function offsetSet($offset, $value) {    

$this->_hasDataChanges = true;    

return parent::offsetSet($offset, $value);

}

下面是Varien_Object

/** 

* Attribute getter (deprecated)  *

* @param string $var 

* @return mixed 

*/ 

public function __get($var) {    

$var = $this->_underscore($var);    

return $this->getData($var);

/** 

* Attribute setter (deprecated)  * 

* @param string $var 

* @param mixed $value 

*/

public function __set($var, $value) {    

$var = $this->_underscore($var);    

$this->setData($var, $value);

}

CRUD,magento的方式

Magento模型支持CRUD基本的Create,Read,Update,和Delete功能,是通过load, save, 和delete方法实现的。你已经在上面的Action里见到了load方法。当传入一个单参数时,load方法返回一条记录,就是id=传入值的那条

$blogpost->load(1);

Save方法可以让我们INSERT一条新记录,也可以更新一条老记录。

public functioncreateNewPostAction() {    

$blogpost = Mage::getModel('weblog/blogpost');    

$blogpost->setTitle('Code Post!');    

$blogpost->setPost('This post was created from code!');    

$blogpost->save();    

echo 'post created';

}

然后执行网址

http://localhost/magento/weblog/index/createNewPost

会发现多了几条记录。接着,增加一个方法

public functioneditFirstPostAction() {    

$blogpost = Mage::getModel('weblog/blogpost');    

$blogpost->load(1);    

$blogpost->setTitle("The First post!");    

$blogpost->save();    

echo 'post edited';

}

最后,你可以使用类似的方式删除你的post

public functiondeleteFirstPostAction() {    

$blogpost = Mage::getModel('weblog/blogpost');    

$blogpost->load(1);    

$blogpost->delete();    

echo 'post removed';

}

Model集合

单个的model很有用,但是有时候我们需要一串model。不是简单的返回一个Model的数组,每个MagentoModel类型有一个不同的collection object与之关联。这些对象实现了php的iteratorAggregate和countable接口,这意味这他们可以传给count函数,或者拿each来构造。

我们后续详细讨论Collection,现在先看看基本的。在Controller里加入下面的方法,

public functionshowAllBlogPostsAction() {    

$posts = Mage::getModel('weblog/blogpost')->getCollection();     foreach($posts as $blog_post){        

echo'

'.$blog_post->getTitle().'

';        

echo nl2br($blog_post->getPost());    

}

 }

载入下面的URL

http://localhost/magento/weblog/index/showAllBlogPosts

会得到一个类似的异常

Warning:include(Alanstormdotcom/Weblog/Model/Mysql4/Blogpost/Collection.php)[function.include]: failed to open stream

你应该不会感到意外吧。我们需要增加一个PHP 类文件,来定义Blogpost collection。每个模块有一个保护的属性叫_resourceCollectionName包含了URI来标志我们的集合

  protected '_resourceCollectionName' =>string 'weblog/blogpost_collection'

在下面的位置增加文件

app/code/local/XStarX/Weblog/Model/Mysql4/Blogpost/Collection.php

class XStarX_Weblog_Model_Mysql4_Blogpost_Collectionextends Mage_Core_Model_Mysql4_Collection_Abstract {    

protected function _construct()    {            

$this->_init('weblog/blogpost');    

}

}

类似于其他类,我们需要用Model URI(weblog/blogpost)初始化我们的集合。重新加载Controller Action,你可以看到post信息。

重要提示

all basic MagentoModels inherit from Mage_Core_Model_Abstract. This isn’t 100% true. ThisAbstract method hasn’t existed for Magento’s entire life, and there are stillmany Models in the system that inherit directly fromVarien_Object.

 


你可能感兴趣的:(magento,php)