现在的位置: 首页 > 综合 > 正文

第十章-数据操作&数据收集器

2014年10月05日 ⁄ 综合 ⁄ 共 10881字 ⁄ 字号 评论关闭

在我们开始介绍数据操作前,我们先介绍一个神兵利器—Varien Data Collections。在最早的时候我们写php通常用Array来做数据收集器,这个小东西可发挥了大作用,要知道如果你想在其他语言中实现Array有多么难过。
例如c、c++。

 

在php5中,更是发扬了Array,php内置了一些类和接口,允许你创建你自己的数据结构。Magento充分利用了这一点,在使用 Varien_Data_Collection来做数据收集的时候,它实现了php内置IteratorAggregate对象迭代器和 Countable两个接口。下面是用php内置类ArrayObject的一个例子。

Php代码  收藏代码
  1. $array = new ArrayObject();  
  2. class MyCollection extends ArrayObject{}  
  3. $collection = new MyCollection();  
  4. $collection[] = 'bar';  

 

在接下来的文章中,我认为你已经了解ArrayObject、IteratorAggregate、Countable。如果还是很陌生,我建议你先阅读这篇文章PHP5对象迭代(Object Iteration) 。当然你不必了解很底层的东西,你只需要知道如何用就可以了。

 

在Magento代码中,其实每个Model都有个Collection。了解这些数据收集器是如何工作的是你成为一个真正Magento开发人员的关键点。


下面让我们开始吧,前面我们创建过一个Helloworld模块,现在我们继续用他开始我们接下来的学习。

 

创建一个数据收集器首先,我们创造一些新的对象。

 

Php代码  收藏代码
  1. $thing_1 = new Varien_Object();  
  2. $thing_1->setName('Richard');  
  3. $thing_1->setAge(24);  
  4.   
  5. $thing_2 = new Varien_Object();  
  6. $thing_2->setName('Jane');  
  7. $thing_2->setAge(12);  
  8.   
  9. $thing_3 = new Varien_Object();  
  10. $thing_3->setName('Spot');  
  11. $thing_3->setLastName('The Dog');  
  12. $thing_3->setAge(7);  

 

Magento中所有的Model都继承Varien_Object,在面向对象编程中,这样做的好处是当你想往多个Model中添加方法的时候,你只需要简单地修改一个文件即可。在继承Varien_Object的类中,有两个魔术方法,get/set,你可以很方便的向对象中加入一个属性(值),让我们看个例子。

Php代码  收藏代码
  1. var_dump($thing_1->getName());  

 

如果你忘记了属性的名字,你可以将所有数据都获取到:

Php代码  收藏代码
  1. var_dump($thing_3->getData());  

 

你将看到以下结果:

Php代码  收藏代码
  1. array  
  2. 'name' => string 'Spot' (length=4)  
  3. 'last_name' => string 'The Dog' (length=7)  
  4. 'age' => int 7  

 

注意last_name属性,是用下滑线分隔的,如果你想用get和set魔术方法,那么需要使用驼峰命名法。

Php代码  收藏代码
  1. $thing_1->setLastName('Smith');  

 

在新版本的magento中你可以用array关联数组的方式获取数据。

Php代码  收藏代码
  1. var_dump($thing_3["last_name"]);  

 

T这个归功于php5的新特性,ArrayAccess接口。也是 “Object Oriented Programming ”.

现在然我们把这些对象加到数据收集器Varien_Data_Collection中。很多程序员将Collection看成是数组,当然我不反对。

Php代码  收藏代码
  1. $collection_of_things = new Varien_Data_Collection();  
  2. $collection_of_things  
  3. ->addItem($thing_1)  
  4. ->addItem($thing_2)  
  5. ->addItem($thing_3);  

 

大多数Magento data Collections继承于Varien_Data_Collection,你可以使用里面的任何一个方法。那么我们可以做些什么呢?接下来我们使用foreach去循环它。

Php代码  收藏代码
  1. foreach($collection_of_things as $thing)  
  2. {  
  3.     var_dump($thing->getData());  
  4. }  

 

这里还有方法取出第一个数据和最后一个数据。

Php代码  收藏代码
  1. var_dump($collection_of_things->getFirstItem());  
  2. var_dump($collection_of_things->getLastItem()->getData());  

 

将你的数据转成xml

Php代码  收藏代码
  1. var_dump( $collection_of_things->toXml() );  

 

只像取某一个字段

Php代码  收藏代码
  1. var_dump($collection_of_things->getColumnValues('name'));  

 

Magneto还给我们提供了一些基本的过滤功能

Php代码  收藏代码
  1. var_dump($collection_of_things->getItemsByColumnValue('name','Spot'));  

 

模型数据收集器(Model Collections)

 

 

