/** * Получить список сущностей по фильтру * * @param array $aFilter Фильтр * @param string|null $sEntityFull Название класса сущности * @return array */ public function GetItemsByFilter($aFilter = array(), $sEntityFull = null) { if (is_null($aFilter)) { $aFilter = array(); } $sEntityFull = $this->_NormalizeEntityRootName($sEntityFull); $aFilter = $this->_applyScopes($sEntityFull, $aFilter); /** * Хук для возможности изменения фильтра */ $this->RunBehaviorHook('module_orm_GetItemsByFilter_before', array('aFilter' => &$aFilter, 'sEntityFull' => $sEntityFull), true); // Если параметр #cache указан и пуст, значит игнорируем кэширование для запроса if (array_key_exists('#cache', $aFilter) && !$aFilter['#cache']) { $aEntities = $this->oMapperORM->GetItemsByFilter($aFilter, $sEntityFull); } else { $aFilterCache = $aFilter; unset($aFilterCache['#with']); $sCacheKey = $sEntityFull . '_items_by_filter_' . serialize($aFilterCache); $aCacheTags = array($sEntityFull . '_save', $sEntityFull . '_delete'); $iCacheTime = 60 * 60 * 24; // скорее лучше хранить в свойстве сущности, для возможности выборочного переопределения // переопределяем из параметров if (isset($aFilter['#cache'][0])) { $sCacheKey = $aFilter['#cache'][0]; } if (isset($aFilter['#cache'][1])) { $aCacheTags = $aFilter['#cache'][1]; } if (isset($aFilter['#cache'][2])) { $iCacheTime = $aFilter['#cache'][2]; } if (false === ($aEntities = $this->Cache_Get($sCacheKey))) { $aEntities = $this->oMapperORM->GetItemsByFilter($aFilter, $sEntityFull); $this->Cache_Set($aEntities, $sCacheKey, $aCacheTags, $iCacheTime); } } /** * Если необходимо подцепить связанные данные */ if (count($aEntities) and isset($aFilter['#with'])) { if (!is_array($aFilter['#with'])) { $aFilter['#with'] = array($aFilter['#with']); } /** * Приводим значение к единой форме ассоциативного массива: array('user'=>array(), 'topic'=>array('blog_id'=>123) ) */ func_array_simpleflip($aFilter['#with'], array()); /** * Формируем список примари ключей */ $aEntityPrimaryKeys = array(); foreach ($aEntities as $oEntity) { $aEntityPrimaryKeys[] = $oEntity->_getPrimaryKeyValue(); } $oEntityEmpty = Engine::GetEntity($sEntityFull); $aRelations = $oEntityEmpty->_getRelations(); foreach ($aFilter['#with'] as $sRelationName => $aRelationFilter) { if (!isset($aRelations[$sRelationName])) { continue; } /** * Если нужна дополнительная обработка через коллбек * Параметр в обработчике должен приниматься по ссылке */ if (isset($aRelationFilter['#callback-filter']) and $aRelationFilter['#callback-filter'] instanceof Closure) { $callback = $aRelationFilter['#callback-filter']; $callback($aEntities, $aRelationFilter); } /** * Если необходимо, то выставляем сразу нужное значение и не делаем никаких запросов */ if (isset($aRelationFilter['#value-set'])) { foreach ($aEntities as $oEntity) { $oEntity->_setData(array($sRelationName => $aRelationFilter['#value-set'])); } continue; } /** * Чистим фильтр от коллбека, иначе он может пройти дальше по цепочке вызовов */ unset($aRelationFilter['#callback-filter']); $sRelType = $aRelations[$sRelationName]['type']; $sRelEntity = $this->Plugin_GetRootDelegater('entity', $aRelations[$sRelationName]['rel_entity']); // получаем корневую сущность, без учета наследников $sRelKey = $aRelations[$sRelationName]['rel_key']; if (!array_key_exists($sRelationName, $aRelations) or !in_array($sRelType, array(EntityORM::RELATION_TYPE_BELONGS_TO, EntityORM::RELATION_TYPE_HAS_ONE, EntityORM::RELATION_TYPE_HAS_MANY, EntityORM::RELATION_TYPE_MANY_TO_MANY))) { throw new Exception("The entity <{$sEntityFull}> not have relation <{$sRelationName}>"); } /** * Делаем общий запрос по всем ключам */ $oRelEntityEmpty = Engine::GetEntity($sRelEntity); $sRelModuleName = Engine::GetModuleName($sRelEntity); $sRelEntityName = Engine::GetEntityName($sRelEntity); $sRelPluginPrefix = Engine::GetPluginPrefix($sRelEntity); $sRelPrimaryKey = method_exists($oRelEntityEmpty, '_getPrimaryKey') ? $oRelEntityEmpty->_getPrimaryKey() : 'id'; if ($sRelType == EntityORM::RELATION_TYPE_BELONGS_TO) { /** * Формируем список ключей */ $aEntityKeyValues = array(); foreach ($aEntities as $oEntity) { $aEntityKeyValues[] = $oEntity->_getDataOne($sRelKey); } $aEntityKeyValues = array_unique($aEntityKeyValues); $sKeyTo = $aRelations[$sRelationName]['rel_key_to'] ?: $sRelPrimaryKey; $aFilterRel = array($sKeyTo . ' in' => $aEntityKeyValues, '#index-from' => $sKeyTo); $aFilterRel = array_merge($aFilterRel, $aRelationFilter); $aRelData = Engine::GetInstance()->_CallModule("{$sRelPluginPrefix}{$sRelModuleName}_get{$sRelEntityName}ItemsByFilter", array($aFilterRel)); } elseif ($sRelType == EntityORM::RELATION_TYPE_HAS_ONE) { $aFilterRel = array($sRelKey . ' in' => $aEntityPrimaryKeys, '#index-from' => $sRelKey); $aFilterRel = array_merge($aFilterRel, $aRelationFilter, $aRelations[$sRelationName]['filter']); $aRelData = Engine::GetInstance()->_CallModule("{$sRelPluginPrefix}{$sRelModuleName}_get{$sRelEntityName}ItemsByFilter", array($aFilterRel)); } elseif ($sRelType == EntityORM::RELATION_TYPE_HAS_MANY) { if ($aRelations[$sRelationName]['key_from']) { /** * Формируем список ключей */ $aEntityKeyValues = array(); foreach ($aEntities as $oEntity) { $aEntityKeyValues[] = $oEntity->_getDataOne($aRelations[$sRelationName]['key_from']); } $aEntityKeyValues = array_unique($aEntityKeyValues); } $aFilterRel = array($sRelKey . ' in' => $aRelations[$sRelationName]['key_from'] ? $aEntityKeyValues : $aEntityPrimaryKeys, '#index-group' => $sRelKey); $aFilterRel = array_merge($aFilterRel, $aRelationFilter, $aRelations[$sRelationName]['filter']); $aRelData = Engine::GetInstance()->_CallModule("{$sRelPluginPrefix}{$sRelModuleName}_get{$sRelEntityName}ItemsByFilter", array($aFilterRel)); } elseif ($sRelType == EntityORM::RELATION_TYPE_MANY_TO_MANY) { $sEntityJoin = $aRelations[$sRelationName]['join_entity']; $sKeyJoin = $aRelations[$sRelationName]['join_key']; $aFilterAdd = $aRelations[$sRelationName]['filter']; if (!array_key_exists('#value-default', $aRelationFilter)) { $aRelationFilter['#value-default'] = array(); } $aFilterRel = array_merge($aFilterAdd, $aRelationFilter); $aRelData = Engine::GetInstance()->_CallModule("{$sRelPluginPrefix}{$sRelModuleName}_get{$sRelEntityName}ItemsByJoinEntity", array($sEntityJoin, $sKeyJoin, $sRelKey, $aEntityPrimaryKeys, $aFilterRel)); $aRelData = $this->_setIndexesGroupJoinField($aRelData, $sKeyJoin); } /** * Собираем набор */ foreach ($aEntities as $oEntity) { if ($sRelType == EntityORM::RELATION_TYPE_BELONGS_TO) { $sKeyData = $oEntity->_getDataOne($sRelKey); } elseif (in_array($sRelType, array(EntityORM::RELATION_TYPE_HAS_ONE, EntityORM::RELATION_TYPE_HAS_MANY, EntityORM::RELATION_TYPE_MANY_TO_MANY))) { $sKeyData = $oEntity->_getPrimaryKeyValue(); } else { break; } if ($sRelType == EntityORM::RELATION_TYPE_HAS_MANY and $aRelations[$sRelationName]['key_from']) { $sKeyData = $oEntity->_getDataOne($aRelations[$sRelationName]['key_from']); } if (isset($aRelData[$sKeyData])) { $oEntity->_setData(array($sRelationName => $aRelData[$sKeyData])); } elseif (isset($aRelationFilter['#value-default'])) { $oEntity->_setData(array($sRelationName => $aRelationFilter['#value-default'])); } if ($sRelType == EntityORM::RELATION_TYPE_MANY_TO_MANY) { // Создаём объекты-обёртки для связей MANY_TO_MANY $oEntity->_setManyToManyRelations(new ORMRelationManyToMany($oEntity->_getRelationsData($sRelationName)), $sRelationName); } } } } /** * Returns assotiative array, indexed by PRIMARY KEY or another field. */ if (in_array('#index-from-primary', $aFilter, true) || !empty($aFilter['#index-from'])) { $aEntities = $this->_setIndexesFromField($aEntities, $aFilter); } /** * Группирует результирующий массив по ключам необходимого поля */ if (!empty($aFilter['#index-group'])) { $aEntities = $this->_setIndexesGroupField($aEntities, $aFilter); } /** * Хук для возможности кастомной обработки результата */ $this->RunBehaviorHook('module_orm_GetItemsByFilter_after', array('aEntities' => $aEntities, 'aFilter' => $aFilter, 'sEntityFull' => $sEntityFull), true); /** * Если запрашиваем постраничный список, то возвращаем сам список и общее количество записей */ if (isset($aFilter['#page'])) { if (isset($aFilter['#cache'][0])) { /** * Задан собственный ключ для хранения кеша, поэтому нужно его сменить для передачи в GetCount* * Добавляем префикс 'count_' */ $aFilter['#cache'][0] = 'count_' . $aFilter['#cache'][0]; } return array('collection' => $aEntities, 'count' => $this->GetCountItemsByFilter($aFilter, $sEntityFull)); } return $aEntities; }
/** * Получить список сущностей по фильтру * * @param array $aFilter Фильтр * @param string|null $sEntityFull Название класса сущности * * @return array * @throws Exception */ public function GetItemsByFilter($aFilter = array(), $sEntityFull = null) { if (is_null($aFilter)) { $aFilter = array(); } if (is_null($sEntityFull)) { $sEntityFull = E::GetPluginPrefix($this) . 'Module' . E::GetModuleName($this) . '_Entity' . E::GetModuleName(get_class($this)); } elseif (!substr_count($sEntityFull, '_')) { $sEntityFull = E::GetPluginPrefix($this) . 'Module' . E::GetModuleName($this) . '_Entity' . $sEntityFull; } elseif (strpos($sEntityFull, '_') && strpos($sEntityFull, 'Module') !== 0 && !strpos($sEntityFull, '_Entity')) { if (substr_count($sEntityFull, '_') == 1) { list($sModule, $sEntity) = explode('_', $sEntityFull); $sEntityFull = E::GetPluginPrefix($this) . 'Module' . $sModule . '_Entity' . $sEntity; } } // Если параметр #cache указан и пуст, значит игнорируем кэширование для запроса if (array_key_exists('#cache', $aFilter) && !$aFilter['#cache']) { $aEntities = $this->oMapper->GetItemsByFilter($aFilter, $sEntityFull); } else { $sEntityFullRoot = E::ModulePlugin()->GetRootDelegater('entity', $sEntityFull); $sCacheKey = $sEntityFullRoot . '_items_by_filter_' . serialize($aFilter); $aCacheTags = array($sEntityFullRoot . '_save', $sEntityFullRoot . '_delete'); $iCacheTime = 60 * 60 * 24; // скорее лучше хранить в свойстве сущности, для возможности выборочного переопределения // переопределяем из параметров if (isset($aFilter['#cache'][0])) { $sCacheKey = $aFilter['#cache'][0]; } if (isset($aFilter['#cache'][1])) { $aCacheTags = $aFilter['#cache'][1]; } if (isset($aFilter['#cache'][2])) { $iCacheTime = $aFilter['#cache'][2]; } if (false === ($aEntities = E::ModuleCache()->Get($sCacheKey))) { $aEntities = $this->oMapper->GetItemsByFilter($aFilter, $sEntityFull); E::ModuleCache()->Set($aEntities, $sCacheKey, $aCacheTags, $iCacheTime); } } /** * Если необходимо подцепить связанные данные */ if (count($aEntities) && isset($aFilter['#with'])) { if (!is_array($aFilter['#with'])) { $aFilter['#with'] = array($aFilter['#with']); } $oEntityEmpty = E::GetEntity($sEntityFull); $aRelations = $oEntityEmpty->_getRelations(); $aEntityKeys = array(); foreach ($aFilter['#with'] as $sRelationName) { $sRelType = $aRelations[$sRelationName][0]; // получаем корневую сущность, без учета наследников $sRelEntity = E::ModulePlugin()->GetRootDelegater('entity', $aRelations[$sRelationName][1]); $sRelKey = $aRelations[$sRelationName][2]; if (!array_key_exists($sRelationName, $aRelations) || !in_array($sRelType, array(EntityORM::RELATION_TYPE_BELONGS_TO, EntityORM::RELATION_TYPE_HAS_ONE))) { throw new Exception("The entity <{$sEntityFull}> not have relation <{$sRelationName}>"); } /** * Формируем список ключей */ foreach ($aEntities as $oEntity) { $aEntityKeys[$sRelKey][] = $oEntity->getProp($sRelKey); } $aEntityKeys[$sRelKey] = array_unique($aEntityKeys[$sRelKey]); /** * Делаем общий запрос по всем ключам */ $oRelEntityEmpty = E::GetEntity($sRelEntity); $sRelModuleName = E::GetModuleName($sRelEntity); $sRelEntityName = E::GetEntityName($sRelEntity); $sRelPluginPrefix = E::GetPluginPrefix($sRelEntity); $sRelPrimaryKey = method_exists($oRelEntityEmpty, '_getPrimaryKey') ? F::StrCamelize($oRelEntityEmpty->_getPrimaryKey()) : 'Id'; $aCallParams = array($aEntityKeys[$sRelKey]); $aRelData = E::GetInstance()->_CallModule("{$sRelPluginPrefix}{$sRelModuleName}_get{$sRelEntityName}ItemsByArray{$sRelPrimaryKey}", $aCallParams); /** * Собираем набор */ foreach ($aEntities as $oEntity) { if (isset($aRelData[$oEntity->getProp($sRelKey)])) { $oEntity->_setData(array($sRelationName => $aRelData[$oEntity->getProp($sRelKey)])); } } } } /** * Returns assotiative array, indexed by PRIMARY KEY or another field. */ if (in_array('#index-from-primary', $aFilter) || !empty($aFilter['#index-from'])) { $aEntities = $this->_setIndexesFromField($aEntities, $aFilter); } /** * Если запрашиваем постраничный список, то возвращаем сам список и общее количество записей */ if (isset($aFilter['#page'])) { return array('collection' => $aEntities, 'count' => $this->GetCountItemsByFilter($aFilter, $sEntityFull)); } return $aEntities; }