RSS Мои друзья Контакты

Symfony 1.4 - мирим I18n и Searchable Doctrine 1.2 шаблоны

Большинство программистов, кто работал с Symfony 1.4 и Doctrine 1.2, наверняка использовали 2 стандартных шаблона для моделей: Searchable и I18n. Но к сожалению (из официальных источников) существует баг в Doctrine, который не позволяет использовать вместе эти шаблоны, т.е. нельзя применить первый ко второму используя actAs директиву в конфигурации schema.yml.

Вот ссылка на баг в Doctrine и в комментариях видим:

this is a known issue with the behaviors and it is a bigger problem that can't be fixed. Some behaviors just won't work together. 

Это означает, что разработчики не могут придумать нормального решения, чтобы исправить баг.

Идеология

Как говорится на нет и суда нет. Нельзя ну и пусть, не будем спорить с авторами такого гибкого и мощного ORM. Вместо этого

CmsContent:
  actAs:
    I18n:
      fields:
        - title
        - description
      actAs:
        Searchable:
          fields: [title]

напишем

CmsContent:
  actAs:
    I18n:
      fields:
        - title
        - description
    Searchable:
      fields: [title_ru, title_en]

т.е. применяем оба шаблона к модели CmsContent.

Копаемся в коде

От источника проблемы мы ушли, теперь осталось реализовать поддержку для нашей идеи. Для этого придется погрепать Doctrine_Template_Searchable

class Doctrine_Template_Searchable extends Doctrine_Template
{
    public function __construct(array $options = array())
    {
          parent::__construct($options);
          $this->_plugin = new Doctrine_Search($this->_options);
    }

    // another lines
}

Видим по коду, что шаблон Searchable использует в качестве плагина (т.е. того кто делает основную работу) объект класса Doctrine_Search. Теперь давайте посмотрим на листенер, который вызывается на postUpdate модели CmsContent для создания поискового индекса

class Doctrine_Search_Listener extends Doctrine_Record_Listener
{
    public function postUpdate(Doctrine_Event $event)
    {
        $record = $event->getInvoker();
        $this->_search->updateIndex($record->toArray());
    }
   
    // some another lines
}

Проанализировав код видим, что в метод updateIndex передается ассоциативный массив данных модели и эти знания очень важны.

Решение

Осталась маленькая проблемка. Мы указали (в файле конфигурации schema.yml), что поиск нужно проводить основываясь на поле title модели CmsContent, но это поле хранится в таблице Translation... Для того чтобы это подкорректировать перепишем метод toArray

class CmsContent extends BaseCmsContent {
    /**
     * Fix for combining Doctrine_Searchable & Doctrine_I18n
     */
    public function toArray($deep = true, $prefixKey = false) {
        $data = parent::toArray($deep, $prefixKey);

        $table = $this->getTable();
        if ($table->hasTemplate('Searchable') && $table->hasTemplate('I18n')) {
            $fields = $table->getTemplate('Searchable')->getOption('fields');
            // get langs from somewhere
            $langs  = array('ru', 'en');
            foreach ($langs as $lang) {
                foreach ($fields as $field) {
                    if (isset($data['Translation'][$lang][$field])) {
                        $data[$field . '_' . $lang] = $data['Translation'][$lang][$field];
                    }
                }
            }
        }
        return $data;
    }

    // another lines
}

Т.е. смотрим если наша таблица имеет и Searchable, и I18n шаблоны, то перепишем поля (которые отвечает за поисковый индекс) из таблицы переводов в массив $data. Теперь наш индекс создается и обновляется.

P.S.: знаю это костиль, но все же лучше чем ничего

Добавить комментарий

Комментариев: 1