前面我们有提到,所有Magento的模型数据收集器都继承Varien_Data_Collectionm,所以理论上我们可以使用之前的所有方法。下面让我们以product模型实战下。

Php代码  收藏代码
  1. public function testAction()  
  2. {  
  3.     $collection_of_products = Mage::getModel('catalog/product')->getCollection();  
  4.     var_dump($collection_of_products->getFirstItem()->getData());  
  5. }  

 

基本所有的Magento模型都有个方法叫getCollection 默认情况下,它会返回系统中所有的数据。Magento的数据收集器Collection包含很多复杂的逻辑来处理数据,无论是否使用索引或缓存、EAV表等。上面的产品数据收集器,它里面还有Varien_Data_Collection_Db 类。这个类给你很多有用的方法,例如如果你向看sql的select语句。

Php代码  收藏代码
  1. public function testAction()  
  2. {  
  3.     $collection_of_products = Mage::getModel('catalog/product')->getCollection();  
  4.     var_dump($collection_of_products->getSelect()); //might cause a segmentation fault  
  5. }  

 

上面的方法将输出

Php代码  收藏代码
  1. object(Varien_Db_Select)[94]  
  2.   protected '_bind' =>  
  3.     array  
  4.       emptyempty  
  5.   protected '_adapter' =>  
  6. ...  

 

从上面可以看出,Magento使用的是ZendFramework的数据库链接层。接下来让我们看看更有意义的东西

Php代码  收藏代码
  1. public function testAction()  
  2. {  
  3.     $collection_of_products = Mage::getModel('catalog/product')->getCollection();  
  4.     //var_dump($collection_of_products->getSelect()); //might cause a segmentation fault  
  5.     var_dump(  
  6.         (string) $collection_of_products->getSelect()  
  7.     );  
  8. }  

 

上面的方法将输出

Php代码  收藏代码
  1. 'SELECT `e`.* FROM `catalog_product_entity` AS `e`'  

 

有时也会比较复杂,例如

Php代码  收藏代码
  1. string 'SELECT `e`.*, `price_index`.`price`, `price_index`.`final_price`, IF(`price_index`.`tier_price`, LEAST(`price_index`.`min_price`, `price_index`.`tier_price`), `price_index`.`min_price`) AS `minimal_price`, `price_index`.`min_price`, `price_index`.`max_price`, `price_index`.`tier_price` FROM `catalog_product_entity` AS `e`  
  2. INNER JOIN `catalog_product_index_price` AS `price_index` ON price_index.entity_id = e.entity_id AND price_index.website_id = '1' AND price_index.customer_group_id = 0'  

 

这个差异取决于你选择的字段,同样也涉及到索引和缓存。如果你看过之前的文章,那么你应该知道很多Magento表是使用Eav表结构的,默认情况 下一个eav的数据收集器将不会包含所有的对象字段,你可以通过addAttributeToSelect来添加它们。让我们看看例子。

Php代码  收藏代码
  1. $collection_of_products = Mage::getModel('catalog/product')  
  2. ->getCollection()  
  3. ->addAttributeToSelect('*');  //the asterisk is like a SQL SELECT *  

 

或者你也可以只选某一个字段

Php代码  收藏代码
  1. //or just one  
  2. $collection_of_products = Mage::getModel('catalog/product')  
  3. ->getCollection()  
  4. ->addAttributeToSelect('meta_title');  

 

或者更多

Php代码  收藏代码
  1. //or just one  
  2. $collection_of_products = Mage::getModel('catalog/product')  
  3. ->getCollection()  
  4. ->addAttributeToSelect('meta_title')  
  5. ->addAttributeToSelect('price');  

 

延迟加载(Lazy Loading)

 

一般情况下,我们在创建sql后需要立刻执行,从而获取数据,例如。

Php代码  收藏代码
  1. $model = new Customer();  
  2. //SQL Calls being made to Populate the Object  
  3. echo 'Done'//execution continues  

 

但是Magento不是这样的,它采用的是Lazy Loading 。延迟加载意味着在程序需要数据前,sql是不执行的,如下。

Php代码  收藏代码
  1. $collection_of_products = Mage::getModel('catalog/product')  
  2. ->getCollection();  

 

在这个时候Magento还没有链接数据库,你可以放心地去做你想要做的事。

Php代码  收藏代码
  1. $collection_of_products = Mage::getModel('catalog/product')  
  2. ->getCollection();  
  3. $collection_of_products->addAttributeToSelect('meta_title');  

 

你不必担心每次添加属性的时候Magento都会执行一个sql,去获取数据,sql只有在你需要数据的时候才会被执行。

Magento对数据库连接层做了良好的封装,当然它也考虑到了效率问题。在一般情况下,你没必要去担心sql后台是怎么执行的,只需要专心做你的功能,例如区块、布局等。这是Magento非常优秀的地方。

 

