Skip to content

Instantly share code, notes, and snippets.

@dng-dev
Created September 28, 2017 18:02
Show Gist options
  • Save dng-dev/75f0cd4300a5a85b66659e434bb71dea to your computer and use it in GitHub Desktop.
Save dng-dev/75f0cd4300a5a85b66659e434bb71dea to your computer and use it in GitHub Desktop.
improves eav based loads
<?php
/**
* Magento
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@magento.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Magento to newer
* versions in the future. If you wish to customize Magento for your
* needs please refer to http://www.magento.com for more information.
*
* @category Mage
* @package Mage_Eav
* @copyright Copyright (c) 2006-2017 X.commerce, Inc. and affiliates (http://www.magento.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
/**
* Entity/Attribute/Model - entity abstract
*
* @category Mage
* @package Mage_Eav
* @author Magento Core Team <core@magentocommerce.com>
*/
abstract class Mage_Eav_Model_Entity_Abstract extends Mage_Core_Model_Resource_Abstract
implements Mage_Eav_Model_Entity_Interface
{
/**
* sorted attributes by attribute Set Id (static cache)
*
* @var array
*/
protected static $_sortedAttributesByAttributeSetId = array();
/**
* Read connection
*
* @var Varien_Db_Adapter_Pdo_Mysql
*/
protected $_read;
/**
* Write connection
*
* @var Varien_Db_Adapter_Pdo_Mysql
*/
protected $_write;
/**
* Entity type configuration
*
* @var Mage_Eav_Model_Entity_Type
*/
protected $_type;
/**
* Attributes array by attribute id
*
* @var array
*/
protected $_attributesById = array();
/**
* Attributes array by attribute name
*
* @var unknown_type
*/
protected $_attributesByCode = array();
/**
* 2-dimentional array by table name and attribute name
*
* @var array
*/
protected $_attributesByTable = array();
/**
* Attributes that are static fields in entity table
*
* @var array
*/
protected $_staticAttributes = array();
/**
* Default Attributes that are static
*
* @var array
*/
protected static $_defaultAttributes = array();
/**
* Entity table
*
* @var string
*/
protected $_entityTable;
/**
* Describe data for tables
*
* @var array
*/
protected $_describeTable = array();
/**
* Entity table identification field name
*
* @var string
*/
protected $_entityIdField;
/**
* Entity values table identification field name
*
* @var string
*/
protected $_valueEntityIdField;
/**
* Entity value table prefix
*
* @var string
*/
protected $_valueTablePrefix;
/* Entity table string
*
* @var string
*/
protected $_entityTablePrefix;
/**
* Partial load flag
*
* @var boolean
*/
protected $_isPartialLoad = false;
/**
* Partial save flag
*
* @var boolean
*/
protected $_isPartialSave = false;
/**
* Attribute set id which used for get sorted attributes
*
* @var int
*/
protected $_sortingSetId = null;
/**
* Entity attribute values per backend table to delete
*
* @var array
*/
protected $_attributeValuesToDelete = array();
/**
* Entity attribute values per backend table to save
*
* @var array
*/
protected $_attributeValuesToSave = array();
/**
* Array of describe attribute backend tables
* The table name as key
*
* @var array
*/
protected static $_attributeBackendTables = array();
/**
* Set connections for entity operations
*
* @param Zend_Db_Adapter_Abstract|string $read
* @param Zend_Db_Adapter_Abstract|string|null $write
* @return Mage_Eav_Model_Entity_Abstract
*/
public function setConnection($read, $write = null)
{
$this->_read = $read;
$this->_write = $write ? $write : $read;
return $this;
}
/**
* Resource initialization
*/
protected function _construct()
{}
/**
* Retrieve connection for read data
*
* @return Varien_Db_Adapter_Interface
*/
protected function _getReadAdapter()
{
if (is_string($this->_read)) {
$this->_read = Mage::getSingleton('core/resource')->getConnection($this->_read);
}
return $this->_read;
}
/**
* Retrieve connection for write data
*
* @return Varien_Db_Adapter_Interface
*/
protected function _getWriteAdapter()
{
if (is_string($this->_write)) {
$this->_write = Mage::getSingleton('core/resource')->getConnection($this->_write);
}
return $this->_write;
}
/**
* Retrieve read DB connection
*
* @return Varien_Db_Adapter_Interface
*/
public function getReadConnection()
{
return $this->_getReadAdapter();
}
/**
* Retrieve write DB connection
*
* @return Varien_Db_Adapter_Interface
*/
public function getWriteConnection()
{
return $this->_getWriteAdapter();
}
/**
* For compatibility with Mage_Core_Model_Abstract
*
* @return string
*/
public function getIdFieldName()
{
return $this->getEntityIdField();
}
/**
* Retreive table name
*
* @param string $alias
* @return string
*/
public function getTable($alias)
{
return Mage::getSingleton('core/resource')->getTableName($alias);
}
/**
* Set configuration for the entity
*
* Accepts config node or name of entity type
*
* @param string|Mage_Eav_Model_Entity_Type $type
* @return Mage_Eav_Model_Entity_Abstract
*/
public function setType($type)
{
$this->_type = Mage::getSingleton('eav/config')->getEntityType($type);
$this->_afterSetConfig();
return $this;
}
/**
* Retrieve current entity config
*
* @return Mage_Eav_Model_Entity_Type
*/
public function getEntityType()
{
if (empty($this->_type)) {
throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Entity is not initialized'));
}
return $this->_type;
}
/**
* Get entity type name
*
* @return string
*/
public function getType()
{
return $this->getEntityType()->getEntityTypeCode();
}
/**
* Get entity type id
*
* @return int
*/
public function getTypeId()
{
return (int)$this->getEntityType()->getEntityTypeId();
}
/**
* Unset attributes
*
* If NULL or not supplied removes configuration of all attributes
* If string - removes only one, if array - all specified
*
* @param array|string|null $attributes
* @return Mage_Eav_Model_Entity_Abstract
*/
public function unsetAttributes($attributes = null)
{
if ($attributes === null) {
$this->_attributesByCode = array();
$this->_attributesById = array();
$this->_attributesByTable = array();
return $this;
}
if (is_string($attributes)) {
$attributes = array($attributes);
}
if (!is_array($attributes)) {
throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Unknown parameter'));
}
foreach ($attributes as $attrCode) {
if (!isset($this->_attributesByCode[$attrCode])) {
continue;
}
$attr = $this->getAttribute($attrCode);
unset($this->_attributesById[$attr->getId()]);
unset($this->_attributesByTable[$attr->getBackend()->getTable()][$attrCode]);
unset($this->_attributesByCode[$attrCode]);
}
return $this;
}
/**
* Retrieve attribute instance by name, id or config node
*
* This will add the attribute configuration to entity's attributes cache
*
* If attribute is not found false is returned
*
* @param string|integer|Mage_Core_Model_Config_Element $attribute
* @return Mage_Eav_Model_Entity_Attribute_Abstract || false
*/
public function getAttribute($attribute)
{
if (is_numeric($attribute)) {
$attributeId = $attribute;
if (isset($this->_attributesById[$attributeId])) {
return $this->_attributesById[$attributeId];
}
$attributeInstance = Mage::getSingleton('eav/config')->getAttribute($this->getEntityType(), $attributeId);
if ($attributeInstance) {
$attributeCode = $attributeInstance->getAttributeCode();
}
} else if (is_string($attribute)) {
$attributeCode = $attribute;
if (isset($this->_attributesByCode[$attributeCode])) {
return $this->_attributesByCode[$attributeCode];
}
$attributeInstance = Mage::getSingleton('eav/config')
->getAttribute($this->getEntityType(), $attributeCode);
if (!$attributeInstance->getAttributeCode() && in_array($attribute, $this->getDefaultAttributes())) {
$attributeInstance
->setAttributeCode($attribute)
->setBackendType(Mage_Eav_Model_Entity_Attribute_Abstract::TYPE_STATIC)
->setIsGlobal(1)
->setEntity($this)
->setEntityType($this->getEntityType())
->setEntityTypeId($this->getEntityType()->getId());
}
} else if ($attribute instanceof Mage_Eav_Model_Entity_Attribute_Abstract) {
$attributeInstance = $attribute;
$attributeCode = $attributeInstance->getAttributeCode();
if (isset($this->_attributesByCode[$attributeCode])) {
return $this->_attributesByCode[$attributeCode];
}
}
if (empty($attributeInstance)
|| !($attributeInstance instanceof Mage_Eav_Model_Entity_Attribute_Abstract)
|| (!$attributeInstance->getId()
&& !in_array($attributeInstance->getAttributeCode(), $this->getDefaultAttributes()))
) {
return false;
}
$attribute = $attributeInstance;
if (empty($attributeId)) {
$attributeId = $attribute->getAttributeId();
}
if (!$attribute->getAttributeCode()) {
$attribute->setAttributeCode($attributeCode);
}
if (!$attribute->getAttributeModel()) {
$attribute->setAttributeModel($this->_getDefaultAttributeModel());
}
$this->addAttribute($attribute);
return $attribute;
}
/**
* Return default static virtual attribute that doesn't exists in EAV attributes
*
* @param string $attributeCode
* @return Mage_Eav_Model_Entity_Attribute
*/
protected function _getDefaultAttribute($attributeCode)
{
$entityTypeId = $this->getEntityType()->getId();
if (!isset(self::$_defaultAttributes[$entityTypeId][$attributeCode])) {
$attribute = Mage::getModel($this->getEntityType()->getAttributeModel())
->setAttributeCode($attributeCode)
->setBackendType(Mage_Eav_Model_Entity_Attribute_Abstract::TYPE_STATIC)
->setIsGlobal(1)
->setEntityType($this->getEntityType())
->setEntityTypeId($this->getEntityType()->getId());
self::$_defaultAttributes[$entityTypeId][$attributeCode] = $attribute;
}
return self::$_defaultAttributes[$entityTypeId][$attributeCode];
}
/**
* Adding attribute to entity
*
* @param Mage_Eav_Model_Entity_Attribute_Abstract $attribute
* @return Mage_Eav_Model_Entity_Abstract
*/
public function addAttribute(Mage_Eav_Model_Entity_Attribute_Abstract $attribute)
{
$attribute->setEntity($this);
$attributeCode = $attribute->getAttributeCode();
$this->_attributesByCode[$attributeCode] = $attribute;
if ($attribute->isStatic()) {
$this->_staticAttributes[$attributeCode] = $attribute;
} else {
$this->_attributesById[$attribute->getId()] = $attribute;
$this->_attributesByTable[$attribute->getBackendTable()][$attributeCode] = $attribute;
}
return $this;
}
/**
* Retreive partial load flag
*
* @param boolean $flag
* @return boolean
*/
public function isPartialLoad($flag = null)
{
$result = $this->_isPartialLoad;
if ($flag !== null) {
$this->_isPartialLoad = (bool)$flag;
}
return $result;
}
/**
* Retreive partial save flag
*
* @param boolean $flag
* @return boolean
*/
public function isPartialSave($flag = null)
{
$result = $this->_isPartialSave;
if ($flag !== null) {
$this->_isPartialSave = (bool)$flag;
}
return $result;
}
/**
* Retrieve configuration for all attributes
*
* @return Mage_Eav_Model_Entity_Attribute_Abstract
*/
public function loadAllAttributes($object=null)
{
$attributeCodes = Mage::getSingleton('eav/config')
->getEntityAttributeCodes($this->getEntityType(), $object);
/**
* Check and init default attributes
*/
$defaultAttributes = $this->getDefaultAttributes();
foreach ($defaultAttributes as $attributeCode) {
$attributeIndex = array_search($attributeCode, $attributeCodes);
if ($attributeIndex !== false) {
$this->getAttribute($attributeCodes[$attributeIndex]);
unset($attributeCodes[$attributeIndex]);
} else {
$this->addAttribute($this->_getDefaultAttribute($attributeCode));
}
}
foreach ($attributeCodes as $code) {
$this->getAttribute($code);
}
return $this;
}
/**
* Retrieve sorted attributes
*
* @param int $setId
* @return array
*/
public function getSortedAttributes($setId = null)
{
if(null !== $setId && true === isset(self::$_sortedAttributesByAttributeSetId[$setId])){
return self::$_sortedAttributesByAttributeSetId[$setId];
}
$attributes = $this->getAttributesByCode();
if ($setId === null) {
$setId = $this->getEntityType()->getDefaultAttributeSetId();
}
// initialize set info
Mage::getSingleton('eav/entity_attribute_set')
->addSetInfo($this->getEntityType(), $attributes, $setId);
foreach ($attributes as $code => $attribute) {
/* @var $attribute Mage_Eav_Model_Entity_Attribute_Abstract */
if (!$attribute->isInSet($setId)) {
unset($attributes[$code]);
}
}
$this->_sortingSetId = $setId;
uasort($attributes, array($this, 'attributesCompare'));
if(null !== $setId){
self::$_sortedAttributesByAttributeSetId[$setId] = $attributes;
}
return $attributes;
}
/**
* Compare attributes
*
* @param Mage_Eav_Model_Entity_Attribute $attribute1
* @param Mage_Eav_Model_Entity_Attribute $attribute2
* @return int
*/
public function attributesCompare($attribute1, $attribute2)
{
$sortPath = sprintf('attribute_set_info/%s/sort', $this->_sortingSetId);
$groupSortPath = sprintf('attribute_set_info/%s/group_sort', $this->_sortingSetId);
$sort1 = ($attribute1->getData($groupSortPath) * 1000) + ($attribute1->getData($sortPath) * 0.0001);
$sort2 = ($attribute2->getData($groupSortPath) * 1000) + ($attribute2->getData($sortPath) * 0.0001);
if ($sort1 > $sort2) {
return 1;
} elseif ($sort1 < $sort2) {
return -1;
}
return 0;
}
/**
* Check whether the attribute is Applicable to the object
*
* @param Varien_Object $object
* @param Mage_Eav_Model_Entity_Attribute_Abstract $attribute
* @return boolean
*/
protected function _isApplicableAttribute($object, $attribute)
{
return true;
}
/**
* Walk through the attributes and run method with optional arguments
*
* Returns array with results for each attribute
*
* if $method is in format "part/method" will run method on specified part
* for example: $this->walkAttributes('backend/validate');
*
* @param string $method
* @param array $args
* @param array $part attribute, backend, frontend, source
* @return array
*/
public function walkAttributes($partMethod, array $args = array())
{
$methodArr = explode('/', $partMethod);
switch (sizeof($methodArr)) {
case 1:
$part = 'attribute';
$method = $methodArr[0];
break;
case 2:
$part = $methodArr[0];
$method = $methodArr[1];
break;
}
$results = array();
foreach ($this->getAttributesByCode() as $attrCode => $attribute) {
if (isset($args[0]) && is_object($args[0]) && !$this->_isApplicableAttribute($args[0], $attribute)) {
continue;
}
switch ($part) {
case 'attribute':
$instance = $attribute;
break;
case 'backend':
$instance = $attribute->getBackend();
break;
case 'frontend':
$instance = $attribute->getFrontend();
break;
case 'source':
$instance = $attribute->getSource();
break;
}
if (!$this->_isCallableAttributeInstance($instance, $method, $args)) {
continue;
}
try {
$results[$attrCode] = call_user_func_array(array($instance, $method), $args);
} catch (Mage_Eav_Model_Entity_Attribute_Exception $e) {
throw $e;
} catch (Exception $e) {
$e = Mage::getModel('eav/entity_attribute_exception', $e->getMessage());
$e->setAttributeCode($attrCode)->setPart($part);
throw $e;
}
}
return $results;
}
/**
* Check whether attribute instance (attribute, backend, frontend or source) has method and applicable
*
* @param Mage_Eav_Model_Entity_Attribute_Abstract|Mage_Eav_Model_Entity_Attribute_Backend_Abstract|Mage_Eav_Model_Entity_Attribute_Frontend_Abstract|Mage_Eav_Model_Entity_Attribute_Source_Abstract $instance
* @param string $method
* @param array $args array of arguments
* @return boolean
*/
protected function _isCallableAttributeInstance($instance, $method, $args)
{
if (!is_object($instance) || !method_exists($instance, $method)) {
return false;
}
return true;
}
/**
* Get attributes by name array
*
* @return array
*/
public function getAttributesByCode()
{
return $this->_attributesByCode;
}
/**
* Get attributes by id array
*
* @return array
*/
public function getAttributesById()
{
return $this->_attributesById;
}
/**
* Get attributes by table and name array
*
* @return array
*/
public function getAttributesByTable()
{
return $this->_attributesByTable;
}
/**
* Get entity table name
*
* @return string
*/
public function getEntityTable()
{
if (!$this->_entityTable) {
$table = $this->getEntityType()->getEntityTable();
if (!$table) {
$table = Mage_Eav_Model_Entity::DEFAULT_ENTITY_TABLE;
}
$this->_entityTable = Mage::getSingleton('core/resource')->getTableName($table);
}
return $this->_entityTable;
}
/**
* Get entity id field name in entity table
*
* @return string
*/
public function getEntityIdField()
{
if (!$this->_entityIdField) {
$this->_entityIdField = $this->getEntityType()->getEntityIdField();
if (!$this->_entityIdField) {
$this->_entityIdField = Mage_Eav_Model_Entity::DEFAULT_ENTITY_ID_FIELD;
}
}
return $this->_entityIdField;
}
/**
* Get default entity id field name in attribute values tables
*
* @return string
*/
public function getValueEntityIdField()
{
return $this->getEntityIdField();
}
/**
* Get prefix for value tables
*
* @return string
*/
public function getValueTablePrefix()
{
if (!$this->_valueTablePrefix) {
$prefix = (string)$this->getEntityType()->getValueTablePrefix();
if (!empty($prefix)) {
$this->_valueTablePrefix = $prefix;
/**
* entity type prefix include DB table name prefix
*/
//Mage::getSingleton('core/resource')->getTableName($prefix);
} else {
$this->_valueTablePrefix = $this->getEntityTable();
}
}
return $this->_valueTablePrefix;
}
/**
* Get entity table prefix for value
*
* @return string
*/
public function getEntityTablePrefix()
{
if (empty($this->_entityTablePrefix)) {
$prefix = $this->getEntityType()->getEntityTablePrefix();
if (empty($prefix)) {
$prefix = $this->getEntityType()->getEntityTable();
if (empty($prefix)) {
$prefix = Mage_Eav_Model_Entity::DEFAULT_ENTITY_TABLE;
}
}
$this->_entityTablePrefix = $prefix;
}
return $this->_entityTablePrefix;
}
/**
* Check whether the attribute is a real field in entity table
*
* @see Mage_Eav_Model_Entity_Abstract::getAttribute for $attribute format
* @param integer|string|Mage_Eav_Model_Entity_Attribute_Abstract $attribute
*
* @return boolean
*/
public function isAttributeStatic($attribute)
{
$attrInstance = $this->getAttribute($attribute);
return $attrInstance && $attrInstance->getBackend()->isStatic();
}
/**
* Validate all object's attributes against configuration
*
* @param Varien_Object $object
* @throws Mage_Eav_Model_Entity_Attribute_Exception
* @return bool|array
*/
public function validate($object)
{
$this->loadAllAttributes($object);
$result = $this->walkAttributes('backend/validate', array($object));
$errors = array();
foreach ($result as $attributeCode => $error) {
if ($error === false) {
$errors[$attributeCode] = true;
} elseif (is_string($error)) {
$errors[$attributeCode] = $error;
}
}
if (!$errors) {
return true;
}
return $errors;
}
/**
* Set new increment id to object
*
* @param Varien_Object $object
* @return Mage_Eav_Model_Entity_Abstract
*/
public function setNewIncrementId(Varien_Object $object)
{
if ($object->getIncrementId()) {
return $this;
}
$incrementId = $this->getEntityType()->fetchNewIncrementId($object->getStoreId());
if ($incrementId !== false) {
$object->setIncrementId($incrementId);
}
return $this;
}
/**
* Check attribute unique value
*
* @param Mage_Eav_Model_Entity_Attribute_Abstract $attribute
* @param Varien_Object $object
* @return boolean
*/
public function checkAttributeUniqueValue(Mage_Eav_Model_Entity_Attribute_Abstract $attribute, $object)
{
$adapter = $this->_getReadAdapter();
$select = $adapter->select();
if ($attribute->getBackend()->getType() === 'static') {
$value = $object->getData($attribute->getAttributeCode());
$bind = array(
'entity_type_id' => $this->getTypeId(),
'attribute_code' => trim($value)
);
$select
->from($this->getEntityTable(), $this->getEntityIdField())
->where('entity_type_id = :entity_type_id')
->where($attribute->getAttributeCode() . ' = :attribute_code');
} else {
$value = $object->getData($attribute->getAttributeCode());
if ($attribute->getBackend()->getType() == 'datetime') {
$date = new Zend_Date($value, Varien_Date::DATE_INTERNAL_FORMAT);
$value = $date->toString(Varien_Date::DATETIME_INTERNAL_FORMAT);
}
$bind = array(
'entity_type_id' => $this->getTypeId(),
'attribute_id' => $attribute->getId(),
'value' => trim($value)
);
$select
->from($attribute->getBackend()->getTable(), $attribute->getBackend()->getEntityIdField())
->where('entity_type_id = :entity_type_id')
->where('attribute_id = :attribute_id')
->where('value = :value');
}
$data = $adapter->fetchCol($select, $bind);
if ($object->getId()) {
if (isset($data[0])) {
return $data[0] == $object->getId();
}
return true;
}
return !count($data);
}
/**
* Retreive default source model
*
* @return string
*/
public function getDefaultAttributeSourceModel()
{
return Mage_Eav_Model_Entity::DEFAULT_SOURCE_MODEL;
}
/**
* Load entity's attributes into the object
*
* @param Mage_Core_Model_Abstract $object
* @param integer $entityId
* @param array|null $attributes
* @return Mage_Eav_Model_Entity_Abstract
*/
public function load($object, $entityId, $attributes = array())
{
Varien_Profiler::start('__EAV_LOAD_MODEL__');
/**
* Load object base row data
*/
$select = $this->_getLoadRowSelect($object, $entityId);
$row = $this->_getReadAdapter()->fetchRow($select);
if (is_array($row)) {
$object->addData($row);
} else {
$object->isObjectNew(true);
}
if (empty($attributes)) {
$this->loadAllAttributes($object);
} else {
foreach ($attributes as $attrCode) {
$this->getAttribute($attrCode);
}
}
$this->_loadModelAttributes($object);
$object->setOrigData();
Varien_Profiler::start('__EAV_LOAD_MODEL_AFTER_LOAD__');
$this->_afterLoad($object);
Varien_Profiler::stop('__EAV_LOAD_MODEL_AFTER_LOAD__');
Varien_Profiler::stop('__EAV_LOAD_MODEL__');
return $this;
}
/**
* Load model attributes data
*
* @param Mage_Core_Model_Abstract $object
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _loadModelAttributes($object)
{
if (!$object->getId()) {
return $this;
}
Varien_Profiler::start('__EAV_LOAD_MODEL_ATTRIBUTES__');
$tableAttributes = array();
$attributeTypes = array();
foreach ($this->getAttributesById() as $attributeId => $attribute){
/** @var Mage_Catalog_Model_Resource_Eav_Attribute $attribute */
if ($attribute instanceof Mage_Catalog_Model_Resource_Eav_Attribute && !$attribute->isStatic()) {
$tableAttributes[$attribute->getBackendTable()][] = $attributeId;
if (!isset($attributeTypes[$attribute->getBackendTable()])) {
$attributeTypes[$attribute->getBackendTable()] = $attribute->getBackendType();
}
}
}
// get eav select skeleton
$select = $this->_getLoadAttributesSelectSkeleton($object);
foreach ($tableAttributes as $table=>$attributes) {
try {
/** @var Varien_Db_Select $select */
$select->reset(Varien_Db_Select::FROM);
$select->from(array('eav_value' => $table), null);
$values = $this->getReadConnection()->fetchAll($select);
uasort($values, function ($value1, $value2){
if ((int)$value1['store_id'] == $value2['store_id']){
return 0;
}
return $value1['store_id'] < $value2['store_id'] ? -1 : 1;
});
} catch (Exception $e) {
Mage::printException($e, $select);
$this->printLogQuery(true, true, $select);
throw $e;
}
foreach ($values as $value) {
$this->_setAttributeValue($object, $value);
}
}
Varien_Profiler::stop('__EAV_LOAD_MODEL_ATTRIBUTES__');
return $this;
foreach (array_keys($this->getAttributesByTable()) as $table) {
$attribute = current($this->_attributesByTable[$table]);
$eavType = $attribute->getBackendType();
$select = $this->_getLoadAttributesSelect($object, $table);
$selects[$eavType][] = $this->_addLoadAttributesSelectFields($select, $table, $eavType);
}
$selectGroups = Mage::getResourceHelper('eav')->getLoadAttributesSelectGroups($selects);
foreach ($selectGroups as $selects) {
if (!empty($selects)) {
$select = $this->_prepareLoadSelect($selects);
//reset order by because we sorting it in php to avoid filesort and tempoary table;
$select->reset(Zend_Db_Select::ORDER);
$values = $this->_getReadAdapter()->fetchAll($select);
uasort($values, function ($value1, $value2){
if ((int)$value1['store_id'] == $value2['store_id']){
return 0;
}
return $value1['store_id'] < $value2['store_id'] ? -1 : 1;
});
foreach ($values as $valueRow) {
$this->_setAttributeValue($object, $valueRow);
}
}
}
$this->_setAttributeValue($object, $valueRow);
Varien_Profiler::stop('__EAV_LOAD_MODEL_ATTRIBUTES__');
return $this;
}
/**
* load attribute select skeleton query
*
* @param Mage_Ca
* @return Varien_Db_Select
*/
protected function _getLoadAttributesSelectSkeleton($object)
{
$entityIdField = $this->getEntityIdField();
$select = $this->_getReadAdapter()->select()
->from('eav_value', null)
->columns(array($entityIdField, 'attribute_id', 'store_id', 'value', 'value_id'), 'eav_value')
->where('entity_type_id = ?', (int)$this->getTypeId())
->where("$entityIdField = ?", (int)$object->getId())
->where('attribute_id IN (?)', array_map('intval', array_keys($this->getAttributesById())))
->order(new Zend_Db_Expr('NULL'));
$storeId = $object->getStoreId();
if((int)$storeId > 0){
$select->where('store_id IN(?)', array((int)$storeId, (int)$this->getDefaultStoreId()));
}else{
$select->where('store_id = ?', (int)$storeId);
}
return $select;
}
/**
* Prepare select object for loading entity attributes values
*
* @param array $selects
* @return Zend_Db_Select
*/
protected function _prepareLoadSelect(array $selects)
{
return $this->_getReadAdapter()->select()->union($selects, Zend_Db_Select::SQL_UNION_ALL);
}
/**
* Retrieve select object for loading base entity row
*
* @param Varien_Object $object
* @param mixed $rowId
* @return Zend_Db_Select
*/
protected function _getLoadRowSelect($object, $rowId)
{
$select = $this->_getReadAdapter()->select()
->from($this->getEntityTable())
->where($this->getEntityIdField() . ' =?', $rowId);
return $select;
}
/**
* Retrieve select object for loading entity attributes values
*
* @param Varien_Object $object
* @param mixed $rowId
* @return Zend_Db_Select
*/
protected function _getLoadAttributesSelect($object, $table)
{
$select = $this->_getReadAdapter()->select()
->from($table, array())
->where($this->getEntityIdField() . ' =?', $object->getId());
return $select;
}
/**
* Adds Columns prepared for union
*
* @param Varien_Db_Select $select
* @param string $table
* @param string $type
* @return Varien_Db_Select
*/
protected function _addLoadAttributesSelectFields($select, $table, $type)
{
$select->columns(
Mage::getResourceHelper('eav')->attributeSelectFields($table, $type)
);
return $select;
}
/**
* Initialize attribute value for object
*
* @deprecated after 1.5.1.0 - mistake in method name
*
* @param Varien_Object $object
* @param array $valueRow
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _setAttribteValue($object, $valueRow)
{
return _setAttributeValue($object, $valueRow);
}
/**
* Initialize attribute value for object
*
* @param Varien_Object $object
* @param array $valueRow
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _setAttributeValue($object, $valueRow)
{
$attribute = $this->getAttribute($valueRow['attribute_id']);
if ($attribute) {
$attributeCode = $attribute->getAttributeCode();
$object->setData($attributeCode, $valueRow['value']);
$attribute->getBackend()->setEntityValueId($object, $valueRow['value_id']);
}
return $this;
}
/**
* Save entity's attributes into the object's resource
*
* @param Varien_Object $object
* @return Mage_Eav_Model_Entity_Abstract
*/
public function save(Varien_Object $object)
{
if ($object->isDeleted()) {
return $this->delete($object);
}
if (!$this->isPartialSave()) {
$this->loadAllAttributes($object);
}
if (!$object->getEntityTypeId()) {
$object->setEntityTypeId($this->getTypeId());
}
$object->setParentId((int) $object->getParentId());
$this->_beforeSave($object);
$this->_processSaveData($this->_collectSaveData($object));
$this->_afterSave($object);
return $this;
}
/**
* Retrieve Object instance with original data
*
* @param Varien_Object $object
* @return Varien_Object
*/
protected function _getOrigObject($object)
{
$className = get_class($object);
$origObject = new $className();
$origObject->setData(array());
$this->load($origObject, $object->getData($this->getEntityIdField()));
return $origObject;
}
/**
* Prepare entity object data for save
*
* result array structure:
* array (
* 'newObject', 'entityRow', 'insert', 'update', 'delete'
* )
*
* @param Varien_Object $newObject
* @return array
*/
protected function _collectSaveData($newObject)
{
$newData = $newObject->getData();
$entityId = $newObject->getData($this->getEntityIdField());
// define result data
$entityRow = array();
$insert = array();
$update = array();
$delete = array();
if (!empty($entityId)) {
$origData = $newObject->getOrigData();
/**
* get current data in db for this entity if original data is empty
*/
if (empty($origData)) {
$origData = $this->_getOrigObject($newObject)->getOrigData();
}
/**
* drop attributes that are unknown in new data
* not needed after introduction of partial entity loading
*/
foreach ($origData as $k => $v) {
if (!array_key_exists($k, $newData)) {
unset($origData[$k]);
}
}
} else {
$origData = array();
}
$staticFields = $this->_getWriteAdapter()->describeTable($this->getEntityTable());
$staticFields = array_keys($staticFields);
$attributeCodes = array_keys($this->_attributesByCode);
foreach ($newData as $k => $v) {
/**
* Check attribute information
*/
if (is_numeric($k) || is_array($v)) {
continue;
}
/**
* Check if data key is presented in static fields or attribute codes
*/
if (!in_array($k, $staticFields) && !in_array($k, $attributeCodes)) {
continue;
}
$attribute = $this->getAttribute($k);
if (empty($attribute)) {
continue;
}
$attrId = $attribute->getAttributeId();
/**
* if attribute is static add to entity row and continue
*/
if ($this->isAttributeStatic($k)) {
$entityRow[$k] = $this->_prepareStaticValue($k, $v);
continue;
}
/**
* Check comparability for attribute value
*/
if ($this->_canUpdateAttribute($attribute, $v, $origData)) {
if ($this->_isAttributeValueEmpty($attribute, $v)) {
$delete[$attribute->getBackend()->getTable()][] = array(
'attribute_id' => $attrId,
'value_id' => $attribute->getBackend()->getEntityValueId($newObject)
);
} elseif ($v !== $origData[$k]) {
$update[$attrId] = array(
'value_id' => $attribute->getBackend()->getEntityValueId($newObject),
'value' => $v,
);
}
} else if (!$this->_isAttributeValueEmpty($attribute, $v)) {
$insert[$attrId] = $v;
}
}
$result = compact('newObject', 'entityRow', 'insert', 'update', 'delete');
return $result;
}
/**
* Return if attribute exists in original data array.
*
* @param Mage_Eav_Model_Entity_Attribute_Abstract $attribute
* @param mixed $value New value of the attribute. Can be used in subclasses.
* @param array $origData
* @return bool
*/
protected function _canUpdateAttribute(Mage_Eav_Model_Entity_Attribute_Abstract $attribute, $v, array &$origData)
{
return array_key_exists($attribute->getAttributeCode(), $origData);
}
/**
* Retrieve static field properties
*
* @param string $field
* @return array
*/
protected function _getStaticFieldProperties($field)
{
if (empty($this->_describeTable[$this->getEntityTable()])) {
$this->_describeTable[$this->getEntityTable()] = $this->_getWriteAdapter()
->describeTable($this->getEntityTable());
}
if (isset($this->_describeTable[$this->getEntityTable()][$field])) {
return $this->_describeTable[$this->getEntityTable()][$field];
}
return false;
}
/**
* Prepare static value for save
*
* @param string $key
* @param mixed $value
* @return mixed
*/
protected function _prepareStaticValue($key, $value)
{
$fieldProp = $this->_getStaticFieldProperties($key);
if (!$fieldProp) {
return $value;
}
if ($fieldProp['DATA_TYPE'] == 'decimal') {
$value = Mage::app()->getLocale()->getNumber($value);
}
return $value;
}
/**
* Save object collected data
*
* @param array $saveData array('newObject', 'entityRow', 'insert', 'update', 'delete')
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _processSaveData($saveData)
{
$this->_attributeValuesToSave = array();
$this->_attributeValuesToDelete = array();
/**
* Import variables from save data array
*
* @see Mage_Eav_Model_Entity_Attribute_Abstract::_collectSaveData()
*
* @var array $entityRow
* @var Mage_Core_Model_Abstract $newObject
* @var array $insert
* @var array $update
* @var array $delete
*/
$newObject = $saveData['newObject'];
$entityRow = $saveData['entityRow'];
$insert = $saveData['insert'];
$update = $saveData['update'];
$delete = $saveData['delete'];
$adapter = $this->_getWriteAdapter();
$insertEntity = true;
$entityTable = $this->getEntityTable();
$entityIdField = $this->getEntityIdField();
$entityId = $newObject->getId();
unset($entityRow[$entityIdField]);
if (!empty($entityId) && is_numeric($entityId)) {
$bind = array('entity_id' => $entityId);
$select = $adapter->select()
->from($entityTable, $entityIdField)
->where("{$entityIdField} = :entity_id");
$result = $adapter->fetchOne($select, $bind);
if ($result) {
$insertEntity = false;
}
} else {
$entityId = null;
}
/**
* Process base row
*/
$entityObject = new Varien_Object($entityRow);
$entityRow = $this->_prepareDataForTable($entityObject, $entityTable);
if ($insertEntity) {
if (!empty($entityId)) {
$entityRow[$entityIdField] = $entityId;
$adapter->insertForce($entityTable, $entityRow);
} else {
$adapter->insert($entityTable, $entityRow);
$entityId = $adapter->lastInsertId($entityTable);
}
$newObject->setId($entityId);
} else {
$where = sprintf('%s=%d', $adapter->quoteIdentifier($entityIdField), $entityId);
$adapter->update($entityTable, $entityRow, $where);
}
/**
* insert attribute values
*/
if (!empty($insert)) {
foreach ($insert as $attributeId => $value) {
$attribute = $this->getAttribute($attributeId);
$this->_insertAttribute($newObject, $attribute, $value);
}
}
/**
* update attribute values
*/
if (!empty($update)) {
foreach ($update as $attributeId => $v) {
$attribute = $this->getAttribute($attributeId);
$this->_updateAttribute($newObject, $attribute, $v['value_id'], $v['value']);
}
}
/**
* delete empty attribute values
*/
if (!empty($delete)) {
foreach ($delete as $table => $values) {
$this->_deleteAttributes($newObject, $table, $values);
}
}
$this->_processAttributeValues();
$newObject->isObjectNew(false);
return $this;
}
/**
* Insert entity attribute value
*
* @param Varien_Object $object
* @param Mage_Eav_Model_Entity_Attribute_Abstract $attribute
* @param mixed $value
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _insertAttribute($object, $attribute, $value)
{
return $this->_saveAttribute($object, $attribute, $value);
}
/**
* Update entity attribute value
*
* @param Varien_Object $object
* @param Mage_Eav_Model_Entity_Attribute_Abstract $attribute
* @param mixed $valueId
* @param mixed $value
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _updateAttribute($object, $attribute, $valueId, $value)
{
return $this->_saveAttribute($object, $attribute, $value);
}
/**
* Save entity attribute value
*
* Collect for mass save
*
* @param Mage_Core_Model_Abstract $object
* @param Mage_Eav_Model_Entity_Attribute_Abstract $attribute
* @param mixed $value
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _saveAttribute($object, $attribute, $value)
{
$table = $attribute->getBackend()->getTable();
if (!isset($this->_attributeValuesToSave[$table])) {
$this->_attributeValuesToSave[$table] = array();
}
$entityIdField = $attribute->getBackend()->getEntityIdField();
$data = array(
'entity_type_id' => $object->getEntityTypeId(),
$entityIdField => $object->getId(),
'attribute_id' => $attribute->getId(),
'value' => $this->_prepareValueForSave($value, $attribute)
);
$this->_attributeValuesToSave[$table][] = $data;
return $this;
}
/**
* Save and detele collected attribute values
*
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _processAttributeValues()
{
try {
$adapter = $this->_getWriteAdapter();
foreach ($this->_attributeValuesToSave as $table => $data) {
$adapter->insertOnDuplicate($table, $data, array('value'));
}
foreach ($this->_attributeValuesToDelete as $table => $valueIds) {
$adapter->delete($table, array('value_id IN (?)' => $valueIds));
}
// reset data arrays
$this->_attributeValuesToSave = array();
$this->_attributeValuesToDelete = array();
} catch (Exception $e) {
$this->_attributeValuesToSave = array();
$this->_attributeValuesToDelete = array();
throw $e;
}
return $this;
}
/**
* Prepare value for save
*
* @param mixed $value
* @param Mage_Eav_Model_Entity_Attribute_Abstract $attribute
* @return mixed
*/
protected function _prepareValueForSave($value, Mage_Eav_Model_Entity_Attribute_Abstract $attribute)
{
if ($attribute->getBackendType() == 'decimal') {
return Mage::app()->getLocale()->getNumber($value);
}
$backendTable = $attribute->getBackendTable();
if (!isset(self::$_attributeBackendTables[$backendTable])) {
self::$_attributeBackendTables[$backendTable] = $this->_getReadAdapter()->describeTable($backendTable);
}
$describe = self::$_attributeBackendTables[$backendTable];
return $this->_getReadAdapter()->prepareColumnValue($describe['value'], $value);
}
/**
* Delete entity attribute values
*
* @param Varien_Object $object
* @param string $table
* @param array $info
* @return Varien_Object
*/
protected function _deleteAttributes($object, $table, $info)
{
$valueIds = array();
foreach ($info as $itemData) {
$valueIds[] = $itemData['value_id'];
}
if (empty($valueIds)) {
return $this;
}
if (isset($this->_attributeValuesToDelete[$table])) {
$this->_attributeValuesToDelete[$table] = array_merge($this->_attributeValuesToDelete[$table], $valueIds);
} else {
$this->_attributeValuesToDelete[$table] = $valueIds;
}
return $this;
}
/**
* Save attribute
*
* @param Varien_Object $object
* @param string $attributeCode
* @return Mage_Eav_Model_Entity_Abstract
*/
public function saveAttribute(Varien_Object $object, $attributeCode)
{
$this->_attributeValuesToSave = array();
$this->_attributeValuesToDelete = array();
$attribute = $this->getAttribute($attributeCode);
$backend = $attribute->getBackend();
$table = $backend->getTable();
$entity = $attribute->getEntity();
$entityIdField = $entity->getEntityIdField();
$adapter = $this->_getWriteAdapter();
$row = array(
'entity_type_id' => $entity->getTypeId(),
'attribute_id' => $attribute->getId(),
$entityIdField => $object->getData($entityIdField),
);
$newValue = $object->getData($attributeCode);
if ($attribute->isValueEmpty($newValue)) {
$newValue = null;
}
$whereArr = array();
foreach ($row as $field => $value) {
$whereArr[] = $adapter->quoteInto($field . '=?', $value);
}
$where = implode(' AND ', $whereArr);
$adapter->beginTransaction();
try {
$select = $adapter->select()
->from($table, 'value_id')
->where($where);
$origValueId = $adapter->fetchOne($select);
if ($origValueId === false && ($newValue !== null)) {
$this->_insertAttribute($object, $attribute, $newValue);
} elseif ($origValueId !== false && ($newValue !== null)) {
$this->_updateAttribute($object, $attribute, $origValueId, $newValue);
} elseif ($origValueId !== false && ($newValue === null)) {
$adapter->delete($table, $where);
}
$this->_processAttributeValues();
$adapter->commit();
} catch (Exception $e) {
$adapter->rollback();
throw $e;
}
return $this;
}
/**
* Delete entity using current object's data
*
* @return Mage_Eav_Model_Entity_Abstract
*/
public function delete($object)
{
if (is_numeric($object)) {
$id = (int)$object;
} elseif ($object instanceof Varien_Object) {
$id = (int)$object->getId();
}
$this->_beforeDelete($object);
try {
$where = array(
$this->getEntityIdField() . '=?' => $id
);
$this->_getWriteAdapter()->delete($this->getEntityTable(), $where);
$this->loadAllAttributes($object);
foreach ($this->getAttributesByTable() as $table => $attributes) {
$this->_getWriteAdapter()->delete($table, $where);
}
} catch (Exception $e) {
throw $e;
}
$this->_afterDelete($object);
return $this;
}
/**
* After Load Entity process
*
* @param Varien_Object $object
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _afterLoad(Varien_Object $object)
{
$this->walkAttributes('backend/afterLoad', array($object));
return $this;
}
/**
* Before delete Entity process
*
* @param Varien_Object $object
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _beforeSave(Varien_Object $object)
{
$this->walkAttributes('backend/beforeSave', array($object));
return $this;
}
/**
* After Save Entity process
*
* @param Varien_Object $object
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _afterSave(Varien_Object $object)
{
$this->walkAttributes('backend/afterSave', array($object));
return $this;
}
/**
* Before Delete Entity process
*
* @param Varien_Object $object
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _beforeDelete(Varien_Object $object)
{
$this->walkAttributes('backend/beforeDelete', array($object));
return $this;
}
/**
* After delete entity process
*
* @param Varien_Object $object
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _afterDelete(Varien_Object $object)
{
$this->walkAttributes('backend/afterDelete', array($object));
return $this;
}
/**
* Retrieve Default attribute model
*
* @return string
*/
protected function _getDefaultAttributeModel()
{
return Mage_Eav_Model_Entity::DEFAULT_ATTRIBUTE_MODEL;
}
/**
* Retrieve default entity attributes
*
* @return array
*/
protected function _getDefaultAttributes()
{
return array('entity_type_id', 'attribute_set_id', 'created_at', 'updated_at', 'parent_id', 'increment_id');
}
/**
* Retrieve default entity static attributes
*
* @return array
*/
public function getDefaultAttributes() {
return array_unique(array_merge($this->_getDefaultAttributes(), array($this->getEntityIdField())));
}
/**
* After set config process
*
* @deprecated
* @return Mage_Eav_Model_Entity_Abstract
*/
protected function _afterSetConfig()
{
return $this;
}
/**
* Check is attribute value empty
*
* @param Mage_Eav_Model_Entity_Attribute_Abstract $attribute
* @param mixed $value
* @return bool
*/
protected function _isAttributeValueEmpty(Mage_Eav_Model_Entity_Attribute_Abstract $attribute, $value)
{
return $attribute->isValueEmpty($value);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment