![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/old/vendor/magento/module-eav/Model/Entity/Collection/ |
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Eav\Model\Entity\Collection; use Magento\Framework\App\ResourceConnection\SourceProviderInterface; use Magento\Framework\Data\Collection\AbstractDb; use Magento\Framework\DB\Select; use Magento\Framework\Exception\LocalizedException; /** * Entity/Attribute/Model - collection abstract * * phpcs:disable Magento2.Classes.AbstractApi * @api * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ abstract class AbstractCollection extends AbstractDb implements SourceProviderInterface { /** * Define default prefix for attribute table alias */ public const ATTRIBUTE_TABLE_ALIAS_PREFIX = 'at_'; /** * Array of items with item id key * * @var array */ protected $_itemsById = []; /** * Entity static fields * * @var array */ protected $_staticFields = []; /** * Entity object to define collection's attributes * * @var \Magento\Eav\Model\Entity\AbstractEntity */ protected $_entity; /** * Entity types to be fetched for objects in collection * * @var array */ protected $_selectEntityTypes = []; /** * Attributes to be fetched for objects in collection * * @var array */ protected $_selectAttributes = []; /** * Attributes to be filtered order sorted by * * @var array */ protected $_filterAttributes = []; /** * Joined entities * * @var array */ protected $_joinEntities = []; /** * Joined attributes * * @var array */ protected $_joinAttributes = []; /** * Joined fields data * * @var array */ protected $_joinFields = []; /** * Cast map for attribute order * * @var string[] */ protected $_castToIntMap = ['validate-digits']; /** * Core event manager proxy * * @var \Magento\Framework\Event\ManagerInterface */ protected $_eventManager = null; /** * @var \Magento\Eav\Model\Config */ protected $_eavConfig; /** * @var \Magento\Framework\App\ResourceConnection */ protected $_resource; /** * @var \Magento\Eav\Model\EntityFactory */ protected $_eavEntityFactory; /** * @var \Magento\Eav\Model\ResourceModel\Helper */ protected $_resourceHelper; /** * * @var \Magento\Framework\Validator\UniversalFactory */ protected $_universalFactory; /** * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Eav\Model\Config $eavConfig * @param \Magento\Framework\App\ResourceConnection $resource * @param \Magento\Eav\Model\EntityFactory $eavEntityFactory * @param \Magento\Eav\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param mixed $connection * @codeCoverageIgnore * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\Data\Collection\EntityFactory $entityFactory, \Psr\Log\LoggerInterface $logger, \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Eav\Model\Config $eavConfig, \Magento\Framework\App\ResourceConnection $resource, \Magento\Eav\Model\EntityFactory $eavEntityFactory, \Magento\Eav\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Framework\DB\Adapter\AdapterInterface $connection = null ) { $this->_eventManager = $eventManager; $this->_eavConfig = $eavConfig; $this->_resource = $resource; $this->_eavEntityFactory = $eavEntityFactory; $this->_resourceHelper = $resourceHelper; $this->_universalFactory = $universalFactory; parent::__construct($entityFactory, $logger, $fetchStrategy, $connection); $this->_construct(); $this->setConnection($this->getEntity()->getConnection()); $this->_prepareStaticFields(); $this->_initSelect(); } /** * Initialize collection * * @return void * phpcs:disable Magento2.CodeAnalysis.EmptyBlock */ protected function _construct() { } /** * Retrieve table name * * @param string $table * @return string * @codeCoverageIgnore */ public function getTable($table) { return $this->getResource()->getTable($table); } /** * Prepare static entity fields * * @return $this */ protected function _prepareStaticFields() { foreach ($this->getEntity()->getDefaultAttributes() as $field) { $this->_staticFields[$field] = $field; } return $this; } /** * Init select * * @return $this */ protected function _initSelect() { $this->getSelect()->from(['e' => $this->getEntity()->getEntityTable()]); $entity = $this->getEntity(); if ($entity->getTypeId() && $entity->getEntityTable() == \Magento\Eav\Model\Entity::DEFAULT_ENTITY_TABLE) { $this->addAttributeToFilter('entity_type_id', $this->getEntity()->getTypeId()); } return $this; } /** * Standard resource collection initialization * * @param string $model * @param string $entityModel * @return $this */ protected function _init($model, $entityModel) { $this->setItemObjectClass($model); $entity = $this->_universalFactory->create($entityModel); $this->setEntity($entity); return $this; } /** * Set entity to use for attributes * * @param \Magento\Eav\Model\Entity\AbstractEntity $entity * @return $this * @throws LocalizedException */ public function setEntity($entity) { if ($entity instanceof \Magento\Eav\Model\Entity\AbstractEntity) { $this->_entity = $entity; } elseif (is_string($entity) || $entity instanceof \Magento\Framework\App\Config\Element) { $this->_entity = $this->_eavEntityFactory->create()->setType($entity); } else { throw new LocalizedException( __('The entity supplied to collection is invalid. Verify the entity and try again.') ); } return $this; } /** * Get collection's entity object * * @return \Magento\Eav\Model\Entity\AbstractEntity * @throws LocalizedException */ public function getEntity() { if (empty($this->_entity)) { throw new LocalizedException(__('Entity is not initialized')); } return $this->_entity; } /** * Get resource instance * * @return \Magento\Framework\Model\ResourceModel\Db\AbstractDb * @codeCoverageIgnore */ public function getResource() { return $this->getEntity(); } /** * Set template object for the collection * * @param \Magento\Framework\DataObject $object * @return $this */ public function setObject($object = null) { if (is_object($object)) { $this->setItemObjectClass(get_class($object)); } else { $this->setItemObjectClass($object); } return $this; } /** * Add an object to the collection * * @param \Magento\Framework\DataObject $object * @return $this * @throws LocalizedException */ public function addItem(\Magento\Framework\DataObject $object) { if (!$object instanceof $this->_itemObjectClass) { throw new LocalizedException( __("The object wasn't added because it's invalid. To continue, enter a valid object and try again.") ); } return parent::addItem($object); } /** * Retrieve entity attribute * * @param string $attributeCode * @return \Magento\Eav\Model\Entity\Attribute\AbstractAttribute */ public function getAttribute($attributeCode) { if (isset($this->_joinAttributes[$attributeCode])) { return $this->_joinAttributes[$attributeCode]['attribute']; } return $this->getEntity()->getAttribute($attributeCode); } /** * Add attribute filter to collection * * If $attribute is an array will add OR condition with following format: * array( * array('attribute'=>'firstname', 'like'=>'test%'), * array('attribute'=>'lastname', 'like'=>'test%'), * ) * * @param \Magento\Eav\Model\Entity\Attribute\AttributeInterface|integer|string|array $attribute * @param null|string|array $condition * @param string $joinType * @return $this * @throws \Magento\Framework\Exception\LocalizedException * * @see self::_getConditionSql for $condition * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function addAttributeToFilter($attribute, $condition = null, $joinType = 'inner') { if ($attribute === null) { $this->getSelect(); return $this; } if (is_numeric($attribute)) { $attributeModel = $this->getEntity()->getAttribute($attribute); if (!$attributeModel) { throw new \Magento\Framework\Exception\LocalizedException( __('Invalid attribute identifier for filter (%1)', get_class($attribute)) ); } $attribute = $attributeModel->getAttributeCode(); } elseif ($attribute instanceof \Magento\Eav\Model\Entity\Attribute\AttributeInterface) { $attribute = $attribute->getAttributeCode(); } if (is_array($attribute)) { $sqlArr = []; foreach ($attribute as $condition) { $sqlArr[] = $this->_getAttributeConditionSql($condition['attribute'], $condition, $joinType); } $conditionSql = '(' . implode(') OR (', $sqlArr) . ')'; } elseif (is_string($attribute)) { if ($condition === null) { $condition = ''; } $conditionSql = $this->_getAttributeConditionSql($attribute, $condition, $joinType); } if (!empty($conditionSql)) { $this->getSelect()->where($conditionSql, null, \Magento\Framework\DB\Select::TYPE_CONDITION); $this->_totalRecords = null; } else { throw new \Magento\Framework\Exception\LocalizedException( __('Invalid attribute identifier for filter (%1)', get_class($attribute)) ); } return $this; } /** * Wrapper for compatibility with \Magento\Framework\Data\Collection\AbstractDb * * @param mixed $attribute * @param mixed $condition * @return $this|AbstractDb * @codeCoverageIgnore */ public function addFieldToFilter($attribute, $condition = null) { return $this->addAttributeToFilter($attribute, $condition, 'left'); } /** * Add attribute to sort order * * @param string $attribute * @param string $dir * @return $this */ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) { if (isset($this->_joinFields[$attribute])) { $this->getSelect()->order($this->_getAttributeFieldName($attribute) . ' ' . $dir); return $this; } if (isset($this->_staticFields[$attribute])) { $this->getSelect()->order("e.{$attribute} {$dir}"); return $this; } if (isset($this->_joinAttributes[$attribute])) { $attrInstance = $this->_joinAttributes[$attribute]['attribute']; $entityField = $this->_getAttributeTableAlias($attribute) . '.' . $attrInstance->getAttributeCode(); } else { $attrInstance = $this->getEntity()->getAttribute($attribute); $entityField = 'e.' . $attribute; } if ($attrInstance) { if ($attrInstance->getBackend()->isStatic()) { $orderExpr = $entityField; } else { $this->_addAttributeJoin($attribute, 'left'); if (isset($this->_joinAttributes[$attribute]) || isset($this->_joinFields[$attribute])) { $orderExpr = $attribute; } else { $orderExpr = $this->_getAttributeTableAlias($attribute) . '.value'; } } if (in_array($attrInstance->getFrontendClass(), $this->_castToIntMap)) { $orderExpr = new \Zend_Db_Expr("CAST({$this->_prepareOrderExpression($orderExpr)} AS SIGNED)"); } $orderExpr .= ' ' . $dir; $this->getSelect()->order($orderExpr); } return $this; } /** * Retrieve attribute expression by specified column * * @param string $field * @return string|Zend_Db_Expr */ protected function _prepareOrderExpression($field) { foreach ($this->getSelect()->getPart(\Magento\Framework\DB\Select::COLUMNS) as $columnEntry) { if ($columnEntry[2] != $field) { continue; } if ($columnEntry[1] instanceof \Zend_Db_Expr) { return $columnEntry[1]; } } return $field; } /** * Add attribute to entities in collection * * If $attribute == '*' select all attributes * * @param array|string|integer|\Magento\Framework\App\Config\Element $attribute * @param bool|string $joinType flag for joining attribute * @return $this * @throws LocalizedException */ public function addAttributeToSelect($attribute, $joinType = false) { if (is_array($attribute)) { foreach ($attribute as $a) { $this->addAttributeToSelect($a, $joinType); } return $this; } if ($joinType !== false && !$this->getEntity()->getAttribute($attribute)->isStatic()) { $this->_addAttributeJoin($attribute, $joinType); } elseif ('*' === $attribute) { $entity = clone $this->getEntity(); $attributes = $entity->loadAllAttributes()->getAttributesByCode(); foreach ($attributes as $attrCode => $attr) { $this->_selectAttributes[$attrCode] = (int) $attr->getId(); } } else { if (isset($this->_joinAttributes[$attribute])) { $attrInstance = $this->_joinAttributes[$attribute]['attribute']; } else { $attrInstance = $this->_eavConfig->getAttribute($this->getEntity()->getType(), $attribute); } if (empty($attrInstance)) { throw new LocalizedException( __( 'The "%1" attribute requested is invalid. Verify the attribute and try again.', (string)$attribute ) ); } $this->_selectAttributes[$attrInstance->getAttributeCode()] = (int) $attrInstance->getId(); } return $this; } /** * Add entity type to select statement * * @param string $entityType * @param string $prefix * @return $this * @codeCoverageIgnore */ public function addEntityTypeToSelect($entityType, $prefix) { $this->_selectEntityTypes[$entityType] = ['prefix' => $prefix]; return $this; } /** * Add field to static * * @param string $field * @return $this */ public function addStaticField($field) { if (!isset($this->_staticFields[$field])) { $this->_staticFields[$field] = $field; } return $this; } /** * Add attribute expression (SUM, COUNT, etc) * * Example: ('sub_total', 'SUM({{attribute}})', 'revenue') * Example: ('sub_total', 'SUM({{revenue}})', 'revenue') * * For some functions like SUM use groupByAttribute. * * @param string $alias * @param string $expression * @param string $attribute * @return $this * @throws LocalizedException */ public function addExpressionAttributeToSelect($alias, $expression, $attribute) { // validate alias if (isset($this->_joinFields[$alias])) { throw new LocalizedException(__('Joint field or attribute expression with this alias is already declared')); } if (!is_array($attribute)) { $attribute = [$attribute]; } $fullExpression = $expression ?: ''; // Replacing multiple attributes foreach ($attribute as $attributeItem) { if (isset($this->_staticFields[$attributeItem])) { $attrField = sprintf('e.%s', $attributeItem); } else { $attributeInstance = $this->getAttribute($attributeItem); if ($attributeInstance->getBackend()->isStatic()) { $attrField = 'e.' . $attributeItem; } else { $this->_addAttributeJoin($attributeItem, 'left'); $attrField = $this->_getAttributeFieldName($attributeItem); } } $fullExpression = str_replace('{{attribute}}', $attrField, $fullExpression); $fullExpression = str_replace('{{' . $attributeItem . '}}', $attrField, $fullExpression); } $this->getSelect()->columns([$alias => $fullExpression]); $this->_joinFields[$alias] = ['table' => false, 'field' => $fullExpression]; return $this; } /** * Groups results by specified attribute * * @param string|array $attribute * @return $this */ public function groupByAttribute($attribute) { if (is_array($attribute)) { foreach ($attribute as $attributeItem) { $this->groupByAttribute($attributeItem); } } else { if (isset($this->_joinFields[$attribute])) { $this->getSelect()->group($this->_getAttributeFieldName($attribute)); return $this; } if (isset($this->_staticFields[$attribute])) { $this->getSelect()->group(sprintf('e.%s', $attribute)); return $this; } if (isset($this->_joinAttributes[$attribute])) { $attrInstance = $this->_joinAttributes[$attribute]['attribute']; $entityField = $this->_getAttributeTableAlias($attribute) . '.' . $attrInstance->getAttributeCode(); } else { $attrInstance = $this->getEntity()->getAttribute($attribute); $entityField = 'e.' . $attribute; } if ($attrInstance->getBackend()->isStatic()) { $this->getSelect()->group($entityField); } else { $this->_addAttributeJoin($attribute); $this->getSelect()->group($this->_getAttributeTableAlias($attribute) . '.value'); } } return $this; } /** * Add attribute from joined entity to select * * Examples: * ('billing_firstname', 'customer_address/firstname', 'default_billing') * ('billing_lastname', 'customer_address/lastname', 'default_billing') * ('shipping_lastname', 'customer_address/lastname', 'default_billing') * ('shipping_postalcode', 'customer_address/postalcode', 'default_shipping') * ('shipping_city', $cityAttribute, 'default_shipping') * * Developer is encouraged to use existing instances of attributes and entities * After first use of string entity name it will be cached in the collection * * @todo connect between joined attributes of same entity * @param string $alias alias for the joined attribute * @param string|\Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute * @param string $bind attribute of the main entity to link with joined $filter * @param string $filter primary key for the joined entity (entity_id default) * @param string $joinType inner|left * @param int|null $storeId * @return $this * @throws LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function joinAttribute($alias, $attribute, $bind, $filter = null, $joinType = 'inner', $storeId = null) { // validate alias if (isset($this->_joinAttributes[$alias])) { throw new LocalizedException(__('Invalid alias, already exists in joint attributes')); } $bindAttribute = null; // validate bind attribute if (is_string($bind)) { $bindAttribute = $this->getAttribute($bind); } if (!$bindAttribute || !$bindAttribute->isStatic() && !$bindAttribute->getId()) { throw new LocalizedException(__('The foreign key is invalid. Verify the foreign key and try again.')); } $entity = null; $attrArr = []; // try to explode combined entity/attribute if supplied if (is_string($attribute)) { $attrArr = explode('/', $attribute); if (isset($attrArr[1])) { $entity = $attrArr[0]; $attribute = $attrArr[1]; } } // validate entity if (empty($entity) && $attribute instanceof \Magento\Eav\Model\Entity\Attribute\AbstractAttribute) { $entity = $attribute->getEntity(); } elseif (is_string($entity)) { // retrieve cached entity if possible if (isset($this->_joinEntities[$entity])) { $entity = $this->_joinEntities[$entity]; } else { $entity = $this->_eavEntityFactory->create()->setType($attrArr[0]); } } if (!$entity || !$entity->getTypeId()) { throw new LocalizedException(__('The entity type is invalid. Verify the entity type and try again.')); } // cache entity if (!isset($this->_joinEntities[$entity->getType()])) { $this->_joinEntities[$entity->getType()] = $entity; } // validate attribute if (is_string($attribute)) { $attribute = $entity->getAttribute($attribute); } if (!$attribute) { throw new LocalizedException(__('The attribute type is invalid. Verify the attribute type and try again.')); } if (empty($filter)) { $filter = $entity->getLinkField(); } // add joined attribute $this->_joinAttributes[$alias] = [ 'bind' => $bind, 'bindAttribute' => $bindAttribute, 'attribute' => $attribute, 'filter' => $filter, 'store_id' => $storeId, ]; $this->_addAttributeJoin($alias, $joinType); return $this; } /** * Join regular table field and use an attribute as fk * * Examples: * ('country_name', 'directory_country_name', 'name', 'country_id=shipping_country', * "{{table}}.language_code='en'", 'left') * * @param string $alias 'country_name' * @param string $table 'directory_country_name' * @param string $field 'name' * @param string $bind 'PK(country_id)=FK(shipping_country_id)' * @param string|array $cond "{{table}}.language_code='en'" OR array('language_code'=>'en') * @param string $joinType 'left' * @return $this * @throws LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function joinField($alias, $table, $field, $bind, $cond = null, $joinType = 'inner') { // validate alias if (isset($this->_joinFields[$alias])) { throw new LocalizedException(__('A joined field with this alias is already declared.')); } $table = $this->_resource->getTableName($table); $tableAlias = $this->_getAttributeTableAlias($alias); // validate bind list($pKey, $fKey) = explode('=', $bind ?: ''); $pKey = $this->getSelect()->getConnection()->quoteColumnAs(trim($pKey ?: ''), null); $bindCond = $tableAlias . '.' . trim($pKey ?: '') . '=' . $this->_getAttributeFieldName(trim($fKey ?: '')); // process join type switch ($joinType) { case 'left': $joinMethod = 'joinLeft'; break; default: $joinMethod = 'join'; break; } $condArr = [$bindCond]; // add where condition if needed if ($cond !== null) { if (is_array($cond)) { foreach ($cond as $key => $value) { $condArr[] = $this->_getConditionSql($tableAlias . '.' . $key, $value); } } else { $condArr[] = str_replace('{{table}}', $tableAlias, $cond); } } $cond = '(' . implode(') AND (', $condArr) . ')'; // join table $this->getSelect()->{$joinMethod}( [$tableAlias => $table], $cond, $field ? [$alias => $field] : [] ); // save joined attribute $this->_joinFields[$alias] = ['table' => $tableAlias, 'field' => $field]; return $this; } /** * Join a table * * @param string|array $table * @param string $bind * @param string|array $fields * @param null|array $cond * @param string $joinType * @return $this * @throws LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function joinTable($table, $bind, $fields = null, $cond = null, $joinType = 'inner') { $tableAlias = null; if (is_array($table)) { list($tableAlias, $tableName) = [key($table), current($table)]; } else { $tableName = $table; } $tableName = $this->_resource->getTableName($tableName); if (empty($tableAlias)) { $tableAlias = $tableName; } // validate fields and aliases if (!$fields) { throw new LocalizedException(__('Invalid joint fields')); } foreach ($fields as $alias => $field) { if (isset($this->_joinFields[$alias])) { throw new LocalizedException(__('A joint field with a "%1" alias is already declared.', $alias)); } $this->_joinFields[$alias] = ['table' => $tableAlias, 'field' => $field]; } // validate bind list($pKey, $fKey) = explode('=', $bind ?: ''); $bindCond = $tableAlias . '.' . $pKey . '=' . $this->_getAttributeFieldName($fKey); // process join type switch ($joinType) { case 'left': $joinMethod = 'joinLeft'; break; default: $joinMethod = 'join'; } $condArr = [$bindCond]; // add where condition if needed if ($cond !== null) { if (is_array($cond)) { foreach ($cond as $key => $value) { $condArr[] = $this->_getConditionSql($tableAlias . '.' . $key, $value); } } else { $condArr[] = str_replace('{{table}}', $tableAlias ?: '', $cond); } } $cond = '(' . implode(') AND (', $condArr) . ')'; // join table $this->getSelect()->{$joinMethod}([$tableAlias => $tableName], $cond, $fields); return $this; } /** * Remove an attribute from selection list * * @param string $attribute * @return $this */ public function removeAttributeToSelect($attribute = null) { if ($attribute === null) { $this->_selectAttributes = []; } else { unset($this->_selectAttributes[$attribute]); } return $this; } /** * Set collection page start and records to show * * @param integer $pageNum * @param integer $pageSize * @return $this * @codeCoverageIgnore */ public function setPage($pageNum, $pageSize) { $this->setCurPage($pageNum)->setPageSize($pageSize); return $this; } /** * Load collection data into object items * * @param bool $printQuery * @param bool $logQuery * @return $this */ public function load($printQuery = false, $logQuery = false) { if ($this->isLoaded()) { return $this; } \Magento\Framework\Profiler::start('EAV:load_collection'); \Magento\Framework\Profiler::start('before_load'); $this->_eventManager->dispatch('eav_collection_abstract_load_before', ['collection' => $this]); $this->_beforeLoad(); \Magento\Framework\Profiler::stop('before_load'); $this->_renderFilters(); $this->_renderOrders(); \Magento\Framework\Profiler::start('load_entities'); $this->_loadEntities($printQuery, $logQuery); \Magento\Framework\Profiler::stop('load_entities'); \Magento\Framework\Profiler::start('load_attributes'); $this->_loadAttributes($printQuery, $logQuery); \Magento\Framework\Profiler::stop('load_attributes'); \Magento\Framework\Profiler::start('set_orig_data'); foreach ($this->_items as $item) { $item->setOrigData(); $this->beforeAddLoadedItem($item); $item->setDataChanges(false); } \Magento\Framework\Profiler::stop('set_orig_data'); $this->_setIsLoaded(); \Magento\Framework\Profiler::start('after_load'); $this->_afterLoad(); \Magento\Framework\Profiler::stop('after_load'); \Magento\Framework\Profiler::stop('EAV:load_collection'); return $this; } /** * Clone and reset collection * * @param int|null $limit * @param int|null $offset * @return Select */ protected function _getAllIdsSelect($limit = null, $offset = null) { $idsSelect = clone $this->getSelect(); $idsSelect->reset(\Magento\Framework\DB\Select::ORDER); $idsSelect->reset(\Magento\Framework\DB\Select::LIMIT_COUNT); $idsSelect->reset(\Magento\Framework\DB\Select::LIMIT_OFFSET); $idsSelect->reset(\Magento\Framework\DB\Select::COLUMNS); $idsSelect->columns('e.' . $this->getEntity()->getIdFieldName()); $idsSelect->limit($limit, $offset); return $idsSelect; } /** * Retrieve all ids for collection * * @param null|int|string $limit * @param null|int|string $offset * @return array */ public function getAllIds($limit = null, $offset = null) { return $this->getConnection()->fetchCol($this->_getAllIdsSelect($limit, $offset), $this->_bindParams); } /** * Retrieve all ids sql * * @return Select */ public function getAllIdsSql() { $idsSelect = clone $this->getSelect(); $idsSelect->reset(\Magento\Framework\DB\Select::ORDER); $idsSelect->reset(\Magento\Framework\DB\Select::LIMIT_COUNT); $idsSelect->reset(\Magento\Framework\DB\Select::LIMIT_OFFSET); $idsSelect->reset(\Magento\Framework\DB\Select::COLUMNS); $idsSelect->reset(\Magento\Framework\DB\Select::GROUP); $idsSelect->columns('e.' . $this->getEntity()->getIdFieldName()); return $idsSelect; } /** * Save all the entities in the collection * * @todo make batch save directly from collection * * @return $this */ public function save() { foreach ($this->getItems() as $item) { $item->save(); } return $this; } /** * Delete all the entities in the collection * * @todo make batch delete directly from collection * * @return $this */ public function delete() { foreach ($this->getItems() as $key => $item) { $this->getEntity()->delete($item); unset($this->_items[$key]); } return $this; } /** * Import 2D array into collection as objects * * If the imported items already exist, update the data for existing objects * * @param array $arr * @return $this */ public function importFromArray($arr) { $entityIdField = $this->getEntity()->getLinkField(); foreach ($arr as $row) { $entityId = $row[$entityIdField]; if (!isset($this->_items[$entityId])) { $this->_items[$entityId] = $this->getNewEmptyItem(); $this->_items[$entityId]->setData($row); } else { $this->_items[$entityId]->addData($row); } } $this->_setIsLoaded(); return $this; } /** * Get collection data as a 2D array * * @return array */ public function exportToArray() { $result = []; $entityIdField = $this->getEntity()->getLinkField(); foreach ($this->getItems() as $item) { $result[$item->getData($entityIdField)] = $item->getData(); } return $result; } /** * Retrieve row id field name * * @return string * @codeCoverageIgnore */ public function getRowIdFieldName() { return $this->getIdFieldName(); } /** * Id field name getter * * @return string */ public function getIdFieldName() { if ($this->_idFieldName === null) { $this->_setIdFieldName($this->getEntity()->getIdFieldName()); } return $this->_idFieldName; } /** * Set row id field name * * @param string $fieldName * @return $this */ public function setRowIdFieldName($fieldName) { return $this->_setIdFieldName($fieldName); } /** * Load entities records into items * * @param bool $printQuery * @param bool $logQuery * @return $this * @throws \Exception */ public function _loadEntities($printQuery = false, $logQuery = false) { $this->getEntity(); if ($this->_pageSize) { $this->getSelect()->limitPage($this->getCurPage(), $this->_pageSize); } $this->printLogQuery($printQuery, $logQuery); /** * Prepare select query * @var string|\Magento\Framework\DB\Select $query */ $query = $this->getSelect(); try { $rows = $this->_fetchAll($query); } catch (\Exception $e) { $this->printLogQuery(false, true, $query); throw $e; } foreach ($rows as $value) { $object = $this->getNewEmptyItem()->setData($value); $this->addItem($object); if (isset($this->_itemsById[$object->getId()])) { $this->_itemsById[$object->getId()][] = $object; } else { $this->_itemsById[$object->getId()] = [$object]; } } return $this; } /** * Load attributes into loaded entities * * @param bool $printQuery * @param bool $logQuery * @return $this * @throws \Exception * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function _loadAttributes($printQuery = false, $logQuery = false) { if (empty($this->_items) || empty($this->_itemsById) || empty($this->_selectAttributes)) { return $this; } $entity = $this->getEntity(); $tableAttributes = []; $attributeTypes = []; foreach ($this->_selectAttributes as $attributeCode => $attributeId) { if (!$attributeId) { continue; } $attribute = $this->_eavConfig->getAttribute($entity->getType(), $attributeCode); if ($attribute && !$attribute->isStatic()) { $tableAttributes[$attribute->getBackendTable()][] = (int) $attributeId; if (!isset($attributeTypes[$attribute->getBackendTable()])) { $attributeTypes[$attribute->getBackendTable()] = $attribute->getBackendType(); } } } $selects = []; foreach ($tableAttributes as $table => $attributes) { $select = $this->_getLoadAttributesSelect($table, $attributes); $selects[$attributeTypes[$table]][] = $this->_addLoadAttributesSelectValues( $select, $table, $attributeTypes[$table] ); } $selectGroups = $this->_resourceHelper->getLoadAttributesSelectGroups($selects); foreach ($selectGroups as $selects) { if (!empty($selects)) { if (is_array($selects)) { $select = implode(' UNION ALL ', $selects); } else { $select = $selects; } try { $values = $this->getConnection()->fetchAll($select); } catch (\Exception $e) { $this->printLogQuery(true, true, $select); throw $e; } foreach ($values as $value) { $this->_setItemAttributeValue($value); } } } return $this; } /** * Retrieve attributes load select * * @param string $table * @param string[] $attributeIds * @return Select */ protected function _getLoadAttributesSelect($table, $attributeIds = []) { if (empty($attributeIds)) { $attributeIds = $this->_selectAttributes; } $entity = $this->getEntity(); $linkField = $entity->getLinkField(); $select = $this->getConnection()->select() ->from( ['e' => $this->getEntity()->getEntityTable()], ['entity_id'] ) ->join( ['t_d' => $table], "e.{$linkField} = t_d.{$linkField}", ['t_d.attribute_id'] )->where( " e.entity_id IN (?)", array_keys($this->_itemsById), \Zend_Db::INT_TYPE )->where( 't_d.attribute_id IN (?)', $attributeIds, \Zend_Db::INT_TYPE ); if ($entity->getEntityTable() == \Magento\Eav\Model\Entity::DEFAULT_ENTITY_TABLE && $entity->getTypeId()) { $select->where( 't_d.entity_type_id =?', $entity->getTypeId() ); } return $select; } /** * Add select values * * @param \Magento\Framework\DB\Select $select * @param string $table * @param string $type * @return Select * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @codeCoverageIgnore */ protected function _addLoadAttributesSelectValues($select, $table, $type) { $select->columns(['value' => 't_d.value']); return $select; } /** * Initialize entity object property value * * Parameter $valueInfo is _getLoadAttributesSelect fetch result row * * @param array $valueInfo * @return $this * @throws LocalizedException */ protected function _setItemAttributeValue($valueInfo) { $entityIdField = $this->getEntity()->getEntityIdField(); $entityId = $valueInfo[$entityIdField]; if (!isset($this->_itemsById[$entityId])) { throw new LocalizedException( __('A header row is missing for an attribute. Verify the header row and try again.') ); } $attributeCode = array_search($valueInfo['attribute_id'], $this->_selectAttributes); if (!$attributeCode) { $attribute = $this->_eavConfig->getAttribute( $this->getEntity()->getType(), $valueInfo['attribute_id'] ); $attributeCode = $attribute->getAttributeCode(); } foreach ($this->_itemsById[$entityId] as $object) { $object->setData($attributeCode, $valueInfo['value']); } return $this; } /** * Get alias for attribute value table * * @param string $attributeCode * @return string */ protected function _getAttributeTableAlias($attributeCode) { return $this->getConnection()->getTableName(self::ATTRIBUTE_TABLE_ALIAS_PREFIX . $attributeCode); } /** * Retrieve attribute field name by attribute code * * @param string $attributeCode * @return string * @throws LocalizedException */ protected function _getAttributeFieldName($attributeCode) { $attributeCode = $attributeCode !== null ? trim($attributeCode) : ''; if (isset($this->_joinAttributes[$attributeCode]['condition_alias'])) { return $this->_joinAttributes[$attributeCode]['condition_alias']; } if (isset($this->_staticFields[$attributeCode])) { return sprintf('e.%s', $attributeCode); } if (isset($this->_joinFields[$attributeCode])) { $attr = $this->_joinFields[$attributeCode]; return $attr['table'] ? $attr['table'] . '.' . $attr['field'] : $attr['field']; } $attribute = $this->getAttribute($attributeCode); if (!$attribute) { throw new LocalizedException( __('The "%1" attribute name is invalid. Reset the name and try again.', $attributeCode) ); } if ($attribute->isStatic()) { if (isset($this->_joinAttributes[$attributeCode])) { $fieldName = $this->_getAttributeTableAlias($attributeCode) . '.' . $attributeCode; } else { $fieldName = 'e.' . $attributeCode; } } else { $fieldName = $this->_getAttributeTableAlias($attributeCode) . '.value'; } return $fieldName; } /** * Add attribute value table to the join if it wasn't added previously * * @param string $attributeCode * @param string $joinType inner|left * @return $this * @throws LocalizedException * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function _addAttributeJoin($attributeCode, $joinType = 'inner') { if (!empty($this->_filterAttributes[$attributeCode])) { return $this; } $connection = $this->getConnection(); $attrTable = $this->_getAttributeTableAlias($attributeCode); if (isset($this->_joinAttributes[$attributeCode])) { $attribute = $this->_joinAttributes[$attributeCode]['attribute']; $fkName = $this->_joinAttributes[$attributeCode]['bind']; $fkAttribute = $this->_joinAttributes[$attributeCode]['bindAttribute']; $fkTable = $this->_getAttributeTableAlias($fkName); if ($fkAttribute->getBackend()->isStatic()) { if (isset($this->_joinAttributes[$fkName])) { $fKey = $fkTable . '.' . $fkAttribute->getAttributeCode(); } else { $fKey = 'e.' . $fkAttribute->getAttributeCode(); } } else { $this->_addAttributeJoin($fkAttribute->getAttributeCode(), $joinType); $fKey = $fkTable . '.value'; } $pKey = $attrTable . '.' . $this->_joinAttributes[$attributeCode]['filter']; } else { $entity = $this->getEntity(); $fKey = 'e.' . $this->getEntityPkName($entity); $pKey = $attrTable . '.' . $this->getEntityPkName($entity); $attribute = $entity->getAttribute($attributeCode); } if (!$attribute) { throw new LocalizedException( __('The "%1" attribute name is invalid. Reset the name and try again.', $attributeCode) ); } if ($attribute->getBackend()->isStatic()) { $attrFieldName = $attrTable . '.' . $attribute->getAttributeCode(); } else { $attrFieldName = $attrTable . '.value'; } $fKey = $connection->quoteColumnAs($fKey, null); $pKey = $connection->quoteColumnAs($pKey, null); $condArr = ["{$pKey} = {$fKey}"]; if (!$attribute->getBackend()->isStatic()) { $condArr[] = $this->getConnection()->quoteInto( $connection->quoteColumnAs("{$attrTable}.attribute_id", null) . ' = ?', $attribute->getId() ); } /** * process join type */ $joinMethod = $joinType == 'left' ? 'joinLeft' : 'join'; $this->_joinAttributeToSelect($joinMethod, $attribute, $attrTable, $condArr, $attributeCode, $attrFieldName); $this->removeAttributeToSelect($attributeCode); $this->_filterAttributes[$attributeCode] = $attribute->getId(); /** * Fix double join for using same as filter */ $this->_joinFields[$attributeCode] = ['table' => '', 'field' => $attrFieldName]; return $this; } /** * Retrieve Entity Primary Key * * @param \Magento\Eav\Model\Entity\AbstractEntity $entity * @return string * @since 100.1.0 */ protected function getEntityPkName(\Magento\Eav\Model\Entity\AbstractEntity $entity) { return $entity->getEntityIdField(); } /** * Adding join statement to collection select instance * * @param string $method * @param object $attribute * @param string $tableAlias * @param array $condition * @param string $fieldCode * @param string $fieldAlias * @return $this */ protected function _joinAttributeToSelect($method, $attribute, $tableAlias, $condition, $fieldCode, $fieldAlias) { $this->getSelect()->{$method}( [$tableAlias => $attribute->getBackend()->getTable()], '(' . implode(') AND (', $condition) . ')', [$fieldCode => $fieldAlias] ); return $this; } /** * Get condition sql for the attribute * * @param string $attribute * @param mixed $condition * @param string $joinType * @return string * * @see self::_getConditionSql */ protected function _getAttributeConditionSql($attribute, $condition, $joinType = 'inner') { if (isset($this->_joinFields[$attribute])) { return $this->_getConditionSql($this->_getAttributeFieldName($attribute), $condition); } if (isset($this->_staticFields[$attribute])) { return $this->_getConditionSql($this->getConnection()->quoteIdentifier('e.' . $attribute), $condition); } // process linked attribute if (isset($this->_joinAttributes[$attribute])) { $entity = $this->getAttribute($attribute)->getEntity(); } else { $entity = $this->getEntity(); } if ($entity->isAttributeStatic($attribute)) { $conditionSql = $this->_getConditionSql( $this->getConnection()->quoteIdentifier('e.' . $attribute), $condition ); } else { if (isset($condition['null'])) { $joinType = 'left'; } $this->_addAttributeJoin($attribute, $joinType); if (isset($this->_joinAttributes[$attribute]['condition_alias'])) { $field = $this->_joinAttributes[$attribute]['condition_alias']; } else { $field = $this->_getAttributeTableAlias($attribute) . '.value'; } $conditionSql = $this->_getConditionSql($field, $condition); } return $conditionSql; } /** * Set sorting order * * Parameter $attribute can also be an array of attributes * * @param string|array $attribute * @param string $dir * @return $this */ public function setOrder($attribute, $dir = self::SORT_ORDER_ASC) { if (is_array($attribute)) { foreach ($attribute as $attr) { parent::setOrder($attr, $dir); } return $this; } return parent::setOrder($attribute, $dir); } /** * Retrieve array of attributes * * @param array $arrAttributes * @return array */ public function toArray($arrAttributes = []) { $arr = []; foreach ($this->getItems() as $key => $item) { $arr[$key] = $item->toArray($arrAttributes); } return $arr; } /** * Treat "order by" items as attributes to sort * * @return $this */ protected function _renderOrders() { if (!$this->_isOrdersRendered) { foreach ($this->_orders as $attribute => $direction) { $this->addAttributeToSort($attribute, $direction); } $this->_isOrdersRendered = true; } return $this; } /** * After load method * * @return $this * @codeCoverageIgnore */ protected function _afterLoad() { return $this; } /** * Reset collection * * @return $this */ protected function _reset() { parent::_reset(); $this->_selectEntityTypes = []; $this->_selectAttributes = []; $this->_filterAttributes = []; $this->_joinEntities = []; $this->_joinAttributes = []; $this->_joinFields = []; return $this; } /** * Check whether attribute with code is already added to collection * * @param string $attributeCode * @return bool * @since 102.0.0 */ public function isAttributeAdded($attributeCode) : bool { return isset($this->_selectAttributes[$attributeCode]); } /** * Returns already loaded element ids * * @return array * @codeCoverageIgnore */ public function getLoadedIds() { return array_keys($this->_items); } /** * Clear collection * * @return $this */ public function clear() { $this->_itemsById = []; return parent::clear(); } /** * Remove all items from collection * * @return $this */ public function removeAllItems() { $this->_itemsById = []; return parent::removeAllItems(); } /** * Remove item from collection by item key * * @param mixed $key * @return $this */ public function removeItemByKey($key) { if (isset($this->_items[$key])) { unset($this->_itemsById[$this->_items[$key]->getId()]); } return parent::removeItemByKey($key); } /** * Returns main table. * Returns main table name - extracted from "module/table" style and * validated by db adapter * * @return string */ public function getMainTable() { return $this->getSelect()->getPart(Select::FROM)['e']['tableName']; } /** * Wrapper for compatibility with \Magento\Framework\Data\Collection\AbstractDb * * @param string $field * @param string $alias * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return $this|\Magento\Framework\Data\Collection\AbstractDb * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @codeCoverageIgnore */ public function addFieldToSelect($field, $alias = null) { return $this->addAttributeToSelect($field); } /** * Wrapper for compatibility with \Magento\Framework\Data\Collection\AbstractDb * * @param string $field * @return $this|\Magento\Framework\Data\Collection\AbstractDb * @codeCoverageIgnore */ public function removeFieldFromSelect($field) { return $this->removeAttributeToSelect($field); } /** * Wrapper for compatibility with \Magento\Framework\Data\Collection\AbstractDb * * @return $this|\Magento\Framework\Data\Collection\AbstractDb * @codeCoverageIgnore */ public function removeAllFieldsFromSelect() { return $this->removeAttributeToSelect(); } }