Doctrine: поля с большими текстовыми данными

stalxed

Новичок
Сейчас вошёл в некоторый ступор, сейчас опишу.
По сути доктрина может возвращать данные главным образом 3 способами:
1) Целые сущности
2) Частичные сущности(partial)
3) Обыкновенные массивы.
У меня есть множество различных сущностей, в которых по сути множество небольших полей, и 1-3 больших.
Например товар, в нём большое поле описание, там до 20к символов UTF-8. Остальные поля весьма небольшие - цена, краткое описание и прочее.
На странице, где список 25 товаров я могу:
1) использовать целые сущности, тогда в теории 20к символов * 25 товаров * 3 байта(вроде...) = 1.5мб памяти будет расходоваться просто так(так как поле описание в списке товаров не выводится)...
2) частичные сущности, выглядит идеальным решением
3) обыкновенные массивы, данные из бд придётся подвергать обработкам, для действий которые в сущностях реализованы GETинг методами, т.е. придётся повторять бизнес логику из сущностей. Это неприемлемо, лениво и велика возможность допустить много ошибок.

Казалось бы всё просто, 2 решение и всё в ажуре. Но не тут то было...
В шаблоне есть встраиваемые блоки, там лучшие товары, наиболее комментируемые и т.д.
Например
1) вначале мы получаем 10 лучших товаров, указав
PHP:
SELECT partial m.{id, title, slug}
2) затем получаем 10 наиболее комментируемых товаров, указав
PHP:
SELECT partial m.{id, title, slug, shortText}
Если в 10 наиболее комментируемых товаров окажется товар из 10 лучших товаров, то мы не получим значение поля shortText. Спасибо паттерну Identity Map...

Вижу два выхода из ситуации:
1) Так как все запросы для конкретной сущности в 1 классе репозитория, то можно сделать так
при написание любого запроса использовать специальный static метод getFields(), т.е. получится так:
PHP:
    private static $fields;

    public static function setFields($fields)
    {
          if (isset(self::$fields))
              throw new Exception();

          self::$fields = $fields;
    }

    public static function getFields()
    {
        if ( ! isset(self::$fields))
            return 'm';

        return self::$fields;
    }

    public function getList()
    {
        return $this->createQueryBuilder('m')
                    ->select($this->getFields())
                    ->orderBy('m.title', 'ASC')
                    ->getQuery()
                     >getResult();
    }
По умолчанию вернёт значение 'm', т.е. все поля, если его изменить , то блокировать эксепшеном дальнейшее его изменение. Т.е. его изменение сделать возможным только раз, а до его изменения - возвращаются полные сущности, т.е. данный метод ничего не разрушит и позволит избежать ситуации описанной выше.
2) Крупные текстовые поля вынести в отдельную таблицу. Сделать их однонаправленным OneToOne отношением. А старую сущность изменить так, чтобы её клиенты и не узнали этого:
PHP:
public function getText()
{
    return $this->textsOffer->getMain();
}
т.е. типичный прокси.
3) Наплевать на потребление ОЗУ...
Какое решение наиболее рационально? Или может есть другое?
 

Koc

Новичок
я сразу подумал о варианте номер 2 еще не дочитав задачу) Не уверен правда, что получится сделать однонаправленный OneToOne. Тогда сначала придется персистить текстовые поля, а потом остальные.

Еще можно вызывать em->refresh для 10 лучших товаров, но это +1 запрос на товар по ПК.

Или может быть поискать какой-нить QueryHint, который скажет что б не записывались в identity map сущности
 

stalxed

Новичок
http://www.doctrine-project.org/jira/browse/DDC-773

т.е. как вариант еще - деаттачить партиал-сущности
Большое спасибо! Этот лучший вариант при использование partial сущностей.

Но всё таки склоняюсь к отдельной таблице. Ибо мне не очень нравится подобное API:
PHP:
$this->getDoctrine()
    ->getRepository('Bundle:Offer')
    ->getLatestOffers(10);
Т.е. из этого кода сразу становится понятно, что возвращается 10 последних товаров, целых объектов. Но это неверно.
Можно исправить это переименованием метода:
PHP:
$this->getDoctrine()
    ->getRepository('Bundle:Offer')
    ->getLatestOffersPartially(10);
но и здесь непонятно, какие поля будут загружены, какие нет.
Потому наверное отдельная таблица лучший вариант.

Не уверен правда, что получится сделать однонаправленный OneToOne. Тогда сначала придется персистить текстовые поля, а потом остальные.
Порядок persist не играет совершенно никакой роли(он лишь меняет статус с NEW в MANAGED).
А cascade={"persist"} позволяет ещё больше упростить жизнь...
 
Сверху