Ошибка не нахождения таблицы при наследовании модуля плагина работающего на орм и с указанием кастомных имен таблиц через конфиг

Баги орм все ещё держатся за ручки и легально водят хороводы вместе с хаками! В этой истории все замечательно, особенно то, что в связи с тем, что по-умолчанию таблицы орм для плагинов в лс 2.0 будут именоваться по-умолчанию не так как в 1.0.3 (ура), а с именем префикса плагина, то этот баг для сложных проектов был бы очень веселой штукой.

Условия задачи

Наследовать орм-модуль плагина, который дает свои имена таблицам для каждой сущности (через ). Чтобы было более понятно, то пример будет такой: есть плагин A с орм модулем и сущностью:

PluginA_ModuleA
PluginA_ModuleA_EntityA

и есть плагин B, который наследует только модуль плагина A:

PluginB_ModuleB -> PluginA_ModuleA

и для плагина A в конфиге указаны кастомные имена таблиц для сущностей.

Поехали

В орм в методах таких как GetByFilter, GetItemsByFilter и других есть конструкции:

if (is_null($sEntityFull)) {
  $sEntityFull=Engine::GetPluginPrefix($this).'Module'.Engine::GetModuleName($this).'_Entity'.Engine::GetModuleName(get_class($this));
} elseif (!substr_count($sEntityFull,'_')) {
  $sEntityFull=Engine::GetPluginPrefix($this).'Module'.Engine::GetModuleName($this).'_Entity'.$sEntityFull;
}

Нетрудно догадаться что при наследовании модуля плагина А переменная $sEntityFull получит префикс плагина В, который наследует исходный и в итоге будет получена сущность, которая реально не существует вообще — PluginB_ModuleB_EntityA. Далее эта переменная передается в маппер, где происходит получение этой не существующей сущности:

$oEntitySample=Engine::GetEntity($sEntityFull);

И тут вполне ожидаемо было бы ждать ексепшн, т.к. сущности такой нет, это ошибка, но тут работает самый давний, самый натуральный что ни есть хак, на который я всегда косо смотрел:

/**
 * If Plugin Entity doesn't exist, search among it's Module delegates
 */
if(isset($sPlugin) && !self::GetClassPath($sClass)) {
  $aModulesChain = Engine::GetInstance()->Plugin_GetDelegationChain('module','Plugin'.$sPlugin.'_Module'.$sModule);
  foreach($aModulesChain as $sModuleName) {
    $sClassTest=$sModuleName.'_Entity'.$sEntity;
    if(self::GetClassPath($sClassTest)) {
      $sClass=$sClassTest;
      break;
    }
  }
  if(!self::GetClassPath($sClass)) {
    $sClass='Module'.$sModule.'_Entity'.$sEntity;
  }
}

И ядро находит таки сущность среди сущностей делегируемых модулей плагина. Уже на данном этапе это проблема в архитектуре, которая должна быть решена, это неверное поведение.

Далее вызов обратно возвращается в орм маппер, где идет получение таблицы, но не с полученной корректной сущности от ядра, а снова от её (ошибочного) имени:

$sTableName = self::GetTableName($sEntityFull);

естественно метод GetTableName разбирает имя на составляющие и не найдет таблицу исходного плагина A для орм модуля, т.к. будет искать с именем плагина B, который наследует оригинальный A и работа завершится ошибкой не нахождения таблицы для плагина А.

Варианты решения

1. Заменить в orm маппере ядра все вызовы

$sTableName = self::GetTableName($sEntityFull);

на полученную корректную сущность

$sTableName = self::GetTableName($oEntitySample);

это как минимум должно быть сделано в движке для новой версии лс. Примечательно что в некоторых методах именно так и сделано, например, в методе получения полей таблицы ShowColumnsFrom, получения примари ключа ShowPrimaryIndexFrom или просто добавления/обновления сущностей, но там изначально передается в методы сама сущность.

2. Сделать правку выше + исправить все места формирования сущности таким образом, чтобы избавиться от вышеуказанного хака в движке и вынести формирование сущности:

if (is_null($sEntityFull)) {
  $sEntityFull=Engine::GetPluginPrefix($this).'Module'.Engine::GetModuleName($this).'_Entity'.Engine::GetModuleName(get_class($this));
} elseif (!substr_count($sEntityFull,'_')) {
  $sEntityFull=Engine::GetPluginPrefix($this).'Module'.Engine::GetModuleName($this).'_Entity'.$sEntityFull;
}

в методах орм в отдельный метод, который и получит корректную сущность, даже если он будет содержать тот же код хака из ядра, но это будет значительно лучше т.к. дальше по коду в методах орм (например, все тот же GetItemsByFilter) идет сессионный кеш:

$sEntityFullRoot=$this->Plugin_GetRootDelegater('entity',$sEntityFull);
$sCacheKey=$sEntityFullRoot.'_items_by_filter_'.serialize($aFilter);
$aCacheTags=array($sEntityFullRoot.'_save',$sEntityFullRoot.'_delete');

и он строится на неверном имени сущности и ключ и теги кеша получаются тоже не корректные (GetRootDelegater вернет то же имя т.к. нету такой передаваемой сущности и нет, соответственно, исходной).

3. А разработчикам придется писать в своих плагинах обходной временной хак с маппингом имени таблицы для каждой сущности оригинального плагина (А) на имя плагина-наследника (В):

$config['$root$']['db']['table']['ПлагинB_МодульB_СущностьА'] = '___db.table.ПлагинА_МодульА_СущностьА___';
0 комментариев
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.