过滤数据(Filtering Database Collections)

 

最重要的一个方法是addFieldToFilter。通过这个方法可以添加我们sql中的WHERE语句。

Php代码  收藏代码
  1. public function testAction()  
  2. {  
  3.     $collection_of_products = Mage::getModel('catalog/product')  
  4.     ->getCollection();  
  5.     $collection_of_products->addFieldToFilter('sku','n2610');  
  6.   
  7.     //another neat thing about collections is you can pass them into the count      //function.  More PHP5 powered goodness  
  8.     echo "Our collection now has " . count($collection_of_products) . ' item(s)';  
  9.     var_dump($collection_of_products->getFirstItem()->getData());  
  10. }  

 

addFieldToFilter方法中的第一个参数是你想过滤的字段名称,第二个是你想过滤的值。例如刚刚sku是字段名称,n2610是值。

 

第二个参数也可以被用来指定某一类型的数据。稍微有些复杂,我们继续往下看。

Php代码  收藏代码
  1. $collection_of_products->addFieldToFilter('sku','n2610');  

 

这个等同于sql中的where条件句

Php代码  收藏代码
  1. WHERE sku = "n2610"  

 

下面的例子自己尝试下

Php代码  收藏代码
  1. public function testAction()  
  2. {  
  3.     var_dump(  
  4.     (string)  
  5.     Mage::getModel('catalog/product')  
  6.     ->getCollection()  
  7.     ->addFieldToFilter('sku','n2610')  
  8.     ->getSelect());  
  9. }  

 

将会输出这个

Php代码  收藏代码
  1. SELECT `e`.* FROM `catalog_product_entity` AS `e` WHERE (e.sku = 'n2610')'  

 

但是这个很快会变得很复杂。试着做下面的练习。

Php代码  收藏代码
  1. var_dump(  
  2. (string)  
  3. Mage::getModel('catalog/product')  
  4. ->getCollection()  
  5. ->addAttributeToSelect('*')  
  6. ->addFieldToFilter('meta_title','my title')  
  7. ->getSelect()  
  8. );  

 

输出的将是下面的sql语句。

Php代码  收藏代码
  1. SELECT `e`.*, IF(_table_meta_title.value_id>0, _table_meta_title.value, _table_meta_title_default.value) AS `meta_title`  
  2. FROM `catalog_product_entity` AS `e`  
  3. INNER JOIN `catalog_product_entity_varchar` AS `_table_meta_title_default`  
  4.     ON (_table_meta_title_default.entity_id = e.entity_id) AND (_table_meta_title_default.attribute_id='103')  
  5.     AND _table_meta_title_default.store_id=0  
  6. LEFT JOIN `catalog_product_entity_varchar` AS `_table_meta_title`  
  7.     ON (_table_meta_title.entity_id = e.entity_id) AND (_table_meta_title.attribute_id='103')  
  8.     AND (_table_meta_title.store_id='1')  
  9. WHERE (IF(_table_meta_title.value_id>0, _table_meta_title.value, _table_meta_title_default.value) = 'my title')  

 

在你有空的时候可以好好研究下上面的sql语句,我们先不转移焦点,继续我们下面的讲解。

 

其它比较运算符

 

我确定在刚刚的练习中,你想知道如何实现一个不是“=”的where条件句,例如不等于、大于、小于。刚刚我们有讲过addFieldToFilter的第二个参数允许传入不同“类型”。

其实很简单,只要将一个简单的数组作为第二个参数传入addFieldToFilter方法就可以变换条件句。数组的键就是“类型”,关联的值就是你想过滤的值。我们改写下上面的代码。

Php代码  收藏代码
  1. public function testAction()  
  2. {  
  3.     var_dump(  
  4.     (string)  
  5.     Mage::getModel('catalog/product')  
  6.     ->getCollection()  
  7.     ->addFieldToFilter('sku',array('eq'=>'n2610'))  
  8.     ->getSelect()  
  9.     );  
  10. }  

 

看上面的过滤器

Php代码  收藏代码
  1. addFieldToFilter('sku',array('eq'=>'n2610'))  

 

正如你看到的,第二个参数是一个php的数组。它的键是“eq”,代表等于的意思。

Magento在这个函数中有一系列英语的缩写,这些词的资料可以参考《tear of remembrance》 。这些沿用了Perl语言中的一些比较运算符号。

 

在这里我将Magento所有的条件判断符号列出来供大家参考。

