К основному контенту

Модели в CakePHP

Беда какая-то с реализацией моделей в Cake. Не то, чтобы совсем уж плохо, или не то, чтобы их совсем нет, как в Zend Framework, но реализация пока хромает.

Особенно с рекурсией туго. Т.е. если 1 уровень рекурсии, то все хорошо, но если нужно 2 уровня, то уже плохо.

Например, есть у нас модель "Лекарство", связанная с моделями "Производитель" и "Цена", а "Цена" связана с моделью "Валюта". Связи примерно такие:

  • Лекарство belongsTo Производитель
  • Производитель hasMany Лекарство
  • Лекарство hasOne Цена
  • Цена belongsTo Валюта

Обратная связь от Производителя — чтоб можно было получить все продукты, ему принадлежащие.

Однако при попытке получить данные о лекарстве получается полная фигня — либо, при recursive=1 не получаем Валюту, либо при recursive=2 получаем на втором уровне все продукты от Производитель.

Для этого есть несколько решений. как для версии 1.1, так и 1.2.

Чаще всего предлагается сделать unbindModel для ненужных связей, в данном случае $this->Лекарство->Производитель->unbindModel(array(hasMany=>array('Лекарство'))). У этого способа есть неприятная особенность, при добавлении новых связей к другим моделям, придется снова залезать в код контроллера и убирать "лишние" связи.

Обратный способ — создавать связи "на лету". Т.е. перед использованием вызывать bindModel, а в описании модели эти связи не хранить. Этот способ лучше, но более трудоемкий и тоже неправильный. Изначально модель должна все-таки "знать" о своих связях :-)

Для версии 1.1, в свое время предлагалось расширить функционал базового класса Model (AppModel) методом expects, в котором можно было бы указывать только те модели, которые нужны для запроса. Код этого метода можно найти либо в Bakery, либо в гугловской группе. Expects, наверняка должен работать и в 1.2, но я не пробовал. Потому, что появился еще один метод.

В 1.2 народ начал активно использовать различные Model behaviors, и ThinkingPHP предложил свой behavior для нашей проблемы. По-моему это на сегодняшний день наилучший вариант. Использование Containable behaivior сильно упрощает жизнь и на сегодняшний день это самый верный Cake way.

Теперь в AppModel надо добавить строчку:


class AppModel extends Model {
 var $actsAs = array('Containable');
}

После этого, перед вызовом метода findAll или любого другого, достаточно указать какие модели или даже отдельные столбцы моделей нужно использовать. Containable behavior сам вызовет нужные методы unbind для тех моделей, которые не пригодились.

Вот например вот так можно перечислить модели, связь с которыми нужна для модели User:


$this->User->contain(
 'UserType',
 'Group.GroupPermission',
 'Post.Category',
 'Post.RelatedPost',
 'Post.Comment.User');

Или даже указать отдельные столбцы (заодно обратите внимание на различные варианты возможного синтаксиса):


$this->User->contain(
 array('Group' =>
  array('fields' => array('id'))));

$this->User->contain(
 array('Group.fields' => array('id'))); 

$this->User->contain(array( 'Group' => array('id'))); $this->User->contain('Group.fields.id');

Все это отлично, если бы не одно "но". Эта замечательная штука не работает с pagination. Дело в том, что pagination выполняет 2 запроса — первый COUNT(*) для подсчета общего числа строк, второй — собственно выборка. А unbindModel, который вызывается из contain работает только на 1 запрос, если ем явно не указать обратного, передав значение false в качестве второго параметра. Решение этой проблемы предложено в комментариях к посту с behaviour. В код behaviour надо добавить свойство, прям в сам класс:


var $reset=true;

также добавить метод для установления этого свойства:


function setContainReset($reset) {
 $this->reset = $reset;

}

а строчку с вызовом unbindModel:


$assocModel->unbindModel($unbind);

поменять на:


$assocModel->unbindModel($unbind, $this->reset);

Voila, теперь перед вызовом $this-Model->contain(...) достаточно вызвать $this->Model->setContainReset(false) чтобы запретить восстанавливать связи после вызова COUNT в pagination.

Остается, однако, еще проблема большого числа запросов при $recursive > 1. Я ее коряво, но решил.

Популярные сообщения из этого блога

Sitemap для Shop-Script

С недавних пор наш национальный поисковик присоединился к славной компании поисковиков, умеющих обрабатывать файлы sitemap . Наверное, это связано с какими-то общеланетарными амбициями. Как бы то ни было, теперь есть возможность скормить Яндексу файл(ы) sitemap . Я решил, что обрабатывать сайт какой-нибудь сторонней приблудой много дольше, чем просто сгенерировать файлы sitemap с помощью скрипта. Тем более, что у shop-script в общем-то довольно простая структура. Новая версия Shop-Script с ЧПУ пока недоступна, получу — модифицирую скрипт. :-) Скрипт простенький, делает sitemap для aux-страниц — это у shop-script так называются обычные статические html страницы; страниц с категориями. Без разбивки на страницы 1, 2 и т.д. на мой взгляд эти страницы не слишком важны страниц с товарами и обсуждениями товаров Можно запускать из браузера, можно по cron’у. Корневую страницу сайта, страницу с новостями, страницу с формой обратной связи и т.п. надо записать в отдельный файл sitemap ...

Набережные

На бывшей доске почета, а ныне просто небольшой фотовыставке достижений Московского Судостроительного и Судоремонтного Завода (МССЗ) увидел пару интересных фотографий продукции этого завода на фоне набережных Москвы. Вот только сами набережные и годы съемки определить не смог. :) Знатоки подсказывают: первая фотография: Гончарная набережная у м.Таганская ; вторая: Фрунзенская набережная дом 34-32-26 .

Зеленые облака

Сегодня часов около 12 дня бежал по делам рядом со станцией метро Юго-Западная и обратил внимание на странный цвет неба. Поскольку прямо по курсу, километрах в трех-четырех находится железнодорожная станция "Очаково", решил, что там авария какая-нибудь. "Хлор или иприт" - внезапно промелькнула мысль. Странно, уроки НВП я, большей частью, прогуливал... Позже в Интернете прочел, что это сильный ветер поднял в воздух большое количество пыльцы. Сделал фотографию на планшет, убедился, что получилось плохо - цвета почти видно, и, так как торопился, не стал дальше заморачиваться. Вот сейчас еду в метро домой, попробовал немного задрать контраст и насыщенность. Может, получится чего.