ifhub2/framework/classes/engine/ModuleORM.class.php

1360 lines
58 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/*
* LiveStreet CMS
* Copyright © 2013 OOO "ЛС-СОФТ"
*
* ------------------------------------------------------
*
* Official site: www.livestreetcms.com
* Contact e-mail: office@livestreetcms.com
*
* GNU General Public License, version 2:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* ------------------------------------------------------
*
* @link http://www.livestreetcms.com
* @copyright 2013 OOO "ЛС-СОФТ"
* @author Maxim Mzhelskiy <rus.engine@gmail.com>
*
*/
/**
* Абстракция модуля ORM
* Предоставляет базовые методы для работы с EntityORM, например,
* <pre>
* $aUsers=$this->User_GetUserItemsByAgeAndSex(18,'male');
* </pre>
*
* @package framework.engine.orm
* @since 1.0
*/
abstract class ModuleORM extends Module
{
/**
* Объект маппера ORM
*
* @var MapperORM
*/
protected $oMapperORM = null;
/**
* Инициализация
* В наследнике этот метод нельзя перекрывать, необходимо вызывать через parent::Init();
*
*/
public function Init()
{
$this->_LoadMapperORM();
}
/**
* Загрузка маппера ORM
*
*/
protected function _LoadMapperORM()
{
$this->oMapperORM = new MapperORM($this->Database_GetConnect());
}
/**
* Добавление сущности в БД
* Вызывается не напрямую, а через сущность, например
* <pre>
* $oUser->setName('Claus');
* $oUser->Add();
* </pre>
*
* @param EntityORM $oEntity Объект сущности
* @return EntityORM|bool
*/
protected function _AddEntity($oEntity)
{
$res = $this->oMapperORM->AddEntity($oEntity);
// сбрасываем кеш
if ($res === 0 or $res) {
$sEntity = $this->Plugin_GetRootDelegater('entity', get_class($oEntity));
$this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array($sEntity . '_save'));
}
if ($res === 0) {
// у таблицы нет автоинремента
$oEntity->_setOriginalData($oEntity->_getDataFieldsForDb());
return $oEntity;
} elseif ($res) {
// есть автоинкремент, устанавливаем его
$oEntity->_setData(array($oEntity->_getPrimaryKey() => $res));
$oEntity->_setOriginalData($oEntity->_getDataFieldsForDb());
/**
* Смотрим наличие связи many_to_many и добавляем их в бд
*/
foreach ($oEntity->_getRelations() as $sRelName => $aRelation) {
if ($aRelation['type'] == EntityORM::RELATION_TYPE_MANY_TO_MANY) {
if ($oEntity->$sRelName->isUpdated()) {
$this->_updateManyToManyRelation($oEntity, $sRelName);
}
$oEntity->resetRelationsData($sRelName);
}
}
return $oEntity;
}
return false;
}
/**
* Обновление сущности в БД
*
* @param EntityORM $oEntity Объект сущности
* @return EntityORM|bool
*/
protected function _UpdateEntity($oEntity)
{
$res = $this->oMapperORM->UpdateEntity($oEntity);
if ($res === 0 or $res) { // запись не изменилась, либо изменилась
// Обновление связей many_to_many
foreach ($oEntity->_getRelations() as $sRelName => $aRelation) {
if ($aRelation['type'] == EntityORM::RELATION_TYPE_MANY_TO_MANY && $oEntity->$sRelName->isUpdated()) {
$this->_updateManyToManyRelation($oEntity, $sRelName);
$oEntity->resetRelationsData($sRelName);
}
}
// обновляем оригинальные данные
$oEntity->_setOriginalData($oEntity->_getDataFieldsForDb());
// сбрасываем кеш
$sEntity = $this->Plugin_GetRootDelegater('entity', get_class($oEntity));
$this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array($sEntity . '_save'));
return $oEntity;
}
return false;
}
/**
* Сохранение сущности в БД
*
* @param EntityORM $oEntity Объект сущности
* @return EntityORM|bool
*/
protected function _SaveEntity($oEntity)
{
if ($oEntity->_isNew()) {
return $this->_AddEntity($oEntity);
} else {
return $this->_UpdateEntity($oEntity);
}
}
/**
* Удаление сущности из БД
*
* @param EntityORM $oEntity Объект сущности
* @return EntityORM|bool
*/
protected function _DeleteEntity($oEntity)
{
$res = $this->oMapperORM->DeleteEntity($oEntity);
if ($res) {
// сбрасываем кеш
$sEntity = $this->Plugin_GetRootDelegater('entity', get_class($oEntity));
$this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array($sEntity . '_delete'));
// Удаление связей many_to_many
foreach ($oEntity->_getRelations() as $sRelName => $aRelation) {
if ($aRelation['type'] == EntityORM::RELATION_TYPE_MANY_TO_MANY) {
$this->_deleteManyToManyRelation($oEntity, $sRelName);
}
}
return $oEntity;
}
return false;
}
/**
* Обновляет данные сущности из БД
*
* @param EntityORM $oEntity Объект сущности
* @return EntityORM|bool
*/
protected function _ReloadEntity($oEntity)
{
if ($sPrimaryKey = $oEntity->_getPrimaryKey()) {
if ($sPrimaryKeyValue = $oEntity->_getDataOne($sPrimaryKey)) {
if ($oEntityNew = $this->GetByFilter(array($sPrimaryKey => $sPrimaryKeyValue),
Engine::GetEntityName($oEntity))
) {
$oEntity->_setData($oEntityNew->_getData());
$oEntity->_setOriginalData($oEntity->_getDataFields());
$oEntity->_setRelationsData(array());
return $oEntity;
}
}
}
return false;
}
/**
* Список полей сущности
*
* @param EntityORM $oEntity Объект сущности
* @return array
*/
protected function _ShowColumnsFrom($oEntity)
{
return $this->oMapperORM->ShowColumnsFrom($oEntity);
}
/**
* Primary индекс сущности
*
* @param EntityORM $oEntity Объект сущности
* @return array
*/
protected function _ShowPrimaryIndexFrom($oEntity)
{
return $this->oMapperORM->ShowPrimaryIndexFrom($oEntity);
}
/**
* Для сущности со связью RELATION_TYPE_TREE возвращает список прямых потомков
*
* @param EntityORM $oEntity Объект сущности
* @return array
*/
protected function _GetChildrenOfEntity($oEntity)
{
if ($oEntity->_isUsedRelationType(EntityORM::RELATION_TYPE_TREE)) {
$aRelationsData = $oEntity->_getRelationsData();
if (array_key_exists('children', $aRelationsData)) {
$aChildren = $aRelationsData['children'];
} else {
$aChildren = array();
if ($sPrimaryKey = $oEntity->_getPrimaryKey()) {
if ($sPrimaryKeyValue = $oEntity->_getDataOne($sPrimaryKey)) {
$aChildren = $this->GetItemsByFilter(array($oEntity->_getTreeParentKey() => $sPrimaryKeyValue),
Engine::GetEntityName($oEntity));
}
}
}
if (is_array($aChildren)) {
$oEntity->setChildren($aChildren);
return $aChildren;
}
}
return false;
}
/**
* Для сущности со связью RELATION_TYPE_TREE возвращает предка
*
* @param EntityORM $oEntity Объект сущности
* @return EntityORM|bool
*/
protected function _GetParentOfEntity($oEntity)
{
if ($oEntity->_isUsedRelationType(EntityORM::RELATION_TYPE_TREE)) {
$aRelationsData = $oEntity->_getRelationsData();
if (array_key_exists('parent', $aRelationsData)) {
$oParent = $aRelationsData['parent'];
} else {
$oParent = null;
if ($sPrimaryKey = $oEntity->_getPrimaryKey()) {
if ($sParentId = $oEntity->_getTreeParentKeyValue()) {
$oParent = $this->GetByFilter(array($sPrimaryKey => $sParentId),
Engine::GetEntityName($oEntity));
}
}
}
$oEntity->setParent($oParent);
return $oParent;
}
return false;
}
/**
* Для сущности со связью RELATION_TYPE_TREE возвращает список всех предков
*
* @param EntityORM $oEntity Объект сущности
* @return array
*/
protected function _GetAncestorsOfEntity($oEntity)
{
if ($oEntity->_isUsedRelationType(EntityORM::RELATION_TYPE_TREE)) {
$aRelationsData = $oEntity->_getRelationsData();
if (array_key_exists('ancestors', $aRelationsData)) {
$aAncestors = $aRelationsData['ancestors'];
} else {
$aAncestors = array();
$oEntityParent = $oEntity->getParent();
while (is_object($oEntityParent)) {
$aAncestors[] = $oEntityParent;
$oEntityParent = $oEntityParent->getParent();
}
}
if (is_array($aAncestors)) {
$oEntity->setAncestors($aAncestors);
return $aAncestors;
}
}
return false;
}
/**
* Для сущности со связью RELATION_TYPE_TREE возвращает список всех потомков
*
* @param EntityORM $oEntity Объект сущности
* @return array
*/
protected function _GetDescendantsOfEntity($oEntity)
{
if ($oEntity->_isUsedRelationType(EntityORM::RELATION_TYPE_TREE)) {
$aRelationsData = $oEntity->_getRelationsData();
if (array_key_exists('descendants', $aRelationsData)) {
$aDescendants = $aRelationsData['descendants'];
} else {
$aDescendants = array();
if ($aChildren = $oEntity->getChildren()) {
$aTree = self::buildTree($aChildren);
foreach ($aTree as $aItem) {
$aDescendants[] = $aItem['entity'];
}
}
}
if (is_array($aDescendants)) {
$oEntity->setDescendants($aDescendants);
return $aDescendants;
}
}
return false;
}
/**
* Для сущностей со связью RELATION_TYPE_TREE возвращает список сущностей в виде дерева
*
* @param array $aFilter Фильтр
* @param string $sEntityFull Название класса сущности
* @return array|bool
*/
public function LoadTree($aFilter = array(), $sEntityFull = null)
{
$sEntityFull = $this->_NormalizeEntityRootName($sEntityFull);
if ($oEntityDefault = Engine::GetEntity($sEntityFull)) {
if ($oEntityDefault->_isUsedRelationType(EntityORM::RELATION_TYPE_TREE)) {
if ($sPrimaryKey = $oEntityDefault->_getPrimaryKey()) {
if ($aItems = $this->GetItemsByFilter($aFilter, $sEntityFull)) {
$aItemsById = array();
$aItemsByParentId = array();
foreach ($aItems as $oEntity) {
$oEntity->setChildren(array());
$aItemsById[$oEntity->_getDataOne($sPrimaryKey)] = $oEntity;
$sParentKeyValue = $oEntity->_getTreeParentKeyValue() ? $oEntity->_getTreeParentKeyValue() : 'root';
if (empty($aItemsByParentId[$sParentKeyValue])) {
$aItemsByParentId[$sParentKeyValue] = array();
}
$aItemsByParentId[$sParentKeyValue][] = $oEntity;
}
foreach ($aItemsByParentId as $iParentId => $aItems) {
if ($iParentId != 'root') {
if (isset($aItemsById[$iParentId])) {
$aItemsById[$iParentId]->setChildren($aItems);
foreach ($aItems as $oEntity) {
$oEntity->setParent($aItemsById[$iParentId]);
}
}
} else {
foreach ($aItems as $oEntity) {
$oEntity->setParent(null);
}
}
}
return isset($aItemsByParentId['root']) ? $aItemsByParentId['root'] : array();
}
}
}
}
return false;
}
/**
* Удаляет сущности по фильтру
* Удаление происходит отдельно для каждой сущности через вызов метода Delete()
*
* @param array $aFilter
* @param null $sEntityFull
*/
public function DeleteItemsByFilter($aFilter = array(), $sEntityFull = null)
{
$aItems = $this->GetItemsByFilter($aFilter, $sEntityFull);
foreach ($aItems as $oItem) {
$oItem->Delete();
}
}
/**
* Получить сущность по фильтру
*
* @param array $aFilter Фильтр
* @param string $sEntityFull Название класса сущности
* @return EntityORM|null
*/
public function GetByFilter($aFilter = array(), $sEntityFull = null)
{
$sEntityFull = $this->_NormalizeEntityRootName($sEntityFull);
$aFilter = $this->_applyScopes($sEntityFull, $aFilter);
/**
* Хук для возможности изменения фильтра
*/
$this->RunBehaviorHook('module_orm_GetByFilter_before',
array('aFilter' => &$aFilter, 'sEntityFull' => $sEntityFull), true);
$aEntities = $this->oMapperORM->GetByFilter($aFilter, $sEntityFull);
/**
* Хук для возможности кастомной обработки результата
*/
$this->RunBehaviorHook('module_orm_GetByFilter_after',
array('aEntities' => $aEntities, 'aFilter' => $aFilter, 'sEntityFull' => $sEntityFull), true);
return $aEntities;
}
/**
* Получить список сущностей по фильтру
*
* @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']);
unset($aFilterCache['#scope']);
$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());
/**
* Подтягиваем связанные данные
*/
$this->_attachRelationObjects($aEntities, $aFilter['#with'], $sEntityFull);
}
/**
* 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;
}
/**
* Returns assotiative array, indexed by PRIMARY KEY or another field.
*
* @param array $aEntities Список сущностей
* @param array $aFilter Фильтр
* @return array
* @throws
*/
protected function _setIndexesFromField($aEntities, $aFilter)
{
$aIndexedEntities = array();
foreach ($aEntities as $oEntity) {
$sKey = in_array('#index-from-primary',
$aFilter, true) || (!empty($aFilter['#index-from']) && $aFilter['#index-from'] == '#primary') ?
$oEntity->_getPrimaryKey() :
$oEntity->_getField($aFilter['#index-from']);
if (is_array($sKey)) {
throw new Exception("The entity <" . get_class($oEntity) . "> allow only single key for index-from");
}
$aIndexedEntities[$oEntity->_getDataOne($sKey)] = $oEntity;
}
return $aIndexedEntities;
}
/**
* @param array $aEntities Список сущностей
* @param array $aWith Список связей (в фильтре используется как "#with")
* @param null|string $sEntityFull Исходный класс сущности для которой подтягиваем связи
* @throws Exception
*/
protected function _attachRelationObjects($aEntities, $aWith, $sEntityFull = null)
{
if (!count($aEntities)) {
return;
}
func_array_simpleflip($aWith, array());
if (is_null($sEntityFull)) {
$oEntityFirst = reset($aEntities);
$sEntityFull = $this->_NormalizeEntityRootName($oEntityFirst);
}
/**
* Формируем список примари ключей
*/
$aEntityPrimaryKeys = array();
foreach ($aEntities as $oEntity) {
$aEntityPrimaryKeys[] = $oEntity->_getPrimaryKeyValue();
}
$oEntityEmpty = Engine::GetEntity($sEntityFull);
$aRelations = $oEntityEmpty->_getRelations();
foreach ($aWith 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']));
} elseif ($sRelType == EntityORM::RELATION_TYPE_HAS_MANY) {
$oEntity->_setData(array($sRelationName => array()));
}
if ($sRelType == EntityORM::RELATION_TYPE_MANY_TO_MANY) {
// Создаём объекты-обёртки для связей MANY_TO_MANY
$oEntity->_setManyToManyRelations(new ORMRelationManyToMany($oEntity->_getRelationsData($sRelationName)),
$sRelationName);
}
}
}
}
/**
* Возвращает сгруппированный массив по нужному полю
*
* @param array $aEntities
* @param array $aFilter
*
* @return array
*/
protected function _setIndexesGroupField($aEntities, $aFilter)
{
$aIndexedEntities = array();
foreach ($aEntities as $oEntity) {
$sKey = $oEntity->_getField($aFilter['#index-group']);
$aIndexedEntities[$oEntity->_getDataOne($sKey)][] = $oEntity;
}
return $aIndexedEntities;
}
/**
* Возвращает сгруппированный массив по нужному полю из данных таблицы связей
*
* @param array $aEntities
* @param string $sField
*
* @return array
*/
protected function _setIndexesGroupJoinField($aEntities, $sField)
{
$aIndexedEntities = array();
foreach ($aEntities as $oEntity) {
$oRelEntity = $oEntity->_getDataOne('_relation_entity');
if ($oRelEntity) {
$sVal = $oRelEntity->_getDataOne($oRelEntity->_getField($sField));
if (!is_null($sVal)) {
$aIndexedEntities[$sVal][] = $oEntity;
}
}
}
return $aIndexedEntities;
}
/**
* Применяет дополнительные фильтры scope
*
* @param $sEntityFull
* @param $aFilter
* @return array
*/
protected function _applyScopes($sEntityFull, $aFilter)
{
if (isset($aFilter['#scope'])) {
$aScopes = $aFilter['#scope'];
if (!is_array($aScopes)) {
$aScopes = array($aScopes);
}
/**
* Приводим значение к единой форме ассоциативного массива: array('user'=>array(), 'topic'=>array('blog_id'=>123) )
*/
func_array_simpleflip($aScopes, array());
$oEntityEmpty = Engine::GetEntity($sEntityFull);
foreach ($aScopes as $sScope => $aScopeParams) {
$sMethod = 'getScope' . func_camelize($sScope);
if (method_exists($oEntityEmpty, $sMethod)) {
if ($aFilterAdd = call_user_func_array(array($oEntityEmpty, $sMethod), $aScopeParams) and is_array($aFilterAdd)) {
$aFilter = array_merge($aFilterAdd, $aFilter);
}
}
}
}
return $aFilter;
}
/**
* Получить значение агрегирующей функции
*
* @param $sAggregateFunction
* @param $sField
* @param array $aFilter
* @param null $sEntityFull
*
* @return EntityORM|null
*/
public function GetAggregateFunctionByFilter($sAggregateFunction, $sField, $aFilter = array(), $sEntityFull = null)
{
$sEntityFull = $this->_NormalizeEntityRootName($sEntityFull);
// Если параметр #cache указан и пуст, значит игнорируем кэширование для запроса
if (array_key_exists('#cache', $aFilter) && !$aFilter['#cache']) {
$iValue = $this->oMapperORM->GetAggregateFunctionByFilter($sAggregateFunction, $sField, $aFilter,
$sEntityFull);
} else {
$sCacheKey = $sEntityFull . "_aggregate_function_by_filter_{$sAggregateFunction}_{$sField}" . serialize($aFilter);
$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 === ($iValue = $this->Cache_Get($sCacheKey))) {
$iValue = $this->oMapperORM->GetAggregateFunctionByFilter($sAggregateFunction, $sField, $aFilter,
$sEntityFull);
$this->Cache_Set($iValue, $sCacheKey, $aCacheTags, $iCacheTime);
}
}
return $iValue;
}
/**
* Получить количество сущностей по фильтру
*
* @param array $aFilter Фильтр
* @param string $sEntityFull Название класса сущности
* @return int
*/
public function GetCountItemsByFilter($aFilter = array(), $sEntityFull = null)
{
$sEntityFull = $this->_NormalizeEntityRootName($sEntityFull);
// Если параметр #cache указан и пуст, значит игнорируем кэширование для запроса
if (array_key_exists('#cache', $aFilter) && !$aFilter['#cache']) {
$iCount = $this->oMapperORM->GetCountItemsByFilter($aFilter, $sEntityFull);
} else {
$aFilterCache = $aFilter;
unset($aFilterCache['#with']);
$sCacheKey = $sEntityFull . '_count_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 === ($iCount = $this->Cache_Get($sCacheKey))) {
$iCount = $this->oMapperORM->GetCountItemsByFilter($aFilter, $sEntityFull);
$this->Cache_Set($iCount, $sCacheKey, $aCacheTags, $iCacheTime);
}
}
return $iCount;
}
/**
* Возвращает список сущностей по фильтру
* В качестве ключей возвращаемого массива используется primary key сущности
*
* @param array $aFilter Фильтр
* @param string|null $sEntityFull Название класса сущности
* @return array
*/
public function GetItemsByArray($aFilter, $sEntityFull = null)
{
foreach ($aFilter as $k => $v) {
$aFilter["{$k} IN"] = $v;
unset($aFilter[$k]);
}
$aFilter[] = '#index-from-primary';
return $this->GetItemsByFilter($aFilter, $sEntityFull);
}
public function GetItemsByJoinEntity(
$sEntityJoin,
$sKeyJoin,
$sRelationKey,
$aRelationValues,
$aFilter,
$sEntityFull = null
) {
$sEntityFull = $this->_NormalizeEntityRootName($sEntityFull);
/**
* Кеширование
* Если параметр #cache указан и пуст, значит игнорируем кэширование для запроса
*/
if (array_key_exists('#cache', $aFilter) && !$aFilter['#cache']) {
$aEntities = $this->oMapperORM->GetItemsByJoinEntity($sEntityJoin, $sKeyJoin, $sRelationKey,
$aRelationValues, $aFilter, $sEntityFull);
} else {
$sEntityJoin = $this->Plugin_GetRootDelegater('entity', $sEntityJoin);
$sCacheKey = 'items_by_join_entity_' . serialize(array(
$sEntityJoin,
$sKeyJoin,
$sRelationKey,
$aRelationValues,
$aFilter,
$sEntityFull
));
/**
* Формируем теги для сброса кеша
* Сброс идет по обновлению запрашиваемой сущности
* Дополнительно по обновлению таблицы связей
*/
$aCacheTags = array(
$sEntityFull . '_save',
$sEntityFull . '_delete',
$sEntityJoin . '_save',
$sEntityJoin . '_delete'
);
$iCacheTime = 60 * 60 * 24; // todo: скорее лучше хранить в свойстве сущности, для возможности выборочного переопределения
/**
* Переопределяем из параметров
*/
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->GetItemsByJoinEntity($sEntityJoin, $sKeyJoin, $sRelationKey,
$aRelationValues, $aFilter, $sEntityFull);
$this->Cache_Set($aEntities, $sCacheKey, $aCacheTags, $iCacheTime);
}
}
/**
* Если запрашиваем постраничный список, то возвращаем сам список и общее количество записей
*/
if (isset($aFilter['#page'])) {
if (isset($aFilter['#cache'][0])) {
/**
* Задан собственный ключ для хранения кеша, поэтому нужно его сменить для передачи в GetCount*
* Добавляем префикс 'count_'
*/
$aFilter['#cache'][0] = 'count_' . $aFilter['#cache'][0];
}
return array(
'collection' => $aEntities,
'count' => $this->GetCountItemsByJoinEntity($sEntityJoin, $sKeyJoin, $sRelationKey,
$aRelationValues, $aFilter, $sEntityFull)
);
}
return $aEntities;
}
public function GetCountItemsByJoinEntity(
$sEntityJoin,
$sKeyJoin,
$sRelationKey,
$aRelationValues,
$aFilter,
$sEntityFull = null
) {
$sEntityFull = $this->_NormalizeEntityRootName($sEntityFull);
/**
* Кеширование
* Если параметр #cache указан и пуст, значит игнорируем кэширование для запроса
*/
if (array_key_exists('#cache', $aFilter) && !$aFilter['#cache']) {
$iCount = $this->oMapperORM->GetCountItemsByJoinEntity($sEntityJoin, $sKeyJoin, $sRelationKey,
$aRelationValues, $aFilter, $sEntityFull);
} else {
$sEntityJoin = $this->Plugin_GetRootDelegater('entity', $sEntityJoin);
$sCacheKey = 'count_items_by_join_entity_' . serialize(array(
$sEntityJoin,
$sKeyJoin,
$sRelationKey,
$aRelationValues,
$aFilter,
$sEntityFull
));
/**
* Формируем теги для сброса кеша
* Сброс идет по обновлению таблицы связей
*/
$aCacheTags = array($sEntityJoin . '_save', $sEntityJoin . '_delete');
$iCacheTime = 60 * 60 * 24; // todo: скорее лучше хранить в свойстве сущности, для возможности выборочного переопределения
/**
* Переопределяем из параметров
*/
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 === ($iCount = $this->Cache_Get($sCacheKey))) {
$iCount = $this->oMapperORM->GetCountItemsByJoinEntity($sEntityJoin, $sKeyJoin, $sRelationKey,
$aRelationValues, $aFilter, $sEntityFull);
$this->Cache_Set($iCount, $sCacheKey, $aCacheTags, $iCacheTime);
}
}
return $iCount;
}
/**
* Ставим хук на вызов неизвестного метода и считаем что хотели вызвать метод какого либо модуля.
* Также обрабатывает различные ORM методы сущности, например
* <pre>
* $oUser->Save();
* $oUser->Delete();
* </pre>
* И методы модуля ORM, например
* <pre>
* $this->User_getUserItemsByName('Claus');
* $this->User_getUserItemsAll();
* </pre>
* @see Engine::_CallModule
*
* @param string $sName Имя метода
* @param array $aArgs Аргументы
* @return mixed
*/
public function __call($sName, $aArgs)
{
$sNameUnderscore = func_underscore($sName);
if (preg_match("@^add([a-z]+)$@i", $sName, $aMatch)) {
return $this->_AddEntity($aArgs[0]);
}
if (preg_match("@^update([a-z]+)$@i", $sName, $aMatch)) {
return $this->_UpdateEntity($aArgs[0]);
}
if (preg_match("@^save([a-z]+)$@i", $sName, $aMatch)) {
return $this->_SaveEntity($aArgs[0]);
}
if (preg_match("@^delete([a-z]+)$@i", $sName, $aMatch) and !strpos($sNameUnderscore, 'items_by_filter')) {
return $this->_DeleteEntity($aArgs[0]);
}
if (preg_match("@^reload([a-z]+)$@i", $sName, $aMatch)) {
return $this->_ReloadEntity($aArgs[0]);
}
if (preg_match("@^showcolumnsfrom([a-z]+)$@i", $sName, $aMatch)) {
return $this->_ShowColumnsFrom($aArgs[0]);
}
if (preg_match("@^showprimaryindexfrom([a-z]+)$@i", $sName, $aMatch)) {
return $this->_ShowPrimaryIndexFrom($aArgs[0]);
}
if (preg_match("@^getchildrenof([a-z]+)$@i", $sName, $aMatch)) {
return $this->_GetChildrenOfEntity($aArgs[0]);
}
if (preg_match("@^getparentof([a-z]+)$@i", $sName, $aMatch)) {
return $this->_GetParentOfEntity($aArgs[0]);
}
if (preg_match("@^getdescendantsof([a-z]+)$@i", $sName, $aMatch)) {
return $this->_GetDescendantsOfEntity($aArgs[0]);
}
if (preg_match("@^getancestorsof([a-z]+)$@i", $sName, $aMatch)) {
return $this->_GetAncestorsOfEntity($aArgs[0]);
}
if (preg_match("@^loadtreeof([a-z]+)$@i", $sName, $aMatch)) {
$sEntityFull = array_key_exists(1, $aMatch) ? $aMatch[1] : null;
return $this->LoadTree(isset($aArgs[0]) ? $aArgs[0] : array(), $sEntityFull);
}
$iEntityPosEnd = 0;
if (strpos($sNameUnderscore, '_items') >= 3) {
$iEntityPosEnd = strpos($sNameUnderscore, '_items');
} else {
if (strpos($sNameUnderscore, '_by') >= 3) {
$iEntityPosEnd = strpos($sNameUnderscore, '_by');
} else {
if (strpos($sNameUnderscore, '_all') >= 3) {
$iEntityPosEnd = strpos($sNameUnderscore, '_all');
}
}
}
if ($iEntityPosEnd && $iEntityPosEnd > 4) {
$sEntityName = substr($sNameUnderscore, 4, $iEntityPosEnd - 4);
} else {
$sEntityName = func_underscore(Engine::GetModuleName($this)) . '_';
$sNameUnderscore = substr_replace($sNameUnderscore, $sEntityName, 4, 0);
$iEntityPosEnd = strlen($sEntityName) - 1 + 4;
}
$sNameUnderscore = substr_replace($sNameUnderscore, str_replace('_', '', $sEntityName), 4, $iEntityPosEnd - 4);
$sEntityName = func_camelize($sEntityName);
/**
* getMaxRatingFromUserByFilter() get_max_rating_from_user_by_filter
*/
if (preg_match("@^get_(max|min|avg|sum)_([a-z][_a-z0-9]*)_from_([a-z][_a-z0-9]*)_by_filter$@i",
func_underscore($sName), $aMatch)) {
return $this->GetAggregateFunctionByFilter($aMatch[1], $aMatch[2], isset($aArgs[0]) ? $aArgs[0] : array(),
func_camelize($aMatch[3]));
}
/**
* getMaxRatingFromUserByStatusAndActive() get_max_rating_from_user_by_status_and_active
*/
if (preg_match("@^get_(max|min|avg|sum)_([a-z][_a-z0-9]*)_from_([a-z][_a-z0-9]*)_by_([_a-z]+)$@i",
func_underscore($sName), $aMatch)) {
$aSearchParams = explode('_and_', $aMatch[4]);
$aSplit = array_chunk($aArgs, count($aSearchParams));
$aFilter = array_combine($aSearchParams, $aSplit[0]);
if (isset($aSplit[1][0])) {
$aFilter = array_merge($aFilter, $aSplit[1][0]);
}
return $this->GetAggregateFunctionByFilter($aMatch[1], $aMatch[2], $aFilter, func_camelize($aMatch[3]));
}
/**
* getCountFromUserByFilter() get_count_from_user_by_filter
*/
if (preg_match("@^get_count_from_([a-z][_a-z0-9]*)_by_filter$@i", func_underscore($sName), $aMatch)) {
return $this->GetCountItemsByFilter(isset($aArgs[0]) ? $aArgs[0] : array(), func_camelize($aMatch[1]));
}
/**
* getUserItemsByFilter() get_user_items_by_filter
*/
if (preg_match("@^get_([a-z]+)((_items)|())_by_filter$@i", $sNameUnderscore, $aMatch)) {
if ($aMatch[2] == '_items') {
return $this->GetItemsByFilter($aArgs[0], $sEntityName);
} else {
return $this->GetByFilter($aArgs[0], $sEntityName);
}
}
/**
* deleteUserItemsByFilter() delete_user_items_by_filter
*/
if (preg_match("@^delete_([a-z\_]+)_items_by_filter$@i", func_underscore($sName), $aMatch)) {
return $this->DeleteItemsByFilter(isset($aArgs[0]) ? $aArgs[0] : array(), func_camelize($aMatch[1]));
}
/**
* getUserItemsByArrayId() get_user_items_by_array_id
*/
if (preg_match("@^get_([a-z]+)_items_by_array_([_a-z]+)$@i", $sNameUnderscore, $aMatch)) {
return $this->GetItemsByArray(array($aMatch[2] => $aArgs[0]), $sEntityName);
}
/**
* getUserItemsByJoinEntity() get_user_items_by_join_entity
*/
if (preg_match("@^get_([a-z]+)_items_by_join_entity$@i", $sNameUnderscore, $aMatch)) {
return $this->GetItemsByJoinEntity($aArgs[0], $aArgs[1], $aArgs[2], $aArgs[3], $aArgs[4],
func_camelize($sEntityName));
}
/**
* getUserByLogin() get_user_by_login
* getUserByLoginAndMail() get_user_by_login_and_mail
* getUserItemsByName() get_user_items_by_name
* getUserItemsByNameAndActive() get_user_items_by_name_and_active
* getUserItemsByDateRegisterGte() get_user_items_by_date_register_gte (>=)
* getUserItemsByProfileNameLike() get_user_items_by_profile_name_like
* getUserItemsByCityIdIn() get_user_items_by_city_id_in
*/
if (preg_match("@^get_([a-z]+)((_items)|())_by_([_a-z]+)$@i", $sNameUnderscore, $aMatch)) {
$aAliases = array(
'_gte' => ' >=',
'_lte' => ' <=',
'_gt' => ' >',
'_lt' => ' <',
'_like' => ' LIKE',
'_in' => ' IN'
);
$sSearchParams = str_replace(array_keys($aAliases), array_values($aAliases), $aMatch[5]);
$aSearchParams = explode('_and_', $sSearchParams);
$aSplit = array_chunk($aArgs, count($aSearchParams));
$aFilter = array_combine($aSearchParams, $aSplit[0]);
if (isset($aSplit[1][0])) {
$aFilter = array_merge($aFilter, $aSplit[1][0]);
}
if ($aMatch[2] == '_items') {
return $this->GetItemsByFilter($aFilter, $sEntityName);
} else {
return $this->GetByFilter($aFilter, $sEntityName);
}
}
/**
* getUserAll() get_user_all OR
* getUserItemsAll() get_user_items_all
*/
if (preg_match("@^get_([a-z]+)_all$@i", $sNameUnderscore, $aMatch) ||
preg_match("@^get_([a-z]+)_items_all$@i", $sNameUnderscore, $aMatch)
) {
$aFilter = array();
if (isset($aArgs[0]) and is_array($aArgs[0])) {
$aFilter = $aArgs[0];
}
return $this->GetItemsByFilter($aFilter, $sEntityName);
}
return parent::__call($sName, $aArgs);
}
/**
* Построение дерева
*
* @param array $aItems Список сущностей
* @param array $aList
* @param int $iLevel Текущий уровень вложенности
* @return array
*/
static function buildTree($aItems, $aList = array(), $iLevel = 0)
{
if (!$aItems) {
return array();
}
foreach ($aItems as $oEntity) {
$aChildren = $oEntity->getChildren();
$bHasChildren = !empty($aChildren);
$sEntityId = $oEntity->_getDataOne($oEntity->_getPrimaryKey());
$aList[$sEntityId] = array(
'entity' => $oEntity,
'parent_id' => $oEntity->_getTreeParentKeyValue(),
'children_count' => $bHasChildren ? count($aChildren) : 0,
'level' => $iLevel,
);
if ($bHasChildren) {
$aList = self::buildTree($aChildren, $aList, $iLevel + 1);
}
}
return $aList;
}
/**
* Выполняет обновление связи many_to_many у сущности
*
* @param $oEntity
* @param $sRelationKey
*/
protected function _updateManyToManyRelation($oEntity, $sRelationKey)
{
$aRelations = $oEntity->_getRelations();
if (!isset($aRelations[$sRelationKey]['type']) or $aRelations[$sRelationKey]['type'] != EntityORM::RELATION_TYPE_MANY_TO_MANY) {
return;
}
$aFilterAdd = $aRelations[$sRelationKey]['filter'];
$oEntityRelation = Engine::GetEntity($aRelations[$sRelationKey]['join_entity']);
/**
* По сущности связи формируем запрос за получение списка сохраненых связей в БД
*/
$sCmd = Engine::GetPluginPrefix($aRelations[$sRelationKey]['join_entity']) . 'Module' . Engine::GetModuleName($aRelations[$sRelationKey]['join_entity']) . '_Get' . Engine::GetEntityName($aRelations[$sRelationKey]['join_entity']) . 'ItemsByFilter';
list($aFilter) = $this->oMapperORM->BuildFilter($aFilterAdd, $oEntityRelation);
$aDataInsert = $aFilter;
$aFilter['#index-from'] = $aRelations[$sRelationKey]['rel_key'];
$aFilter[$aRelations[$sRelationKey]['join_key']] = $oEntity->_getPrimaryKeyValue();
$aRelationItemsSaved = Engine::GetInstance()->_CallModule($sCmd, array($aFilter));
/**
* Получаем текущие связи из сущности
*/
$aTargetItemsCurrent = $oEntity->$sRelationKey->getCollection();
/**
* Удаляем связи, которых нет в текущих связях
*/
foreach ($aRelationItemsSaved as $k => $oRelationItem) {
if (!isset($aTargetItemsCurrent[$k])) {
$oRelationItem->Delete();
}
}
/**
* Создаем новые связи, которых нет в сохраненных
*/
foreach ($aTargetItemsCurrent as $k => $oTargetItem) {
if (!isset($aRelationItemsSaved[$k])) {
$oRelationNew = Engine::GetEntity($aRelations[$sRelationKey]['join_entity']);
$aDataInsert[$aRelations[$sRelationKey]['join_key']] = $oEntity->_getPrimaryKeyValue();
$aDataInsert[$aRelations[$sRelationKey]['rel_key']] = $oTargetItem->_getPrimaryKeyValue();
$oRelationNew->_setData($aDataInsert);
$oRelationNew->Add();
}
}
}
/**
* Выполняет удаление всех связей many_to_many сущности
*
* @param $oEntity
* @param $sRelationKey
*/
protected function _deleteManyToManyRelation($oEntity, $sRelationKey)
{
$aRelations = $oEntity->_getRelations();
if (!isset($aRelations[$sRelationKey]['type']) or $aRelations[$sRelationKey]['type'] != EntityORM::RELATION_TYPE_MANY_TO_MANY) {
return;
}
$aFilterAdd = $aRelations[$sRelationKey]['filter'];
$oEntityRelation = Engine::GetEntity($aRelations[$sRelationKey]['join_entity']);
/**
* По сущности связи формируем запрос за получение списка сохраненых связей в БД
*/
$sCmd = Engine::GetPluginPrefix($aRelations[$sRelationKey]['join_entity']) . 'Module' . Engine::GetModuleName($aRelations[$sRelationKey]['join_entity']) . '_Get' . Engine::GetEntityName($aRelations[$sRelationKey]['join_entity']) . 'ItemsByFilter';
list($aFilter) = $this->oMapperORM->BuildFilter($aFilterAdd, $oEntityRelation);
$aFilter[$aRelations[$sRelationKey]['join_key']] = $oEntity->_getPrimaryKeyValue();
$aRelationItemsSaved = Engine::GetInstance()->_CallModule($sCmd, array($aFilter));
foreach ($aRelationItemsSaved as $oRelation) {
$oRelation->Delete();
}
}
/**
* Приводит название сущности к единому формату полного имени класса
* Если используется наследование, то возвращается корневой класс
* $sEntity может содержать как короткое имя сущности (без плагина и модуля), так и полное
*
* @param string|object|null $sEntity
*
* @return string
*/
protected function _NormalizeEntityRootName($sEntity)
{
/**
* Если передан объект сущности, то просто возвращаем ее корневой класс
*/
if (is_object($sEntity)) {
return $this->Plugin_GetRootDelegater('entity', get_class($sEntity));
}
/**
* Разбиваем сущность на составляющие
*/
if (is_null($sEntity)) {
$sPluginPrefix = Engine::GetPluginPrefix($this);
$sModuleName = Engine::GetModuleName($this);
$sEntityName = Engine::GetEntityName($this) ?: $sModuleName;
} elseif (substr_count($sEntity, '_')) {
$sPluginPrefix = Engine::GetPluginPrefix($sEntity);
$sModuleName = Engine::GetModuleName($sEntity);
$sEntityName = Engine::GetEntityName($sEntity);
} else {
$sPluginPrefix = Engine::GetPluginPrefix($this);
$sModuleName = Engine::GetModuleName($this);
$sEntityName = ucfirst($sEntity);
}
/**
* Получаем корневой модуль
*/
$sModuleRoot = $this->Plugin_GetRootDelegater('module', $sPluginPrefix . 'Module' . $sModuleName);
/**
* Возвращаем корневую сущность
*/
return $this->Plugin_GetRootDelegater('entity', $sModuleRoot . '_Entity' . $sEntityName);
}
}