PageRenderTime 48ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 1336 lines | 800 code | 136 blank | 400 comment | 132 complexity | ff67cb0721d4409a1d4c2e5e70418c11 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Mage
  22. * @package Mage_Eav
  23. * @copyright Copyright (c) 2010 Magento Inc. (http://www.magentocommerce.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. /**
  27. * Entity/Attribute/Model - collection abstract
  28. *
  29. * @category Mage
  30. * @package Mage_Eav
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. abstract class Mage_Eav_Model_Entity_Collection_Abstract extends Varien_Data_Collection_Db
  34. {
  35. /**
  36. * Array of items with item id key
  37. *
  38. * @var array
  39. */
  40. protected $_itemsById = array();
  41. /**
  42. * Entity static fields
  43. *
  44. * @var array
  45. */
  46. protected $_staticFields = array();
  47. /**
  48. * Entity object to define collection's attributes
  49. *
  50. * @var Mage_Eav_Model_Entity_Abstract
  51. */
  52. protected $_entity;
  53. /**
  54. * Entity types to be fetched for objects in collection
  55. *
  56. * @var array
  57. */
  58. protected $_selectEntityTypes = array();
  59. /**
  60. * Attributes to be fetched for objects in collection
  61. *
  62. * @var array
  63. */
  64. protected $_selectAttributes=array();
  65. /**
  66. * Attributes to be filtered order sorted by
  67. *
  68. * @var array
  69. */
  70. protected $_filterAttributes=array();
  71. /**
  72. * Joined entities
  73. *
  74. * @var array
  75. */
  76. protected $_joinEntities = array();
  77. /**
  78. * Joined attributes
  79. *
  80. * @var array
  81. */
  82. protected $_joinAttributes = array();
  83. /**
  84. * Joined fields data
  85. *
  86. * @var array
  87. */
  88. protected $_joinFields = array();
  89. /**
  90. * Collection constructor
  91. *
  92. * @param Mage_Core_Model_Mysql4_Abstract $resource
  93. */
  94. public function __construct($resource=null)
  95. {
  96. parent::__construct();
  97. $this->_construct();
  98. $this->setConnection($this->getEntity()->getReadConnection());
  99. $this->_prepareStaticFields();
  100. $this->_initSelect();
  101. }
  102. /**
  103. * Initialize collection
  104. */
  105. protected function _construct()
  106. {
  107. }
  108. public function getTable($table)
  109. {
  110. return $this->getResource()->getTable($table);
  111. }
  112. /**
  113. * Prepare static entity fields
  114. *
  115. * @return Mage_Eav_Model_Entity_Collection_Abstract
  116. */
  117. protected function _prepareStaticFields()
  118. {
  119. foreach ($this->getEntity()->getDefaultAttributes() as $field) {
  120. $this->_staticFields[$field] = $field;
  121. }
  122. return $this;
  123. }
  124. protected function _initSelect()
  125. {
  126. $this->getSelect()->from(array('e'=>$this->getEntity()->getEntityTable()));
  127. if ($this->getEntity()->getTypeId()) {
  128. $this->addAttributeToFilter('entity_type_id', $this->getEntity()->getTypeId());
  129. }
  130. return $this;
  131. }
  132. /**
  133. * Standard resource collection initalization
  134. *
  135. * @param string $model
  136. * @return Mage_Core_Model_Mysql4_Collection_Abstract
  137. */
  138. protected function _init($model, $entityModel=null)
  139. {
  140. $this->setItemObjectClass(Mage::getConfig()->getModelClassName($model));
  141. if (is_null($entityModel)) {
  142. $entityModel = $model;
  143. }
  144. $entity = Mage::getResourceSingleton($entityModel);
  145. $this->setEntity($entity);
  146. return $this;
  147. }
  148. /**
  149. * Set entity to use for attributes
  150. *
  151. * @param Mage_Eav_Model_Entity_Abstract $entity
  152. * @return Mage_Eav_Model_Entity_Collection_Abstract
  153. */
  154. public function setEntity($entity)
  155. {
  156. if ($entity instanceof Mage_Eav_Model_Entity_Abstract) {
  157. $this->_entity = $entity;
  158. } elseif (is_string($entity) || $entity instanceof Mage_Core_Model_Config_Element) {
  159. $this->_entity = Mage::getModel('eav/entity')->setType($entity);
  160. } else {
  161. Mage::throwException(Mage::helper('eav')->__('Invalid entity supplied: %s.', print_r($entity,1)));
  162. }
  163. return $this;
  164. }
  165. /**
  166. * Get collection's entity object
  167. *
  168. * @return Mage_Eav_Model_Entity_Abstract
  169. */
  170. public function getEntity()
  171. {
  172. if (empty($this->_entity)) {
  173. throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Entity is not initialized.'));
  174. }
  175. return $this->_entity;
  176. }
  177. /**
  178. * Get resource instance
  179. *
  180. * @return Mage_Core_Model_Mysql4_Abstract
  181. */
  182. public function getResource()
  183. {
  184. return $this->getEntity();
  185. }
  186. /**
  187. * Set template object for the collection
  188. *
  189. * @param Varien_Object $object
  190. * @return Mage_Eav_Model_Entity_Collection_Abstract
  191. */
  192. public function setObject($object=null)
  193. {
  194. if (is_object($object)) {
  195. $this->setItemObjectClass(get_class($object));
  196. }
  197. else {
  198. $this->setItemObjectClass($object);
  199. }
  200. return $this;
  201. }
  202. /**
  203. * Add an object to the collection
  204. *
  205. * @param Varien_Object $object
  206. * @return Mage_Eav_Model_Entity_Collection_Abstract
  207. */
  208. public function addItem(Varien_Object $object)
  209. {
  210. if (get_class($object)!== $this->_itemObjectClass) {
  211. throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Attempt to add an invalid object.'));
  212. }
  213. return parent::addItem($object);
  214. }
  215. /**
  216. * Retrieve entity attribute
  217. *
  218. * @param string $attributeCode
  219. * @return Mage_Eav_Model_Entity_Attribute_Abstract
  220. */
  221. public function getAttribute($attributeCode)
  222. {
  223. if (isset($this->_joinAttributes[$attributeCode])) {
  224. return $this->_joinAttributes[$attributeCode]['attribute'];
  225. } else {
  226. return $this->getEntity()->getAttribute($attributeCode);
  227. }
  228. }
  229. /**
  230. * Add attribute filter to collection
  231. *
  232. * If $attribute is an array will add OR condition with following format:
  233. * array(
  234. * array('attribute'=>'firstname', 'like'=>'test%'),
  235. * array('attribute'=>'lastname', 'like'=>'test%'),
  236. * )
  237. *
  238. * @see self::_getConditionSql for $condition
  239. * @param Mage_Eav_Model_Entity_Attribute_Interface|integer|string|array $attribute
  240. * @param null|string|array $condition
  241. * @param string $operator
  242. * @return Mage_Eav_Model_Entity_Collection_Abstract
  243. */
  244. public function addAttributeToFilter($attribute, $condition=null, $joinType='inner')
  245. {
  246. if ($attribute===null) {
  247. $this->getSelect();
  248. return $this;
  249. }
  250. if (is_numeric($attribute)) {
  251. $attribute = $this->getEntity()->getAttribute($attribute)->getAttributeCode();
  252. } else if ($attribute instanceof Mage_Eav_Model_Entity_Attribute_Interface) {
  253. $attribute = $attribute->getAttributeCode();
  254. }
  255. if (is_array($attribute)) {
  256. $sqlArr = array();
  257. foreach ($attribute as $condition) {
  258. $sqlArr[] = $this->_getAttributeConditionSql($condition['attribute'], $condition, $joinType);
  259. }
  260. $conditionSql = '('.join(') OR (', $sqlArr).')';
  261. } elseif (is_string($attribute)) {
  262. if (is_null($condition)) {
  263. $condition = '';
  264. }
  265. $conditionSql = $this->_getAttributeConditionSql($attribute, $condition, $joinType);
  266. }
  267. if (!empty($conditionSql)) {
  268. $this->getSelect()->where($conditionSql, null, Varien_Db_Select::TYPE_CONDITION);
  269. } else {
  270. Mage::throwException('Invalid attribute identifier for filter ('.get_class($attribute).')');
  271. }
  272. return $this;
  273. }
  274. /**
  275. * Wrapper for compatibility with Varien_Data_Collection_Db
  276. *
  277. * @param mixed $attribute
  278. * @param mixed $condition
  279. */
  280. public function addFieldToFilter($attribute, $condition=null)
  281. {
  282. return $this->addAttributeToFilter($attribute, $condition);
  283. }
  284. /**
  285. * Add attribute to sort order
  286. *
  287. * @param string $attribute
  288. * @param string $dir
  289. * @return Mage_Eav_Model_Entity_Collection_Abstract
  290. */
  291. public function addAttributeToSort($attribute, $dir='asc')
  292. {
  293. if (isset($this->_joinFields[$attribute])) {
  294. $this->getSelect()->order($this->_getAttributeFieldName($attribute).' '.$dir);
  295. return $this;
  296. }
  297. if (isset($this->_staticFields[$attribute])) {
  298. $this->getSelect()->order("e.{$attribute} {$dir}");
  299. }
  300. if (isset($this->_joinAttributes[$attribute])) {
  301. $attrInstance = $this->_joinAttributes[$attribute]['attribute'];
  302. $entityField = $this->_getAttributeTableAlias($attribute).'.'.$attrInstance->getAttributeCode();
  303. } else {
  304. $attrInstance = $this->getEntity()->getAttribute($attribute);
  305. $entityField = 'e.'.$attribute;
  306. }
  307. if ($attrInstance) {
  308. if ($attrInstance->getBackend()->isStatic()) {
  309. $this->getSelect()->order($entityField.' '.$dir);
  310. } else {
  311. $this->_addAttributeJoin($attribute, 'left');
  312. if (isset($this->_joinAttributes[$attribute])) {
  313. $this->getSelect()->order($attribute.' '.$dir);
  314. } else {
  315. $this->getSelect()->order($this->_getAttributeTableAlias($attribute).'.value '.$dir);
  316. }
  317. }
  318. }
  319. return $this;
  320. }
  321. /**
  322. * Add attribute to entities in collection
  323. *
  324. * If $attribute=='*' select all attributes
  325. *
  326. * @param array|string|integer|Mage_Core_Model_Config_Element $attribute
  327. * @param false|string $joinType flag for joining attribute
  328. * @return Mage_Eav_Model_Entity_Collection_Abstract
  329. */
  330. public function addAttributeToSelect($attribute, $joinType=false)
  331. {
  332. if (is_array($attribute)) {
  333. Mage::getSingleton('eav/config')->loadCollectionAttributes($this->getEntity()->getType(), $attribute);
  334. foreach ($attribute as $a) {
  335. $this->addAttributeToSelect($a, $joinType);
  336. }
  337. return $this;
  338. }
  339. if ($joinType!==false && !$this->getEntity()->getAttribute($attribute)->isStatic()) {
  340. $this->_addAttributeJoin($attribute, $joinType);
  341. } elseif ('*'===$attribute) {
  342. $attributes = $this->getEntity()
  343. ->loadAllAttributes()
  344. ->getAttributesByCode();
  345. foreach ($attributes as $attrCode=>$attr) {
  346. $this->_selectAttributes[$attrCode] = $attr->getId();
  347. }
  348. } else {
  349. if (isset($this->_joinAttributes[$attribute])) {
  350. $attrInstance = $this->_joinAttributes[$attribute]['attribute'];
  351. } else {
  352. //$attrInstance = $this->getEntity()->getAttribute($attribute);
  353. $attrInstance = Mage::getSingleton('eav/config')
  354. ->getCollectionAttribute($this->getEntity()->getType(), $attribute);
  355. }
  356. if (empty($attrInstance)) {
  357. throw Mage::exception('Mage_Eav',
  358. Mage::helper('eav')->__('Invalid attribute requested: %s', (string)$attribute));
  359. }
  360. $this->_selectAttributes[$attrInstance->getAttributeCode()] = $attrInstance->getId();
  361. }
  362. return $this;
  363. }
  364. public function addEntityTypeToSelect($entityType, $prefix)
  365. {
  366. $this->_selectEntityTypes[$entityType] = array(
  367. 'prefix'=>$prefix,
  368. );
  369. return $this;
  370. }
  371. /**
  372. * Add field to static
  373. *
  374. * @param string $field
  375. * @return Mage_Eav_Model_Entity_Collection_Abstract
  376. */
  377. public function addStaticField($field)
  378. {
  379. if (!isset($this->_staticFields[$field])) {
  380. $this->_staticFields[$field] = $field;
  381. }
  382. return $this;
  383. }
  384. /**
  385. * Add attribute expression (SUM, COUNT, etc)
  386. *
  387. * Example: ('sub_total', 'SUM({{attribute}})', 'revenue')
  388. * Example: ('sub_total', 'SUM({{revenue}})', 'revenue')
  389. *
  390. * For some functions like SUM use groupByAttribute.
  391. *
  392. * @param string $alias
  393. * @param string $expression
  394. * @param string $attribute
  395. * @return Mage_Eav_Model_Entity_Collection_Abstract
  396. */
  397. public function addExpressionAttributeToSelect($alias, $expression, $attribute)
  398. {
  399. // validate alias
  400. if (isset($this->_joinFields[$alias])) {
  401. throw Mage::exception('Mage_Eav',
  402. Mage::helper('eav')->__('Joint field or attribute expression with this alias is already declared.'));
  403. }
  404. if(!is_array($attribute)) {
  405. $attribute = array($attribute);
  406. }
  407. $fullExpression = $expression;
  408. // Replacing multiple attributes
  409. foreach($attribute as $attributeItem) {
  410. if (isset($this->_staticFields[$attributeItem])) {
  411. $attrField = sprintf('e.%s', $attributeItem);
  412. }
  413. else {
  414. $attributeInstance = $this->getAttribute($attributeItem);
  415. if ($attributeInstance->getBackend()->isStatic()) {
  416. $attrField = 'e.' . $attributeItem;
  417. } else {
  418. $this->_addAttributeJoin($attributeItem, 'left');
  419. $attrField = $this->_getAttributeFieldName($attributeItem);
  420. }
  421. }
  422. $fullExpression = str_replace('{{attribute}}', $attrField, $fullExpression);
  423. $fullExpression = str_replace('{{' . $attributeItem . '}}', $attrField, $fullExpression);
  424. }
  425. $this->getSelect()->columns(array($alias=>$fullExpression));
  426. $this->_joinFields[$alias] = array(
  427. 'table' => false,
  428. 'field' => $fullExpression
  429. );
  430. return $this;
  431. }
  432. /**
  433. * Groups results by specified attribute
  434. *
  435. * @param string|array $attribute
  436. */
  437. public function groupByAttribute($attribute)
  438. {
  439. if(is_array($attribute)) {
  440. foreach ($attribute as $attributeItem) {
  441. $this->groupByAttribute($attributeItem);
  442. }
  443. } else {
  444. if (isset($this->_joinFields[$attribute])) {
  445. $this->getSelect()->group($this->_getAttributeFieldName($attribute));
  446. return $this;
  447. }
  448. if (isset($this->_staticFields[$attribute])) {
  449. $this->getSelect()->group(sprintf('e.%s', $attribute));
  450. return $this;
  451. }
  452. if (isset($this->_joinAttributes[$attribute])) {
  453. $attrInstance = $this->_joinAttributes[$attribute]['attribute'];
  454. $entityField = $this->_getAttributeTableAlias($attribute).'.'.$attrInstance->getAttributeCode();
  455. } else {
  456. $attrInstance = $this->getEntity()->getAttribute($attribute);
  457. $entityField = 'e.'.$attribute;
  458. }
  459. if ($attrInstance->getBackend()->isStatic()) {
  460. $this->getSelect()->group($entityField);
  461. } else {
  462. $this->_addAttributeJoin($attribute);
  463. $this->getSelect()->group($this->_getAttributeTableAlias($attribute).'.value');
  464. }
  465. }
  466. return $this;
  467. }
  468. /**
  469. * Add attribute from joined entity to select
  470. *
  471. * Examples:
  472. * ('billing_firstname', 'customer_address/firstname', 'default_billing')
  473. * ('billing_lastname', 'customer_address/lastname', 'default_billing')
  474. * ('shipping_lastname', 'customer_address/lastname', 'default_billing')
  475. * ('shipping_postalcode', 'customer_address/postalcode', 'default_shipping')
  476. * ('shipping_city', $cityAttribute, 'default_shipping')
  477. *
  478. * Developer is encouraged to use existing instances of attributes and entities
  479. * After first use of string entity name it will be cached in the collection
  480. *
  481. * @todo connect between joined attributes of same entity
  482. * @param string $alias alias for the joined attribute
  483. * @param string|Mage_Eav_Model_Entity_Attribute_Abstract $attribute
  484. * @param string $bind attribute of the main entity to link with joined $filter
  485. * @param string $filter primary key for the joined entity (entity_id default)
  486. * @param string $joinType inner|left
  487. * @return Mage_Eav_Model_Entity_Collection_Abstract
  488. */
  489. public function joinAttribute($alias, $attribute, $bind, $filter=null, $joinType='inner', $storeId=null)
  490. {
  491. // validate alias
  492. if (isset($this->_joinAttributes[$alias])) {
  493. throw Mage::exception('Mage_Eav',
  494. Mage::helper('eav')->__('Invalid alias, already exists in joint attributes.'));
  495. }
  496. // validate bind attribute
  497. if (is_string($bind)) {
  498. $bindAttribute = $this->getAttribute($bind);
  499. }
  500. if (!$bindAttribute || (!$bindAttribute->isStatic() && !$bindAttribute->getId())) {
  501. throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Invalid foreign key.'));
  502. }
  503. // try to explode combined entity/attribute if supplied
  504. if (is_string($attribute)) {
  505. $attrArr = explode('/', $attribute);
  506. if (isset($attrArr[1])) {
  507. $entity = $attrArr[0];
  508. $attribute = $attrArr[1];
  509. }
  510. }
  511. // validate entity
  512. if (empty($entity) && $attribute instanceof Mage_Eav_Model_Entity_Attribute_Abstract) {
  513. $entity = $attribute->getEntity();
  514. } elseif (is_string($entity)) {
  515. // retrieve cached entity if possible
  516. if (isset($this->_joinEntities[$entity])) {
  517. $entity = $this->_joinEntities[$entity];
  518. } else {
  519. $entity = Mage::getModel('eav/entity')->setType($attrArr[0]);
  520. }
  521. }
  522. if (!$entity || !$entity->getTypeId()) {
  523. throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Invalid entity type.'));
  524. }
  525. // cache entity
  526. if (!isset($this->_joinEntities[$entity->getType()])) {
  527. $this->_joinEntities[$entity->getType()] = $entity;
  528. }
  529. // validate attribute
  530. if (is_string($attribute)) {
  531. $attribute = $entity->getAttribute($attribute);
  532. }
  533. if (!$attribute) {
  534. throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Invalid attribute type.'));
  535. }
  536. if (empty($filter)) {
  537. $filter = $entity->getEntityIdField();
  538. }
  539. // add joined attribute
  540. $this->_joinAttributes[$alias] = array(
  541. 'bind' => $bind,
  542. 'bindAttribute' => $bindAttribute,
  543. 'attribute' => $attribute,
  544. 'filter' => $filter,
  545. 'store_id' => $storeId,
  546. );
  547. $this->_addAttributeJoin($alias, $joinType);
  548. return $this;
  549. }
  550. /**
  551. * Join regular table field and use an attribute as fk
  552. *
  553. * Examples:
  554. * ('country_name', 'directory/country_name', 'name', 'country_id=shipping_country',
  555. * "{{table}}.language_code='en'", 'left')
  556. *
  557. * @param string $alias 'country_name'
  558. * @param string $table 'directory/country_name'
  559. * @param string $field 'name'
  560. * @param string $bind 'PK(country_id)=FK(shipping_country_id)'
  561. * @param string|array $cond "{{table}}.language_code='en'" OR array('language_code'=>'en')
  562. * @param string $joinType 'left'
  563. * @return Mage_Eav_Model_Entity_Collection_Abstract
  564. */
  565. public function joinField($alias, $table, $field, $bind, $cond=null, $joinType='inner')
  566. {
  567. // validate alias
  568. if (isset($this->_joinFields[$alias])) {
  569. throw Mage::exception('Mage_Eav',
  570. Mage::helper('eav')->__('Joined field with this alias is already declared.'));
  571. }
  572. // validate table
  573. if (strpos($table, '/')!==false) {
  574. $table = Mage::getSingleton('core/resource')->getTableName($table);
  575. }
  576. $tableAlias = $this->_getAttributeTableAlias($alias);
  577. // validate bind
  578. list($pk, $fk) = explode('=', $bind);
  579. $pk = $this->getSelect()->getAdapter()->quoteColumnAs(trim($pk), null);
  580. $bindCond = $tableAlias . '.' . $pk . '=' . $this->_getAttributeFieldName($fk);
  581. // process join type
  582. switch ($joinType) {
  583. case 'left':
  584. $joinMethod = 'joinLeft';
  585. break;
  586. default:
  587. $joinMethod = 'join';
  588. }
  589. $condArr = array($bindCond);
  590. // add where condition if needed
  591. if (!is_null($cond)) {
  592. if (is_array($cond)) {
  593. foreach ($cond as $k=>$v) {
  594. $condArr[] = $this->_getConditionSql($tableAlias.'.'.$k, $v);
  595. }
  596. } else {
  597. $condArr[] = str_replace('{{table}}', $tableAlias, $cond);
  598. }
  599. }
  600. $cond = '(' . join(') AND (', $condArr) . ')';
  601. // join table
  602. $this->getSelect()->$joinMethod(array($tableAlias=>$table), $cond, ($field ? array($alias=>$field) : array()));
  603. // save joined attribute
  604. $this->_joinFields[$alias] = array(
  605. 'table' => $tableAlias,
  606. 'field' => $field,
  607. );
  608. return $this;
  609. }
  610. /**
  611. * Join a table
  612. *
  613. * @param string|array $table
  614. * @param string $bind
  615. * @param string|array $fields
  616. * @param null|array $cond
  617. * @param string $joinType
  618. * @return Mage_Eav_Model_Entity_Collection_Abstract
  619. */
  620. public function joinTable($table, $bind, $fields=null, $cond=null, $joinType='inner')
  621. {
  622. $tableAlias = null;
  623. if (is_array($table)) {
  624. list($tableAlias, $tableName) = each($table);
  625. }
  626. else {
  627. $tableName = $table;
  628. }
  629. // validate table
  630. if (strpos($tableName, '/') !== false) {
  631. $tableName = Mage::getSingleton('core/resource')->getTableName($tableName);
  632. }
  633. if (empty($tableAlias)) {
  634. $tableAlias = $tableName;
  635. }
  636. // validate fields and aliases
  637. if (!$fields) {
  638. throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Invalid joint fields.'));
  639. }
  640. foreach ($fields as $alias=>$field) {
  641. if (isset($this->_joinFields[$alias])) {
  642. throw Mage::exception('Mage_Eav',
  643. Mage::helper('eav')->__('A joint field with this alias (%s) is already declared.', $alias));
  644. }
  645. $this->_joinFields[$alias] = array(
  646. 'table' => $tableAlias,
  647. 'field' => $field,
  648. );
  649. }
  650. // validate bind
  651. list($pk, $fk) = explode('=', $bind);
  652. $bindCond = $tableAlias . '.' . $pk . '=' . $this->_getAttributeFieldName($fk);
  653. // process join type
  654. switch ($joinType) {
  655. case 'left':
  656. $joinMethod = 'joinLeft';
  657. break;
  658. default:
  659. $joinMethod = 'join';
  660. }
  661. $condArr = array($bindCond);
  662. // add where condition if needed
  663. if (!is_null($cond)) {
  664. if (is_array($cond)) {
  665. foreach ($cond as $k=>$v) {
  666. $condArr[] = $this->_getConditionSql($tableAlias.'.'.$k, $v);
  667. }
  668. } else {
  669. $condArr[] = str_replace('{{table}}', $tableAlias, $cond);
  670. }
  671. }
  672. $cond = '('.join(') AND (', $condArr).')';
  673. // join table
  674. $this->getSelect()->$joinMethod(array($tableAlias => $tableName), $cond, $fields);
  675. return $this;
  676. }
  677. /**
  678. * Remove an attribute from selection list
  679. *
  680. * @param string $attribute
  681. * @return Mage_Eav_Model_Entity_Collection_Abstract
  682. */
  683. public function removeAttributeToSelect($attribute=null)
  684. {
  685. if (is_null($attribute)) {
  686. $this->_selectAttributes = array();
  687. } else {
  688. unset($this->_selectAttributes[$attribute]);
  689. }
  690. return $this;
  691. }
  692. /**
  693. * Set collection page start and records to show
  694. *
  695. * @param integer $pageNum
  696. * @param integer $pageSize
  697. * @return Mage_Eav_Model_Entity_Collection_Abstract
  698. */
  699. public function setPage($pageNum, $pageSize)
  700. {
  701. $this->setCurPage($pageNum)
  702. ->setPageSize($pageSize);
  703. return $this;
  704. }
  705. /**
  706. * Load collection data into object items
  707. *
  708. * @return Mage_Eav_Model_Entity_Collection_Abstract
  709. */
  710. public function load($printQuery = false, $logQuery = false)
  711. {
  712. if ($this->isLoaded()) {
  713. return $this;
  714. }
  715. Varien_Profiler::start('__EAV_COLLECTION_BEFORE_LOAD__');
  716. Mage::dispatchEvent('eav_collection_abstract_load_before', array('collection' => $this));
  717. $this->_beforeLoad();
  718. Varien_Profiler::stop('__EAV_COLLECTION_BEFORE_LOAD__');
  719. $this->_renderFilters();
  720. Varien_Profiler::start('__EAV_COLLECTION_LOAD_ENT__');
  721. $this->_loadEntities($printQuery, $logQuery);
  722. Varien_Profiler::stop('__EAV_COLLECTION_LOAD_ENT__');
  723. Varien_Profiler::start('__EAV_COLLECTION_LOAD_ATTR__');
  724. $this->_loadAttributes($printQuery, $logQuery);
  725. Varien_Profiler::stop('__EAV_COLLECTION_LOAD_ATTR__');
  726. Varien_Profiler::start('__EAV_COLLECTION_ORIG_DATA__');
  727. foreach ($this->_items as $item) {
  728. $item->setOrigData();
  729. }
  730. Varien_Profiler::stop('__EAV_COLLECTION_ORIG_DATA__');
  731. $this->_setIsLoaded();
  732. Varien_Profiler::start('__EAV_COLLECTION_AFTER_LOAD__');
  733. $this->_afterLoad();
  734. Varien_Profiler::stop('__EAV_COLLECTION_AFTER_LOAD__');
  735. return $this;
  736. }
  737. /**
  738. * Clone and reset collection
  739. *
  740. * @return Mage_Eav_Model_Entity_Collection_Abstract
  741. */
  742. protected function _getAllIdsSelect($limit=null, $offset=null)
  743. {
  744. $idsSelect = clone $this->getSelect();
  745. $idsSelect->reset(Zend_Db_Select::ORDER);
  746. $idsSelect->reset(Zend_Db_Select::LIMIT_COUNT);
  747. $idsSelect->reset(Zend_Db_Select::LIMIT_OFFSET);
  748. $idsSelect->reset(Zend_Db_Select::COLUMNS);
  749. $idsSelect->columns('e.'.$this->getEntity()->getIdFieldName());
  750. $idsSelect->limit($limit, $offset);
  751. return $idsSelect;
  752. }
  753. /**
  754. * Retrive all ids for collection
  755. *
  756. * @return array
  757. */
  758. public function getAllIds($limit=null, $offset=null)
  759. {
  760. return $this->getConnection()->fetchCol($this->_getAllIdsSelect($limit, $offset), $this->_bindParams);
  761. }
  762. /**
  763. * Retrive all ids sql
  764. *
  765. * @return array
  766. */
  767. public function getAllIdsSql()
  768. {
  769. $idsSelect = clone $this->getSelect();
  770. $idsSelect->reset(Zend_Db_Select::ORDER);
  771. $idsSelect->reset(Zend_Db_Select::LIMIT_COUNT);
  772. $idsSelect->reset(Zend_Db_Select::LIMIT_OFFSET);
  773. $idsSelect->reset(Zend_Db_Select::COLUMNS);
  774. $idsSelect->reset(Zend_Db_Select::GROUP);
  775. $idsSelect->columns('e.'.$this->getEntity()->getIdFieldName());
  776. return $idsSelect;
  777. }
  778. /**
  779. * Save all the entities in the collection
  780. *
  781. * @todo make batch save directly from collection
  782. */
  783. public function save()
  784. {
  785. foreach ($this->getItems() as $item) {
  786. $item->save();
  787. }
  788. return $this;
  789. }
  790. /**
  791. * Delete all the entities in the collection
  792. *
  793. * @todo make batch delete directly from collection
  794. */
  795. public function delete()
  796. {
  797. foreach ($this->getItems() as $k=>$item) {
  798. $this->getEntity()->delete($item);
  799. unset($this->_items[$k]);
  800. }
  801. return $this;
  802. }
  803. /**
  804. * Import 2D array into collection as objects
  805. *
  806. * If the imported items already exist, update the data for existing objects
  807. *
  808. * @param array $arr
  809. * @return Mage_Eav_Model_Entity_Collection_Abstract
  810. */
  811. public function importFromArray($arr)
  812. {
  813. $entityIdField = $this->getEntity()->getEntityIdField();
  814. foreach ($arr as $row) {
  815. $entityId = $row[$entityIdField];
  816. if (!isset($this->_items[$entityId])) {
  817. $this->_items[$entityId] = $this->getNewEmptyItem();
  818. $this->_items[$entityId]->setData($row);
  819. } else {
  820. $this->_items[$entityId]->addData($row);
  821. }
  822. }
  823. return $this;
  824. }
  825. /**
  826. * Get collection data as a 2D array
  827. *
  828. * @return array
  829. */
  830. public function exportToArray()
  831. {
  832. $result = array();
  833. $entityIdField = $this->getEntity()->getEntityIdField();
  834. foreach ($this->getItems() as $item) {
  835. $result[$item->getData($entityIdField)] = $item->getData();
  836. }
  837. return $result;
  838. }
  839. public function getRowIdFieldName()
  840. {
  841. if (is_null($this->_idFieldName)) {
  842. $this->_setIdFieldName($this->getEntity()->getIdFieldName());
  843. }
  844. return $this->getIdFieldName();
  845. }
  846. public function setRowIdFieldName($fieldName)
  847. {
  848. return $this->_setIdFieldName($fieldName);
  849. }
  850. /**
  851. * Load entities records into items
  852. *
  853. * @return Mage_Eav_Model_Entity_Collection_Abstract
  854. */
  855. public function _loadEntities($printQuery = false, $logQuery = false)
  856. {
  857. $entity = $this->getEntity();
  858. // $entityIdField = $entity->getEntityIdField();
  859. if ($this->_pageSize) {
  860. $this->getSelect()->limitPage($this->getCurPage(), $this->_pageSize);
  861. }
  862. $this->printLogQuery($printQuery, $logQuery);
  863. try {
  864. $rows = $this->_fetchAll($this->getSelect());
  865. } catch (Exception $e) {
  866. Mage::printException($e, $this->getSelect());
  867. $this->printLogQuery(true, true, $this->getSelect());
  868. throw $e;
  869. }
  870. foreach ($rows as $v) {
  871. $object = $this->getNewEmptyItem()
  872. ->setData($v);
  873. $this->addItem($object);
  874. if (isset($this->_itemsById[$object->getId()])) {
  875. $this->_itemsById[$object->getId()][] = $object;
  876. }
  877. else {
  878. $this->_itemsById[$object->getId()] = array($object);
  879. }
  880. }
  881. return $this;
  882. }
  883. /**
  884. * Load attributes into loaded entities
  885. *
  886. * @return Mage_Eav_Model_Entity_Collection_Abstract
  887. */
  888. public function _loadAttributes($printQuery = false, $logQuery = false)
  889. {
  890. if (empty($this->_items) || empty($this->_itemsById) || empty($this->_selectAttributes)) {
  891. return $this;
  892. }
  893. $entity = $this->getEntity();
  894. $entityIdField = $entity->getEntityIdField();
  895. $tableAttributes = array();
  896. foreach ($this->_selectAttributes as $attributeCode => $attributeId) {
  897. if (!$attributeId) {
  898. continue;
  899. }
  900. $attribute = Mage::getSingleton('eav/config')->getCollectionAttribute($entity->getType(), $attributeCode);
  901. if ($attribute && !$attribute->isStatic()) {
  902. $tableAttributes[$attribute->getBackendTable()][] = $attributeId;
  903. }
  904. }
  905. $selects = array();
  906. foreach ($tableAttributes as $table=>$attributes) {
  907. $selects[] = $this->_getLoadAttributesSelect($table, $attributes);
  908. }
  909. if (!empty($selects)) {
  910. try {
  911. $select = implode(' UNION ', $selects);
  912. $values = $this->_fetchAll($select);
  913. } catch (Exception $e) {
  914. Mage::printException($e, $select);
  915. $this->printLogQuery(true, true, $select);
  916. throw $e;
  917. }
  918. foreach ($values as $value) {
  919. $this->_setItemAttributeValue($value);
  920. }
  921. }
  922. return $this;
  923. }
  924. /**
  925. * Retrieve attributes load select
  926. *
  927. * @param string $table
  928. * @return Mage_Eav_Model_Entity_Collection_Abstract
  929. */
  930. protected function _getLoadAttributesSelect($table, $attributeIds=array())
  931. {
  932. if (empty($attributeIds)) {
  933. $attributeIds = $this->_selectAttributes;
  934. }
  935. $entityIdField = $this->getEntity()->getEntityIdField();
  936. $select = $this->getConnection()->select()
  937. ->from($table, array($entityIdField, 'attribute_id', 'value'))
  938. ->where('entity_type_id=?', $this->getEntity()->getTypeId())
  939. ->where("$entityIdField in (?)", array_keys($this->_itemsById))
  940. ->where('attribute_id in (?)', $attributeIds);
  941. return $select;
  942. }
  943. /**
  944. * Initialize entity ubject property value
  945. *
  946. * $valueInfo is _getLoadAttributesSelect fetch result row
  947. *
  948. * @param array $valueInfo
  949. * @return Mage_Eav_Model_Entity_Collection_Abstract
  950. */
  951. protected function _setItemAttributeValue($valueInfo)
  952. {
  953. $entityIdField = $this->getEntity()->getEntityIdField();
  954. $entityId = $valueInfo[$entityIdField];
  955. if (!isset($this->_itemsById[$entityId])) {
  956. Mage::throwException('Mage_Eav',
  957. Mage::helper('eav')->__('Data integrity: No header row found for attribute.')
  958. );
  959. }
  960. $attributeCode = array_search($valueInfo['attribute_id'], $this->_selectAttributes);
  961. if (!$attributeCode) {
  962. $attribute = Mage::getSingleton('eav/config')->getCollectionAttribute(
  963. $this->getEntity()->getType(),
  964. $valueInfo['attribute_id']
  965. );
  966. $attributeCode = $attribute->getAttributeCode();
  967. }
  968. foreach ($this->_itemsById[$entityId] as $object) {
  969. $object->setData($attributeCode, $valueInfo['value']);
  970. }
  971. return $this;
  972. }
  973. /**
  974. * Get alias for attribute value table
  975. *
  976. * @param string $attributeCode
  977. * @return string
  978. */
  979. protected function _getAttributeTableAlias($attributeCode)
  980. {
  981. return '_table_'.$attributeCode;
  982. }
  983. protected function _getAttributeFieldName($attributeCode)
  984. {
  985. if (isset($this->_joinAttributes[$attributeCode]['condition_alias'])) {
  986. return $this->_joinAttributes[$attributeCode]['condition_alias'];
  987. }
  988. if (isset($this->_staticFields[$attributeCode])) {
  989. return sprintf('e.%s', $attributeCode);
  990. }
  991. if (isset($this->_joinFields[$attributeCode])) {
  992. $attr = $this->_joinFields[$attributeCode];
  993. return $attr['table'] ? $attr['table'] .'.'.$attr['field'] : $attr['field'];
  994. }
  995. $attribute = $this->getAttribute($attributeCode);
  996. if (!$attribute) {
  997. throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Invalid attribute name: %s.', $attributeCode));
  998. }
  999. if ($attribute->isStatic()) {
  1000. if (isset($this->_joinAttributes[$attributeCode])) {
  1001. $fieldName = $this->_getAttributeTableAlias($attributeCode).'.'.$attributeCode;
  1002. } else {
  1003. $fieldName = 'e.'.$attributeCode;
  1004. }
  1005. } else {
  1006. $fieldName = $this->_getAttributeTableAlias($attributeCode).'.value';
  1007. }
  1008. return $fieldName;
  1009. }
  1010. /**
  1011. * Add attribute value table to the join if it wasn't added previously
  1012. *
  1013. * @param string $attributeCode
  1014. * @param string $joinType inner|left
  1015. * @return Mage_Eav_Model_Entity_Collection_Abstract
  1016. */
  1017. protected function _addAttributeJoin($attributeCode, $joinType='inner')
  1018. {
  1019. if (!empty($this->_filterAttributes[$attributeCode])) {
  1020. return $this;
  1021. }
  1022. $adapter = $this->getConnection();
  1023. $attrTable = $this->_getAttributeTableAlias($attributeCode);
  1024. if (isset($this->_joinAttributes[$attributeCode])) {
  1025. $attribute = $this->_joinAttributes[$attributeCode]['attribute'];
  1026. $entity = $attribute->getEntity();
  1027. $entityIdField = $entity->getEntityIdField();
  1028. $fkName = $this->_joinAttributes[$attributeCode]['bind'];
  1029. $fkAttribute = $this->_joinAttributes[$attributeCode]['bindAttribute'];
  1030. $fkTable = $this->_getAttributeTableAlias($fkName);
  1031. if ($fkAttribute->getBackend()->isStatic()) {
  1032. if (isset($this->_joinAttributes[$fkName])) {
  1033. $fk = $fkTable.".".$fkAttribute->getAttributeCode();
  1034. } else {
  1035. $fk = "e.".$fkAttribute->getAttributeCode();
  1036. }
  1037. } else {
  1038. $this->_addAttributeJoin($fkAttribute->getAttributeCode(), $joinType);
  1039. $fk = "$fkTable.value";
  1040. }
  1041. $pk = $attrTable.'.'.$this->_joinAttributes[$attributeCode]['filter'];
  1042. } else {
  1043. $entity = $this->getEntity();
  1044. $entityIdField = $entity->getEntityIdField();
  1045. $attribute = $entity->getAttribute($attributeCode);
  1046. $fk = "e.$entityIdField";
  1047. $pk = "$attrTable.$entityIdField";
  1048. }
  1049. if (!$attribute) {
  1050. throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Invalid attribute name: %s.', $attributeCode));
  1051. }
  1052. if ($attribute->getBackend()->isStatic()) {
  1053. $attrFieldName = "$attrTable.".$attribute->getAttributeCode();
  1054. } else {
  1055. $attrFieldName = "$attrTable.value";
  1056. }
  1057. $fk = $adapter->quoteColumnAs($fk, null);
  1058. $pk = $adapter->quoteColumnAs($pk, null);
  1059. $condArr = array("$pk = $fk");
  1060. if (!$attribute->getBackend()->isStatic()) {
  1061. $condArr[] = $this->getConnection()->quoteInto(
  1062. $adapter->quoteColumnAs("$attrTable.attribute_id", null) . ' = ?', $attribute->getId());
  1063. }
  1064. /**
  1065. * process join type
  1066. */
  1067. $joinMethod = ($joinType == 'left') ? 'joinLeft' : 'join';
  1068. $this->_joinAttributeToSelect($joinMethod, $attribute, $attrTable, $condArr, $attributeCode, $attrFieldName);
  1069. $this->removeAttributeToSelect($attributeCode);
  1070. $this->_filterAttributes[$attributeCode] = $attribute->getId();
  1071. /**
  1072. * Fix double join for using same as filter
  1073. */
  1074. $this->_joinFields[$attributeCode] = array(
  1075. 'table' => '',
  1076. 'field' => $attrFieldName,
  1077. );
  1078. return $this;
  1079. }
  1080. /**
  1081. * Adding join statement to collection select instance
  1082. *
  1083. * @param string $method
  1084. * @param object $attribute
  1085. * @param string $tableAlias
  1086. * @param array $condition
  1087. * @param string $fieldCode
  1088. * @param string $fieldAlias
  1089. * @return Mage_Eav_Model_Entity_Collection_Abstract
  1090. */
  1091. protected function _joinAttributeToSelect($method, $attribute, $tableAlias, $condition, $fieldCode, $fieldAlias)
  1092. {
  1093. $this->getSelect()->$method(
  1094. array($tableAlias => $attribute->getBackend()->getTable()),
  1095. '('.join(') AND (', $condition).')',
  1096. array($fieldCode=>$fieldAlias)
  1097. );
  1098. return $this;
  1099. }
  1100. /**
  1101. * Get condition sql for the attribute
  1102. *
  1103. * @see self::_getConditionSql
  1104. * @param string $attribute
  1105. * @param mixed $condition
  1106. * @param string $joinType
  1107. * @return string
  1108. */
  1109. protected function _getAttributeConditionSql($attribute, $condition, $joinType='inner')
  1110. {
  1111. if (isset($this->_joinFields[$attribute])) {
  1112. return $this->_getConditionSql($this->_getAttributeFieldName($attribute), $condition);
  1113. }
  1114. if (isset($this->_staticFields[$attribute])) {
  1115. return $this->_getConditionSql(sprintf('e.%s', $attribute), $condition);
  1116. }
  1117. // process linked attribute
  1118. if (isset($this->_joinAttributes[$attribute])) {
  1119. $entity = $this->getAttribute($attribute)->getEntity();
  1120. $entityTable = $entity->getEntityTable();
  1121. } else {
  1122. $entity = $this->getEntity();
  1123. $entityTable = 'e';
  1124. }
  1125. if ($entity->isAttributeStatic($attribute)) {
  1126. $conditionSql = $this->_getConditionSql('e.'.$attribute, $condition);
  1127. } else {
  1128. $this->_addAttributeJoin($attribute, $joinType);
  1129. if (isset($this->_joinAttributes[$attribute]['condition_alias'])) {
  1130. $field = $this->_joinAttributes[$attribute]['condition_alias'];
  1131. }
  1132. else {
  1133. $field = $this->_getAttributeTableAlias($attribute).'.value';
  1134. }
  1135. $conditionSql = $this->_getConditionSql($field, $condition);
  1136. }
  1137. return $conditionSql;
  1138. }
  1139. /**
  1140. * Set sorting order
  1141. *
  1142. * $attribute can also be an array of attributes
  1143. *
  1144. * @param string|array $attribute
  1145. * @param string $dir
  1146. * @return Mage_Eav_Model_Entity_Collection_Abstract
  1147. */
  1148. public function setOrder($attribute, $dir='desc')
  1149. {
  1150. if (is_array($attribute)) {
  1151. foreach ($attribute as $attr) {
  1152. $this->addAttributeToSort($attr, $dir);
  1153. }
  1154. } else {
  1155. $this->addAttributeToSort($attribute, $dir);
  1156. }
  1157. return $this;
  1158. }
  1159. public function toArray($arrAttributes = array())
  1160. {
  1161. $arr = array();
  1162. foreach ($this->_items as $k=>$item) {
  1163. $arr[$k] = $item->toArray($arrAttributes);
  1164. }
  1165. return $arr;
  1166. }
  1167. protected function _beforeLoad()
  1168. {
  1169. return $this;
  1170. }
  1171. protected function _afterLoad()
  1172. {
  1173. return $this;
  1174. }
  1175. /**
  1176. * Reset collection
  1177. *
  1178. * @return Mage_Eav_Model_Entity_Collection_Abstract
  1179. */
  1180. protected function _reset()
  1181. {
  1182. parent::_reset();
  1183. $this->_selectEntityTypes = array();
  1184. $this->_selectAttributes = array();
  1185. $this->_filterAttributes = array();
  1186. $this->_joinEntities = array();
  1187. $this->_joinAttributes = array();
  1188. $this->_joinFields = array();
  1189. return $this;
  1190. }
  1191. /**
  1192. * Returns already loaded element ids
  1193. *
  1194. * return array
  1195. */
  1196. public function getLoadedIds()
  1197. {
  1198. return array_keys($this->_items);
  1199. }
  1200. }