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

Составные первичные индексы и CakePHP

Общеизвестный факт: CakePHP не поддерживает составные первичные индексы. Разработчики предлагают добавить в качестве первичного индекса поле id, а составной индекс из нескольких полей просто сделать уникальным.

Это, в общем-то, разумно — первичный индекс используется в моделях для установления и проверки связей, поэтому добавление процедур для работы с составными индексами неплохо увеличит объем кода, что, в свою очередь, скажется на производительности. Более того, использование составных индексов в качестве первичных — редкое явление, хотя я сходу придумал пару ситуаций когда это оправданно.

Схема с добавлением еще одного поля, предлагаемая разработчиками Cake неплохо функционирует, однако контроль за уникальностью составного индекса перекладывается на веб-разработчика.

Для определения, вставляется-ли новая запись (INSERT…) или обновляется старая (UPDATE…) при вызове из метода save(), Cake поступает очень просто: продеряет наличие в элемента массива $this->data[‘Model’][‘id’] и, если он есть, проверяет наличие записи в БД с таким ID. Если запись есть, значит UPDATE, если нет – INSERT.

В своем контроллере, который работает с моделью с составным индексом я сначала искал id с помощью Model->field() и, если находил, присваивал его $data[‘Model’]'[‘id’], после чего Cake искренне считал, что запись надо обновить, а не добавить. Это не совсем Cake Way — не дело контроллера определять, режимы записи данных.

Никаких указаний на то, как влиять на выбот UPDATE/INSERT, в учебнике по CakePHP я не нашел. Тяжело вздохнув решил сначала переопределить метод save(), добавив в него код поиска поля ID по моему уникальному индексу, а потом вызывать родительский метод save. В процессе разгладывания API, в частности кода метода save, обнаружил, что save() вызывает метод getID, который и ищет id записи во всех вариантах ее указания.

Тогда я и добрался до этого самого метода exists в cake/model.php. Нужный мне кусок кода выглядел вот так:

if ($this->getID() === false || $this->useTable === false) {
    return false;
}

В коде своей модели я переопределил этот метод, скопировав его весь, а вышеуказанные строчки заменил на такие:

if ($this->useTable === false) { //Модель без таблицы
    return false;
}

/* Три поля таблицы supplier_id, supplier_param1 и supplier_param2
 * это у меня как раз и есть уникальный индекс
 */
if ($this->getID() === false) // $this->id не указан
{

  $id = $this->field('id', array(
  $this->alias . '.supplier_id'=>
    $this->data[$this->alias]['supplier_id'],

  $this->alias . '.supplier_param1'=>
    $this->data[$this->alias]['supplier_param1'],

  $this->alias . '.supplier_param2'=>
    (string)$this->data[$this->alias]['supplier_param2']));

  if ($id !== false) { // Удачно получили id
    $this->data[$this->alias]['id']=$id;
    $this->id = $id;
    $this->__exists = true;
    return true;
}

    return false;
}

Т.е. фактически вставил выполнение запроса на получение id записи в этот метод. Теперь я из контроллера, как и положено, просто вызываю Model->save() и никаких проблем с ошибками про уникальный индекс нет. :-)

Обидно, что в документации на CakePHP никаких упоминаний про этот самый волшебный метод exists я не нашел, он упоминается только в перечислении методов класса в описании API.

Пока писал пост, подумалось: в принципе, можно сделать и более общий код — в модель добавить свойство-массив, с перечислением полей уникального индекса, а в этом коде в запрос подставлять имена полей из этого массива.

Совсем избавиться от поля ID, видимо, не удастся, на его значение, а также просто наличие много чего завязано.

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

Sitemap для Shop-Script

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

Набережные

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

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

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