Php代码  收藏代码
  1. array("eq"=>'n2610')  
  2. WHERE (e.sku = 'n2610')  
  3.   
  4. array("neq"=>'n2610')  
  5. WHERE (e.sku != 'n2610')  
  6.   
  7. array("like"=>'n2610')  
  8. WHERE (e.sku like 'n2610')  
  9.   
  10. array("nlike"=>'n2610')  
  11. WHERE (e.sku not like 'n2610')  
  12.   
  13. array("is"=>'n2610')  
  14. WHERE (e.sku is 'n2610')  
  15.   
  16. array("in"=>array('n2610'))  
  17. WHERE (e.sku in ('n2610'))  
  18.   
  19. array("nin"=>array('n2610'))  
  20. WHERE (e.sku not in ('n2610'))  
  21.   
  22. array("notnull"=>'n2610')  
  23. WHERE (e.sku is NOT NULL)  
  24.   
  25. array("null"=>'n2610')  
  26. WHERE (e.sku is NULL)  
  27.   
  28. array("gt"=>'n2610')  
  29. WHERE (e.sku > 'n2610')  
  30.   
  31. array("lt"=>'n2610')  
  32. WHERE (e.sku < 'n2610')  
  33.   
  34. array("gteq"=>'n2610')  
  35. WHERE (e.sku >= 'n2610')  
  36.   
  37. array("moreq"=>'n2610'//a weird, second way to do greater than equal  
  38. WHERE (e.sku >= 'n2610')  
  39.   
  40. array("lteq"=>'n2610')  
  41. WHERE (e.sku <= 'n2610')  
  42.   
  43. array("finset"=>array('n2610'))  
  44. WHERE (find_in_set('n2610',e.sku))  
  45.   
  46. array('from'=>'10','to'=>'20')  
  47. WHERE e.sku >= '10' and e.sku <= '20'  

 

其中大多数是自我的理解,但有几个得特别注意。

in, nin, find_in_set

in and nin 条件句中,语序你传入一个数组作为值。例如:

Php代码  收藏代码
  1. array("in"=>array('n2610','ABC123')  
  2. WHERE (e.sku in ('n2610','ABC123'))  

 

notnull, null

关键字NULL是最特殊的sql句,它将忽略你传入的值。

Php代码  收藏代码
  1. array("notnull"=>'n2610')  
  2. WHERE (e.sku is NOT NULL)  

 

from – to 过滤

这是另一种过滤方式,在传入的数组中,允许你传入两个键,是从哪里到哪里的意思,一个数值区间。

Php代码  收藏代码
  1. public function testAction  
  2. {  
  3.         var_dump(  
  4.         (string)  
  5.         Mage::getModel('catalog/product')  
  6.         ->getCollection()  
  7.         ->addFieldToFilter('price',array('from'=>'10','to'=>'20'))  
  8.         ->getSelect()  
  9.         );  
  10. }  

 

上面等同于

Php代码  收藏代码
  1. WHERE (_table_price.value >= '10' AND _table_price.value <= '20')  

 

AND 或者 OR

根据刚才讲的内容,你可以知道,通过多个 addFieldToFilter 方法可以获得一个”AND”的条件句。

Php代码  收藏代码
  1. function testAction()  
  2. {  
  3.         echo(  
  4.         (string)  
  5.         Mage::getModel('catalog/product')  
  6.         ->getCollection()  
  7.         ->addFieldToFilter('sku',array('like'=>'a%'))  
  8.         ->addFieldToFilter('sku',array('like'=>'b%'))  
  9.         ->getSelect()  
  10.         );  
  11. }  

 

等同于下面的子句

Php代码  收藏代码
  1. WHERE (e.sku LIKE 'a%') AND (e.sku LIKE 'b%')  

 

但是,聪明的你可以发现,上面的例子不可能返回任何结果,因为一个sku不可能以a开头,同时也以b开头。
我们希望用的应该是”OR”,那么如何实现呢?这又使我们将焦点集中到了addFieldToFilter方法的第二个参数上。

如果你希望构造一个or的语句,首先我们构造两个参数。

Php代码  收藏代码
  1. public function testAction()  
  2. {  
  3.         $filter_a = array('like'=>'a%');  
  4.         $filter_b = array('like'=>'b%');  
  5. }  

 

然后将它们作为一组参数传入addFieldToFilter方法中,如下。

Php代码  收藏代码
  1. public function testAction()  
  2. {  
  3.         $filter_a = array('like'=>'a%');  
  4.         $filter_b = array('like'=>'b%');  
  5.         echo(  
  6.         (string)  
  7.         Mage::getModel('catalog/product')  
  8.         ->getCollection()  
  9.         ->addFieldToFilter('sku',array($filter_a,$filter_b))  
  10.         ->getSelect()  
  11.         );  
  12. }  

 

你可以看到这样的一个子句。

Php代码  收藏代码
  1. WHERE (((e.sku LIKE 'a%') OR (e.sku LIKE 'b%')))  

 

总结

 

恭喜你,你现在已经是一个很不错的Magento开发者了!因为你不需要写任何sql语句,就可以获取几乎所有模型的所有你想要的数据。

抱歉!评论已关闭.