Многие ко многим — опасные связи

На этой неделе обновил CakePHP из SVN и тут же перестало работать добавление связей «многие-ко-многим» (hasAndBelongsToMany, HABTM). Небольшое расследование, сравнение изменений в коде и вопросы в гугл группе дали неутешительные результаты.

Во-первых у меня сложилось впечатление, что разработчики, несмотря на статус ReleaseCandidate (RC3) все еще не пришли к единому мнению относительно структуры данных, которую надо скармливать методу save(). :-/

Во-вторых естественные ключи в CakePHP практически «вне закона». Вот непонятно мне это — чем с точки зрения методов find(), save() и др. так сильно отличаются естественные ключи от синтетических? Тем более, что нечисловые первичные ключи все-таки поддерживаются. Это я про поля с UUID.

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

Как обычно, начал с поиска уже готовых решений и в блоге «Программируем на CakePHP» нашел код нужных методов. Работает даже лучше прежнего — передаем id записи из модели, массив id записей другой модели и все добавляется в связующую таблицу. Это, конечно, не «автомагия», но зато просто и понятно. :-)

Обратил внимание на некоторую неоптимальность в работе кода. Алгоритм метода добавления примерно такой:

  1. Как уже написал, получаем id записи и массив id связанных записей
  2. Проверяем есть-ли уже какие-нибудь пары id-id и удаляем их
  3. Добавляем связи

Мне это не очень понравилось — считаю, что один SELECT, лучше, чем 5 DELETE. :-) Поэтому немного переписал метод. Во-первых отказался от принудительного удаления уже существующих связей, просто удаляю из массива те id связанных записей, отношение с которыми уже есть. Во-вторых заменил серию INSERT’ов, по одному на каждую связь, на один общий INSERT. Получился вот такой код:

Метод удаления связей переделал похожим образом.

Автор

Сергей Родовниченко

Родился, учился, работал и все такое. Занимаюсь поддержкой сайтов на Shop-Script, Joomla, Wordpress, Prestashop. А также на самописных движках на базе CakePHP.

10 thoughts on “Многие ко многим — опасные связи”

  1. Доработка очень хорошая, спасибо. Если вы не против, хочу разместить у себя на блоге в разделе «сниппетов».

  2. Доработка очень хорошая, спасибо. Если вы не против, хочу разместить у себя на блоге в разделе «сниппетов».

  3. Размещайте, конечно. Еслиб я против был — в интернете не публиковал бы :-)

  4. Размещайте, конечно. Еслиб я против был — в интернете не публиковал бы :-)

  5. ох уж мне эта самодеятельность. считаете, что что-то поломалось — заводите тикет в багтрекере, знаете как починить — добавляйте патч там же, не знаете как починить — сделайте тест, чтобы показать что не работает.
    вот куда этот ваш код добавлять, в core? и что потом с ним делать при апдейте кэйка?

  6. ох уж мне эта самодеятельность. считаете, что что-то поломалось — заводите тикет в багтрекере, знаете как починить — добавляйте патч там же, не знаете как починить — сделайте тест, чтобы показать что не работает.
    вот куда этот ваш код добавлять, в core? и что потом с ним делать при апдейте кэйка?

  7. А смысл заводить патч, если авторы решили в RC3 поменять в очередной раз структуру данных для HABTM и это не баг, а фича?

    Зачем в core? Я его вообще не трогаю. Можете добавить по выбору:
    1. в свою модель.
    2. в AppModel
    3. написать behavior

  8. А смысл заводить патч, если авторы решили в RC3 поменять в очередной раз структуру данных для HABTM и это не баг, а фича?

    Зачем в core? Я его вообще не трогаю. Можете добавить по выбору:
    1. в свою модель.
    2. в AppModel
    3. написать behavior

  9. И еще вдогонку. Начиная с RC3 «автомагически» сохраняются связи только если первичные ключи связываемых моделей — типа INT. Или CHAR(36) (UUID). Все остальное игнорируется и не сохраняется. Т.е. естественный ключ типа VARCHAR(100) не сохранится в связующей таблице. И ответ на тикет будет простым — используйте синтетические целочисленные ключи.

  10. И еще вдогонку. Начиная с RC3 «автомагически» сохраняются связи только если первичные ключи связываемых моделей — типа INT. Или CHAR(36) (UUID). Все остальное игнорируется и не сохраняется. Т.е. естественный ключ типа VARCHAR(100) не сохранится в связующей таблице. И ответ на тикет будет простым — используйте синтетические целочисленные ключи.

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *