Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
AbstractEntity.php
Go to the documentation of this file.
1 <?php
8 
24 
35 abstract class AbstractEntity extends AbstractResource implements EntityInterface, DefaultAttributesProvider
36 {
41  protected $attributeLoader;
42 
48  protected $connectionName;
49 
55  protected $_type;
56 
62  protected $_attributesByCode = [];
63 
69  private $attributesByScope = [];
70 
76  protected $_attributesByTable = [];
77 
83  protected $_staticAttributes = [];
84 
90  protected $_entityTable;
91 
97  protected $_describeTable = [];
98 
104  protected $_entityIdField;
105 
112  protected $linkIdField;
113 
120 
127 
134 
140  protected $_isPartialLoad = false;
141 
147  protected $_isPartialSave = false;
148 
154  protected $_sortingSetId = null;
155 
162 
168  protected $_attributeValuesToSave = [];
169 
176  protected static $_attributeBackendTables = [];
177 
181  protected $_resource;
182 
186  protected $_eavConfig;
187 
191  protected $_attrSetEntity;
192 
196  protected $_localeFormat;
197 
201  protected $_resourceHelper;
202 
207 
212 
217 
222  public function __construct(Context $context, $data = [])
223  {
224  $this->_eavConfig = $context->getEavConfig();
225  $this->_resource = $context->getResource();
226  $this->_attrSetEntity = $context->getAttributeSetEntity();
227  $this->_localeFormat = $context->getLocaleFormat();
228  $this->_resourceHelper = $context->getResourceHelper();
229  $this->_universalFactory = $context->getUniversalFactory();
230  $this->transactionManager = $context->getTransactionManager();
231  $this->objectRelationProcessor = $context->getObjectRelationProcessor();
232  parent::__construct();
233  $properties = get_object_vars($this);
234  foreach ($data as $key => $value) {
235  if (array_key_exists('_' . $key, $properties)) {
236  $this->{'_' . $key} = $value;
237  }
238  }
239  }
240 
248  public function setConnection($connection)
249  {
250  $this->connectionName = $connection;
251  return $this;
252  }
253 
259  protected function _construct()
260  {
261  }
262 
269  public function getConnection()
270  {
271  return $this->_resource->getConnection();
272  }
273 
280  public function getIdFieldName()
281  {
282  return $this->getEntityIdField();
283  }
284 
292  public function getTable($alias)
293  {
294  return $this->_resource->getTableName($alias);
295  }
296 
305  public function setType($type)
306  {
307  $this->_type = $this->_eavConfig->getEntityType($type);
308  return $this;
309  }
310 
317  public function getEntityType()
318  {
319  if (empty($this->_type)) {
320  throw new LocalizedException(__('Entity is not initialized'));
321  }
322  return $this->_type;
323  }
324 
330  public function getType()
331  {
332  return $this->getEntityType()->getEntityTypeCode();
333  }
334 
340  public function getTypeId()
341  {
342  return (int) $this->getEntityType()->getEntityTypeId();
343  }
344 
355  public function unsetAttributes($attributes = null)
356  {
357  if ($attributes === null) {
358  $this->_attributesByCode = [];
359  $this->_attributesByTable = [];
360  return $this;
361  }
362 
363  if (is_string($attributes)) {
365  }
366 
367  if (!is_array($attributes)) {
368  throw new LocalizedException(__('This parameter is unknown. Verify and try again.'));
369  }
370 
371  foreach ($attributes as $attrCode) {
372  if (!isset($this->_attributesByCode[$attrCode])) {
373  continue;
374  }
375 
376  $attr = $this->getAttribute($attrCode);
377  unset($this->_attributesByTable[$attr->getBackend()->getTable()][$attrCode]);
378  unset($this->_attributesByCode[$attrCode]);
379  }
380 
381  return $this;
382  }
383 
389  protected function _getConfig()
390  {
391  return $this->_eavConfig;
392  }
393 
405  public function getAttribute($attribute)
406  {
408  $config = $this->_getConfig();
409  if (is_numeric($attribute)) {
410  $attributeId = $attribute;
411  $attributeInstance = $config->getAttribute($this->getEntityType(), $attributeId);
412  if ($attributeInstance) {
413  $attributeCode = $attributeInstance->getAttributeCode();
414  }
415  } elseif (is_string($attribute)) {
417  $attributeInstance = $config->getAttribute($this->getEntityType(), $attributeCode);
418  if (!$attributeInstance->getAttributeCode() && in_array($attribute, $this->getDefaultAttributes())) {
419  $attributeInstance->setAttributeCode(
420  $attribute
421  )->setBackendType(
423  )->setIsGlobal(
424  1
425  )->setEntity(
426  $this
427  )->setEntityType(
428  $this->getEntityType()
429  )->setEntityTypeId(
430  $this->getEntityType()->getId()
431  );
432  }
433  } elseif ($attribute instanceof AbstractAttribute) {
434  $attributeInstance = $attribute;
435  $attributeCode = $attributeInstance->getAttributeCode();
436  }
437 
438  if (empty($attributeInstance)
439  || !$attributeInstance instanceof AbstractAttribute
440  || !$attributeInstance->getId()
441  && !in_array($attributeInstance->getAttributeCode(), $this->getDefaultAttributes())
442  ) {
443  return false;
444  }
445 
446  $attribute = $attributeInstance;
447 
448  if (!$attribute->getAttributeCode()) {
449  $attribute->setAttributeCode($attributeCode);
450  }
451  if (!$attribute->getAttributeModel()) {
452  $attribute->setAttributeModel($this->_getDefaultAttributeModel());
453  }
454 
455  $this->addAttribute($attribute);
456 
457  return $attribute;
458  }
459 
467  public function addAttribute(AbstractAttribute $attribute, $object = null)
468  {
469  $attribute->setEntity($this);
470  $attributeCode = $attribute->getAttributeCode();
471 
472  $this->_attributesByCode[$attributeCode] = $attribute;
473 
474  if ($object !== null) {
475  $suffix = $this->getAttributesCacheSuffix($object);
476  $this->attributesByScope[$suffix][$attributeCode] = $attribute;
477  }
478 
479  if ($attribute->isStatic()) {
480  $this->_staticAttributes[$attributeCode] = $attribute;
481  } else {
482  $this->_attributesByTable[$attribute->getBackendTable()][$attributeCode] = $attribute;
483  }
484 
485  return $this;
486  }
487 
493  private function getAttributesByScope($suffix)
494  {
495  return (isset($this->attributesByScope[$suffix]) && !empty($this->attributesByScope[$suffix]))
496  ? $this->attributesByScope[$suffix]
497  : $this->getAttributesByCode();
498  }
499 
506  private function getAttributesCacheSuffix(DataObject $object)
507  {
508  $attributeSetId = $object->getAttributeSetId() ?: 0;
509  $storeId = $object->getStoreId() ?: 0;
510  return $storeId . '-' . $attributeSetId;
511  }
512 
519  public function isPartialLoad($flag = null)
520  {
522  if ($flag !== null) {
523  $this->_isPartialLoad = (bool)$flag;
524  }
525  return $result;
526  }
527 
534  public function isPartialSave($flag = null)
535  {
537  if ($flag !== null) {
538  $this->_isPartialSave = (bool) $flag;
539  }
540  return $result;
541  }
542 
549  public function loadAllAttributes($object = null)
550  {
551  return $this->getAttributeLoader()->loadAllAttributes($this, $object);
552  }
553 
560  public function getSortedAttributes($setId = null)
561  {
562  $attributes = $this->getAttributesByCode();
563  if ($setId === null) {
564  $setId = $this->getEntityType()->getDefaultAttributeSetId();
565  }
566 
567  // initialize set info
568  $this->_attrSetEntity->addSetInfo($this->getEntityType(), $attributes, $setId);
569 
570  foreach ($attributes as $code => $attribute) {
571  /* @var $attribute AbstractAttribute */
572  if (!$attribute->isInSet($setId)) {
573  unset($attributes[$code]);
574  }
575  }
576 
577  $this->_sortingSetId = $setId;
578  uasort($attributes, [$this, 'attributesCompare']);
579  return $attributes;
580  }
581 
589  public function attributesCompare($firstAttribute, $secondAttribute)
590  {
591  $firstSort = $firstAttribute->getSortWeight((int) $this->_sortingSetId);
592  $secondSort = $secondAttribute->getSortWeight((int) $this->_sortingSetId);
593 
594  return $firstSort <=> $secondSort;
595  }
596 
605  protected function _isApplicableAttribute($object, $attribute)
606  {
607  return true;
608  }
609 
627  public function walkAttributes($partMethod, array $args = [], $collectExceptionMessages = null)
628  {
629  $methodArr = explode('/', $partMethod);
630  switch (sizeof($methodArr)) {
631  case 1:
632  $part = 'attribute';
633  $method = $methodArr[0];
634  break;
635 
636  case 2:
637  $part = $methodArr[0];
638  $method = $methodArr[1];
639  break;
640 
641  default:
642  break;
643  }
644  $results = [];
645  $suffix = $this->getAttributesCacheSuffix($args[0]);
646  foreach ($this->getAttributesByScope($suffix) as $attrCode => $attribute) {
647  if (isset($args[0]) && is_object($args[0]) && !$this->_isApplicableAttribute($args[0], $attribute)) {
648  continue;
649  }
650 
651  switch ($part) {
652  case 'attribute':
653  $instance = $attribute;
654  break;
655 
656  case 'backend':
657  $instance = $attribute->getBackend();
658  break;
659 
660  case 'frontend':
661  $instance = $attribute->getFrontend();
662  break;
663 
664  case 'source':
665  $instance = $attribute->getSource();
666  break;
667 
668  default:
669  break;
670  }
671 
672  if (!$this->_isCallableAttributeInstance($instance, $method, $args)) {
673  continue;
674  }
675 
676  try {
677  $results[$attrCode] = call_user_func_array([$instance, $method], $args);
678  } catch (\Magento\Eav\Model\Entity\Attribute\Exception $e) {
679  if ($collectExceptionMessages) {
680  $results[$attrCode] = $e->getMessage();
681  } else {
682  throw $e;
683  }
684  } catch (\Exception $e) {
685  if ($collectExceptionMessages) {
686  $results[$attrCode] = $e->getMessage();
687  } else {
689  $e = $this->_universalFactory->create(
690  \Magento\Eav\Model\Entity\Attribute\Exception::class,
691  ['phrase' => __($e->getMessage())]
692  );
693  $e->setAttributeCode($attrCode)->setPart($part);
694  throw $e;
695  }
696  }
697  }
698 
699  return $results;
700  }
701 
711  protected function _isCallableAttributeInstance($instance, $method, $args)
712  {
713  if (!is_object($instance) || !method_exists($instance, $method) || !is_callable([$instance, $method])) {
714  return false;
715  }
716 
717  return true;
718  }
719 
725  public function getAttributesByCode()
726  {
728  }
729 
735  public function getAttributesByTable()
736  {
738  }
739 
745  public function getEntityTable()
746  {
747  if (!$this->_entityTable) {
748  $table = $this->getEntityType()->getEntityTable();
749  if (!$table) {
751  }
752  $this->_entityTable = $this->_resource->getTableName($table);
753  }
754 
755  return $this->_entityTable;
756  }
757 
764  public function getLinkField()
765  {
766  if (!$this->linkIdField) {
767  $indexList = $this->getConnection()->getIndexList($this->getEntityTable());
768  $pkName = $this->getConnection()->getPrimaryKeyName($this->getEntityTable());
769  $this->linkIdField = $indexList[$pkName]['COLUMNS_LIST'][0];
770  if (!$this->linkIdField) {
771  $this->linkIdField = $this->getEntityIdField();
772  }
773  }
774 
775  return $this->linkIdField;
776  }
777 
783  public function getEntityIdField()
784  {
785  if (!$this->_entityIdField) {
786  $this->_entityIdField = $this->getEntityType()->getEntityIdField();
787  if (!$this->_entityIdField) {
789  }
790  }
791 
792  return $this->_entityIdField;
793  }
794 
800  public function getValueEntityIdField()
801  {
802  return $this->getLinkField();
803  }
804 
810  public function getValueTablePrefix()
811  {
812  if (!$this->_valueTablePrefix) {
813  $prefix = (string) $this->getEntityType()->getValueTablePrefix();
814  if (!empty($prefix)) {
815  $this->_valueTablePrefix = $prefix;
819  //$this->_resource->getTableName($prefix);
820  } else {
821  $this->_valueTablePrefix = $this->getEntityTable();
822  }
823  }
824 
826  }
827 
833  public function getEntityTablePrefix()
834  {
835  if (empty($this->_entityTablePrefix)) {
836  $prefix = $this->getEntityType()->getEntityTablePrefix();
837  if (empty($prefix)) {
838  $prefix = $this->getEntityType()->getEntityTable();
839  if (empty($prefix)) {
841  }
842  }
843  $this->_entityTablePrefix = $prefix;
844  }
845 
847  }
848 
856  public function isAttributeStatic($attribute)
857  {
858  $attrInstance = $this->getAttribute($attribute);
859  return $attrInstance && $attrInstance->getBackend()->isStatic();
860  }
861 
869  public function validate($object)
870  {
871  $this->loadAllAttributes($object);
872  $result = $this->walkAttributes('backend/validate', [$object], $object->getCollectExceptionMessages());
873  $errors = [];
874  foreach ($result as $attributeCode => $error) {
875  if ($error === false) {
876  $errors[$attributeCode] = true;
877  } elseif (is_string($error)) {
878  $errors[$attributeCode] = $error;
879  }
880  }
881  if (!$errors) {
882  return true;
883  }
884 
885  return $errors;
886  }
887 
894  public function setNewIncrementId(DataObject $object)
895  {
896  if ($object->getIncrementId()) {
897  return $this;
898  }
899 
900  $incrementId = $this->getEntityType()->fetchNewIncrementId($object->getStoreId());
901 
902  if ($incrementId !== false) {
903  $object->setIncrementId($incrementId);
904  }
905 
906  return $this;
907  }
908 
917  {
918  $connection = $this->getConnection();
919  $select = $connection->select();
920 
921  $entityIdField = $this->getEntityIdField();
922  $attributeBackend = $attribute->getBackend();
923  if ($attributeBackend->getType() === 'static') {
924  $value = $object->getData($attribute->getAttributeCode());
925  $bind = ['value' => trim($value)];
926 
927  $select->from(
928  $this->getEntityTable(),
929  $entityIdField
930  )->where(
931  $attribute->getAttributeCode() . ' = :value'
932  );
933  } else {
934  $value = $object->getData($attribute->getAttributeCode());
935  if ($attributeBackend->getType() == 'datetime') {
936  $value = (new \DateTime($value))->format('Y-m-d H:i:s');
937  }
938  $bind = [
939  'attribute_id' => $attribute->getId(),
940  'value' => trim($value),
941  ];
942 
943  $entityIdField = $object->getResource()->getLinkField();
944  $select->from(
945  $attributeBackend->getTable(),
946  $entityIdField
947  )->where(
948  'attribute_id = :attribute_id'
949  )->where(
950  'value = :value'
951  );
952  }
953 
954  if ($this->getEntityTable() == \Magento\Eav\Model\Entity::DEFAULT_ENTITY_TABLE) {
955  $bind['entity_type_id'] = $this->getTypeId();
956  $select->where('entity_type_id = :entity_type_id');
957  }
958 
959  $data = $connection->fetchCol($select, $bind);
960 
961  $objectId = $object->getData($entityIdField);
962  if ($objectId) {
963  if (isset($data[0])) {
964  return $data[0] == $objectId;
965  }
966  return true;
967  }
968 
969  return !count($data);
970  }
971 
978  {
979  return \Magento\Eav\Model\Entity::DEFAULT_SOURCE_MODEL;
980  }
981 
990  public function load($object, $entityId, $attributes = [])
991  {
992  \Magento\Framework\Profiler::start('EAV:load_entity');
996  $object->beforeLoad($entityId);
997  $select = $this->_getLoadRowSelect($object, $entityId);
998  $row = $this->getConnection()->fetchRow($select);
999 
1000  if (is_array($row)) {
1001  $object->addData($row);
1002  $this->loadAttributesForObject($attributes, $object);
1003 
1004  $this->_loadModelAttributes($object);
1005  $this->_afterLoad($object);
1006  $object->afterLoad();
1007  $object->setOrigData();
1008  $object->setHasDataChanges(false);
1009  } else {
1010  $object->isObjectNew(true);
1011  }
1012 
1013  \Magento\Framework\Profiler::stop('EAV:load_entity');
1014  return $this;
1015  }
1016 
1026  {
1028  }
1029 
1036  protected function _loadModelAttributes($object)
1037  {
1038  if (!$object->getId()) {
1039  return $this;
1040  }
1041 
1042  \Magento\Framework\Profiler::start('load_model_attributes');
1043 
1044  $selects = [];
1045  foreach (array_keys($this->getAttributesByTable()) as $table) {
1046  $attribute = current($this->_attributesByTable[$table]);
1047  $eavType = $attribute->getBackendType();
1048  $select = $this->_getLoadAttributesSelect($object, $table);
1049  $selects[$eavType][] = $select->columns('*');
1050  }
1051  $selectGroups = $this->_resourceHelper->getLoadAttributesSelectGroups($selects);
1052  foreach ($selectGroups as $selects) {
1053  if (!empty($selects)) {
1054  if (is_array($selects)) {
1055  $select = $this->_prepareLoadSelect($selects);
1056  } else {
1057  $select = $selects;
1058  }
1059  $values = $this->getConnection()->fetchAll($select);
1060  foreach ($values as $valueRow) {
1061  $this->_setAttributeValue($object, $valueRow);
1062  }
1063  }
1064  }
1065 
1066  \Magento\Framework\Profiler::stop('load_model_attributes');
1067 
1068  return $this;
1069  }
1070 
1077  protected function _prepareLoadSelect(array $selects)
1078  {
1079  return $this->getConnection()->select()->union($selects, \Magento\Framework\DB\Select::SQL_UNION_ALL);
1080  }
1081 
1090  protected function _getLoadRowSelect($object, $rowId)
1091  {
1092  $select = $this->getConnection()->select()->from(
1093  $this->getEntityTable()
1094  )->where(
1095  $this->getEntityIdField() . ' =?',
1096  $rowId
1097  );
1098 
1099  return $select;
1100  }
1101 
1109  protected function _getLoadAttributesSelect($object, $table)
1110  {
1111  $select = $this->getConnection()->select()->from(
1112  $table,
1113  []
1114  )->where(
1115  $this->getEntityIdField() . ' =?',
1116  $object->getId()
1117  );
1118 
1119  return $select;
1120  }
1121 
1129  protected function _setAttributeValue($object, $valueRow)
1130  {
1131  $attribute = $this->getAttribute($valueRow['attribute_id']);
1132  if ($attribute) {
1133  $attributeCode = $attribute->getAttributeCode();
1134  $object->setData($attributeCode, $valueRow['value']);
1135  $attribute->getBackend()->setEntityValueId($object, $valueRow['value_id']);
1136  }
1137 
1138  return $this;
1139  }
1140 
1149  public function save(\Magento\Framework\Model\AbstractModel $object)
1150  {
1154  if ($object->isDeleted()) {
1155  return $this->delete($object);
1156  }
1157  if (!$object->hasDataChanges()) {
1158  return $this;
1159  }
1160  $this->beginTransaction();
1161  try {
1162  $object->validateBeforeSave();
1163  $object->beforeSave();
1164  if ($object->isSaveAllowed()) {
1165  if (!$this->isPartialSave()) {
1166  $this->loadAllAttributes($object);
1167  }
1168 
1169  if ($this->getEntityTable() == \Magento\Eav\Model\Entity::DEFAULT_ENTITY_TABLE
1170  && !$object->getEntityTypeId()
1171  ) {
1172  $object->setEntityTypeId($this->getTypeId());
1173  }
1174 
1175  $object->setParentId((int)$object->getParentId());
1176 
1177  $this->objectRelationProcessor->validateDataIntegrity($this->getEntityTable(), $object->getData());
1178 
1179  $this->_beforeSave($object);
1180  $this->processSave($object);
1181  $this->_afterSave($object);
1182 
1183  $object->afterSave();
1184  }
1185  $this->addCommitCallback([$object, 'afterCommitCallback'])->commit();
1186  $object->setHasDataChanges(false);
1187  } catch (DuplicateException $e) {
1188  $this->rollBack();
1189  $object->setHasDataChanges(true);
1190  throw new AlreadyExistsException(__('Unique constraint violation found'), $e);
1191  } catch (\Exception $e) {
1192  $this->rollBack();
1193  $object->setHasDataChanges(true);
1194  throw $e;
1195  }
1196 
1197  return $this;
1198  }
1199 
1207  protected function processSave($object)
1208  {
1209  $this->_processSaveData($this->_collectSaveData($object));
1210  }
1211 
1218  protected function _getOrigObject($object)
1219  {
1220  $className = get_class($object);
1221  $origObject = $this->_universalFactory->create($className);
1222  $origObject->setData([]);
1223  $this->load($origObject, $object->getData($this->getEntityIdField()));
1224 
1225  return $origObject;
1226  }
1227 
1236  private function _aggregateDeleteData(&$delete, $attribute, $object)
1237  {
1238  foreach ($attribute->getBackend()->getAffectedFields($object) as $tableName => $valuesData) {
1239  if (!isset($delete[$tableName])) {
1240  $delete[$tableName] = [];
1241  }
1242  $delete[$tableName] = array_merge((array)$delete[$tableName], $valuesData);
1243  }
1244  }
1245 
1259  protected function _collectSaveData($newObject)
1260  {
1261  $newData = $newObject->getData();
1262  $entityId = $newObject->getData($this->getEntityIdField());
1263 
1264  // define result data
1265  $entityRow = [];
1266  $insert = [];
1267  $update = [];
1268  $delete = [];
1269 
1270  if (!empty($entityId)) {
1271  $origData = $newObject->getOrigData();
1275  if (empty($origData)) {
1276  $origData = $this->_getOrigObject($newObject)->getOrigData();
1277  }
1278 
1279  if ($origData === null) {
1280  $origData = [];
1281  }
1282 
1287  foreach ($origData as $k => $v) {
1288  if (!array_key_exists($k, $newData)) {
1289  unset($origData[$k]);
1290  }
1291  }
1292  } else {
1293  $origData = [];
1294  }
1295 
1296  $staticFields = $this->getConnection()->describeTable($this->getEntityTable());
1297  $staticFields = array_keys($staticFields);
1298  $attributeCodes = array_keys($this->_attributesByCode);
1299 
1300  foreach ($newData as $k => $v) {
1304  if (!in_array($k, $staticFields) && !in_array($k, $attributeCodes)) {
1305  continue;
1306  }
1307 
1308  $attribute = $this->getAttribute($k);
1309  if (empty($attribute)) {
1310  continue;
1311  }
1312 
1313  if (!$attribute->isInSet($newObject->getAttributeSetId()) && !in_array($k, $staticFields)) {
1314  $this->_aggregateDeleteData($delete, $attribute, $newObject);
1315  continue;
1316  }
1317 
1318  $attrId = $attribute->getAttributeId();
1319 
1323  if (!$attribute->getBackend()->isScalar()) {
1324  continue;
1325  }
1326 
1330  if ($this->isAttributeStatic($k)) {
1331  $entityRow[$k] = $this->_prepareStaticValue($k, $v);
1332  continue;
1333  }
1334 
1338  if ($this->_canUpdateAttribute($attribute, $v, $origData)) {
1339  if ($this->_isAttributeValueEmpty($attribute, $v)) {
1340  $this->_aggregateDeleteData($delete, $attribute, $newObject);
1341  } elseif (!is_numeric($v) && $v !== $origData[$k] || is_numeric($v) && $v != $origData[$k]) {
1342  $update[$attrId] = [
1343  'value_id' => $attribute->getBackend()->getEntityValueId($newObject),
1344  'value' => is_array($v) ? array_shift($v) : $v,//@TODO: MAGETWO-44182,
1345  ];
1346  }
1347  } elseif (!$this->_isAttributeValueEmpty($attribute, $v)) {
1348  $insert[$attrId] = is_array($v) ? array_shift($v) : $v;//@TODO: MAGETWO-44182
1349  }
1350  }
1351 
1352  $result = compact('newObject', 'entityRow', 'insert', 'update', 'delete');
1353  return $result;
1354  }
1355 
1365  protected function _canUpdateAttribute(AbstractAttribute $attribute, $v, array &$origData)
1366  {
1367  return array_key_exists($attribute->getAttributeCode(), $origData);
1368  }
1369 
1376  protected function _getStaticFieldProperties($field)
1377  {
1378  if (empty($this->_describeTable[$this->getEntityTable()])) {
1379  $this->_describeTable[$this->getEntityTable()] = $this->getConnection()->describeTable(
1380  $this->getEntityTable()
1381  );
1382  }
1383 
1384  if (isset($this->_describeTable[$this->getEntityTable()][$field])) {
1385  return $this->_describeTable[$this->getEntityTable()][$field];
1386  }
1387 
1388  return false;
1389  }
1390 
1398  protected function _prepareStaticValue($key, $value)
1399  {
1400  $fieldProp = $this->_getStaticFieldProperties($key);
1401 
1402  if (!$fieldProp) {
1403  return $value;
1404  }
1405 
1406  if ($fieldProp['DATA_TYPE'] == 'decimal') {
1407  $value = $this->_localeFormat->getNumber($value);
1408  }
1409 
1410  return $value;
1411  }
1412 
1421  protected function _processSaveData($saveData)
1422  {
1423  extract($saveData, EXTR_SKIP);
1435  $connection = $this->getConnection();
1436  $insertEntity = true;
1437  $entityTable = $this->getEntityTable();
1438  $entityIdField = $this->getEntityIdField();
1439  $entityId = $newObject->getId();
1440 
1441  unset($entityRow[$entityIdField]);
1442  if (!empty($entityId) && is_numeric($entityId)) {
1443  $bind = ['entity_id' => $entityId];
1444  $select = $connection->select()->from($entityTable, $entityIdField)->where("{$entityIdField} = :entity_id");
1445  $result = $connection->fetchOne($select, $bind);
1446  if ($result) {
1447  $insertEntity = false;
1448  }
1449  } else {
1450  $entityId = null;
1451  }
1452 
1456  $entityObject = new DataObject($entityRow);
1457  $entityRow = $this->_prepareDataForTable($entityObject, $entityTable);
1458  if ($insertEntity) {
1459  if (!empty($entityId)) {
1460  $entityRow[$entityIdField] = $entityId;
1461  $connection->insertForce($entityTable, $entityRow);
1462  } else {
1463  $connection->insert($entityTable, $entityRow);
1464  $entityId = $connection->lastInsertId($entityTable);
1465  }
1466  $newObject->setId($entityId);
1467  } else {
1468  $where = sprintf('%s=%d', $connection->quoteIdentifier($entityIdField), $entityId);
1469  $connection->update($entityTable, $entityRow, $where);
1470  }
1471 
1475  if (!empty($insert)) {
1476  foreach ($insert as $attributeId => $value) {
1477  $attribute = $this->getAttribute($attributeId);
1478  $this->_insertAttribute($newObject, $attribute, $value);
1479  }
1480  }
1481 
1485  if (!empty($update)) {
1486  foreach ($update as $attributeId => $v) {
1487  $attribute = $this->getAttribute($attributeId);
1488  $this->_updateAttribute($newObject, $attribute, $v['value_id'], $v['value']);
1489  }
1490  }
1491 
1495  if (!empty($delete)) {
1496  foreach ($delete as $table => $values) {
1497  $this->_deleteAttributes($newObject, $table, $values);
1498  }
1499  }
1500 
1501  $this->_processAttributeValues();
1502 
1503  $newObject->isObjectNew(false);
1504 
1505  return $this;
1506  }
1507 
1516  protected function _insertAttribute($object, $attribute, $value)
1517  {
1518  return $this->_saveAttribute($object, $attribute, $value);
1519  }
1520 
1531  protected function _updateAttribute($object, $attribute, $valueId, $value)
1532  {
1533  return $this->_saveAttribute($object, $attribute, $value);
1534  }
1535 
1546  protected function _saveAttribute($object, $attribute, $value)
1547  {
1548  $table = $attribute->getBackend()->getTable();
1549  if (!isset($this->_attributeValuesToSave[$table])) {
1550  $this->_attributeValuesToSave[$table] = [];
1551  }
1552 
1553  $entityIdField = $attribute->getBackend()->getEntityIdField();
1554 
1555  $data = [
1556  $entityIdField => $object->getId(),
1557  'attribute_id' => $attribute->getId(),
1558  'value' => $this->_prepareValueForSave($value, $attribute),
1559  ];
1560 
1561  if (!$this->getEntityTable() || $this->getEntityTable() == \Magento\Eav\Model\Entity::DEFAULT_ENTITY_TABLE) {
1562  $data['entity_type_id'] = $object->getEntityTypeId();
1563  }
1564 
1565  $this->_attributeValuesToSave[$table][] = $data;
1566 
1567  return $this;
1568  }
1569 
1575  protected function _processAttributeValues()
1576  {
1577  $connection = $this->getConnection();
1578  foreach ($this->_attributeValuesToSave as $table => $data) {
1579  $connection->insertOnDuplicate($table, $data, ['value']);
1580  }
1581 
1582  foreach ($this->_attributeValuesToDelete as $table => $valueIds) {
1583  $connection->delete($table, ['value_id IN (?)' => $valueIds]);
1584  }
1585 
1586  // reset data arrays
1587  $this->_attributeValuesToSave = [];
1588  $this->_attributeValuesToDelete = [];
1589 
1590  return $this;
1591  }
1592 
1601  {
1602  $type = $attribute->getBackendType();
1603  if (($type == 'int' || $type == 'decimal' || $type == 'datetime') && $value === '') {
1604  $value = null;
1605  } elseif ($type == 'decimal') {
1606  $value = $this->_localeFormat->getNumber($value);
1607  }
1608  $backendTable = $attribute->getBackendTable();
1609  if (!isset(self::$_attributeBackendTables[$backendTable])) {
1610  self::$_attributeBackendTables[$backendTable] = $this->getConnection()->describeTable($backendTable);
1611  }
1612  $describe = self::$_attributeBackendTables[$backendTable];
1613  return $this->getConnection()->prepareColumnValue($describe['value'], $value);
1614  }
1615 
1625  protected function _deleteAttributes($object, $table, $info)
1626  {
1627  $valueIds = [];
1628  foreach ($info as $itemData) {
1629  $valueIds[] = $itemData['value_id'];
1630  }
1631 
1632  if (empty($valueIds)) {
1633  return $this;
1634  }
1635 
1636  if (isset($this->_attributeValuesToDelete[$table])) {
1637  $this->_attributeValuesToDelete[$table] = array_merge($this->_attributeValuesToDelete[$table], $valueIds);
1638  } else {
1639  $this->_attributeValuesToDelete[$table] = $valueIds;
1640  }
1641 
1642  return $this;
1643  }
1644 
1654  public function saveAttribute(DataObject $object, $attributeCode)
1655  {
1656  $attribute = $this->getAttribute($attributeCode);
1657  $backend = $attribute->getBackend();
1658  $table = $backend->getTable();
1659  $entity = $attribute->getEntity();
1660  $connection = $this->getConnection();
1661  $row = $this->getAttributeRow($entity, $object, $attribute);
1662 
1663  $newValue = $object->getData($attributeCode);
1664  if ($attribute->isValueEmpty($newValue)) {
1665  $newValue = null;
1666  }
1667 
1668  $whereArr = [];
1669  foreach ($row as $field => $value) {
1670  $whereArr[] = $connection->quoteInto($field . '=?', $value);
1671  }
1672  $where = implode(' AND ', $whereArr);
1673 
1674  $connection->beginTransaction();
1675 
1676  try {
1677  $select = $connection->select()->from($table, 'value_id')->where($where);
1678  $origValueId = $connection->fetchOne($select);
1679 
1680  if ($origValueId === false && $newValue !== null) {
1681  $this->_insertAttribute($object, $attribute, $newValue);
1682  } elseif ($origValueId !== false && $newValue !== null) {
1683  $this->_updateAttribute($object, $attribute, $origValueId, $newValue);
1684  } elseif ($origValueId !== false && $newValue === null) {
1685  $connection->delete($table, $where);
1686  }
1687  $this->_processAttributeValues();
1688  $connection->commit();
1689  } catch (\Exception $e) {
1690  $connection->rollBack();
1691  throw $e;
1692  }
1693 
1694  return $this;
1695  }
1696 
1705  protected function getAttributeRow($entity, $object, $attribute)
1706  {
1707  $data = [
1708  'attribute_id' => $attribute->getId(),
1709  $this->getLinkField() => $object->getData($this->getLinkField()),
1710  ];
1711 
1712  if (!$this->getEntityTable()) {
1713  $data['entity_type_id'] = $entity->getTypeId();
1714  }
1715 
1716  return $data;
1717  }
1718 
1727  public function delete($object)
1728  {
1729  try {
1730  $connection = $this->transactionManager->start($this->getConnection());
1731  if (is_numeric($object)) {
1732  $id = (int) $object;
1733  } elseif ($object instanceof \Magento\Framework\Model\AbstractModel) {
1734  $object->beforeDelete();
1735  $id = (int) $object->getData($this->getLinkField());
1736  }
1737  $this->_beforeDelete($object);
1738  $this->evaluateDelete(
1739  $object,
1740  $id,
1741  $connection
1742  );
1743 
1744  $this->_afterDelete($object);
1745 
1746  if ($object instanceof \Magento\Framework\Model\AbstractModel) {
1747  $object->isDeleted(true);
1748  $object->afterDelete();
1749  }
1750  $this->transactionManager->commit();
1751  if ($object instanceof \Magento\Framework\Model\AbstractModel) {
1752  $object->afterDeleteCommit();
1753  }
1754  } catch (\Exception $e) {
1755  $this->transactionManager->rollBack();
1756  throw $e;
1757  }
1758  return $this;
1759  }
1760 
1772  protected function evaluateDelete($object, $id, $connection)
1773  {
1774  $where = [$this->getEntityIdField() . '=?' => $id];
1775  $this->objectRelationProcessor->delete(
1776  $this->transactionManager,
1777  $connection,
1778  $this->getEntityTable(),
1779  $this->getConnection()->quoteInto(
1780  $this->getEntityIdField() . '=?',
1781  $id
1782  ),
1783  [$this->getEntityIdField() => $id]
1784  );
1785 
1786  $this->loadAllAttributes($object);
1787  foreach ($this->getAttributesByTable() as $table => $attributes) {
1788  $this->getConnection()->delete(
1789  $table,
1790  $where
1791  );
1792  }
1793  }
1794 
1801  protected function _afterLoad(DataObject $object)
1802  {
1803  \Magento\Framework\Profiler::start('after_load');
1804  $this->walkAttributes('backend/afterLoad', [$object]);
1805  \Magento\Framework\Profiler::stop('after_load');
1806  return $this;
1807  }
1808 
1815  protected function _beforeSave(DataObject $object)
1816  {
1817  $this->walkAttributes('backend/beforeSave', [$object]);
1818  return $this;
1819  }
1820 
1827  protected function _afterSave(DataObject $object)
1828  {
1829  $this->walkAttributes('backend/afterSave', [$object]);
1830  return $this;
1831  }
1832 
1839  protected function _beforeDelete(DataObject $object)
1840  {
1841  $this->walkAttributes('backend/beforeDelete', [$object]);
1842  return $this;
1843  }
1844 
1851  protected function _afterDelete(DataObject $object)
1852  {
1853  $this->walkAttributes('backend/afterDelete', [$object]);
1854  return $this;
1855  }
1856 
1862  protected function _getDefaultAttributeModel()
1863  {
1864  return \Magento\Eav\Model\Entity::DEFAULT_ATTRIBUTE_MODEL;
1865  }
1866 
1872  protected function _getDefaultAttributes()
1873  {
1874  return ['entity_type_id', 'attribute_set_id', 'created_at', 'updated_at', 'parent_id', 'increment_id'];
1875  }
1876 
1882  public function getDefaultAttributes()
1883  {
1884  return array_unique(array_merge(
1885  $this->_getDefaultAttributes(),
1886  [$this->getEntityIdField(), $this->getLinkField()]
1887  ));
1888  }
1889 
1897  protected function _isAttributeValueEmpty(AbstractAttribute $attribute, $value)
1898  {
1899  return $attribute->isValueEmpty($value);
1900  }
1901 
1910  protected function getAttributeLoader()
1911  {
1912  if ($this->attributeLoader === null) {
1913  $this->attributeLoader= ObjectManager::getInstance()->get(AttributeLoaderInterface::class);
1914  }
1915  return $this->attributeLoader;
1916  }
1917 
1924  public function afterLoad(DataObject $object)
1925  {
1926  $this->_afterLoad($object);
1927  }
1928 
1935  public function beforeSave(DataObject $object)
1936  {
1937  $this->_beforeSave($object);
1938  }
1939 
1946  public function afterSave(DataObject $object)
1947  {
1948  $this->_afterSave($object);
1949  }
1950 
1957  public function beforeDelete(DataObject $object)
1958  {
1959  $this->_beforeDelete($object);
1960  }
1961 
1968  public function afterDelete(DataObject $object)
1969  {
1970  $this->_afterDelete($object);
1971  }
1972 
1982  protected function loadAttributesForObject($attributes, $object = null)
1983  {
1984  if (empty($attributes)) {
1985  $this->loadAllAttributes($object);
1986  } else {
1987  if (!is_array($attributes)) {
1989  }
1990  foreach ($attributes as $attrCode) {
1991  $this->getAttribute($attrCode);
1992  }
1993  }
1994  }
1995 }
_isCallableAttributeInstance($instance, $method, $args)
$results
Definition: popup.phtml:13
$tableName
Definition: trigger.php:13
$suffix
Definition: name.phtml:27
getData($key='', $index=null)
Definition: DataObject.php:119
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
saveAttribute(DataObject $object, $attributeCode)
$attr
Definition: text.phtml:8
addAttribute(AbstractAttribute $attribute, $object=null)
$config
Definition: fraud_order.php:17
$id
Definition: fieldset.phtml:14
checkAttributeUniqueValue(AbstractAttribute $attribute, $object)
load($object, $entityId, $attributes=[])
$values
Definition: options.phtml:88
_saveAttribute($object, $attribute, $value)
getAttributeRow($entity, $object, $attribute)
__()
Definition: __.php:13
save(\Magento\Framework\Model\AbstractModel $object)
_insertAttribute($object, $attribute, $value)
_updateAttribute($object, $attribute, $valueId, $value)
evaluateDelete($object, $id, $connection)
$type
Definition: item.phtml:13
$prefix
Definition: name.phtml:25
$value
Definition: gender.phtml:16
_prepareValueForSave($value, AbstractAttribute $attribute)
$attributeCode
Definition: extend.phtml:12
const SQL_UNION_ALL
Definition: Select.php:69
const DEFAULT_ENTITY_ID_FIELD
Definition: Entity.php:26
$entity
Definition: element.phtml:22
_isAttributeValueEmpty(AbstractAttribute $attribute, $value)
$attributes
Definition: matrix.phtml:13
__construct(Context $context, $data=[])
$method
Definition: info.phtml:13
attributesCompare($firstAttribute, $secondAttribute)
setData($key, $value=null)
Definition: DataObject.php:72
if(!trim($html)) $alias
Definition: details.phtml:20
$properties
Definition: categories.php:26
foreach( $_productCollection as $_product)() ?>" class $info
Definition: listing.phtml:52
$connection
Definition: bulk.php:13
$entityTable
Definition: tablerates.php:11
$table
Definition: trigger.php:14
loadAttributesForObject($attributes, $object=null)
_canUpdateAttribute(AbstractAttribute $attribute, $v, array &$origData)
$errors
Definition: overview.phtml:9
$code
Definition: info.phtml:12
if($currentSelectedMethod==$_code) $className
Definition: form.phtml:31