Реализация изменения записи в таблице связи с составным primary key?
Столкнулся с вроде бы стандартной ситуацией, но не могу найти некостыльное решение (или может просто я такой нежный). Ощущение, что всё очень просто и элементарно решается, а я недалекий орангутанг, который ничего не знает.
Есть две таблицы A и B, первичные ключи id — стандартные суррогатные эти самые INT NOT NULL AUTO_INCREMENT PRIMA…
Для реализации между ними связи «много ко многим» есть таблица связи A_B, состоящая из двух полей: a_id и b_id — внешних ключей таблиц A и B соответственно. Первичный ключ, что вроде логично, состоит из этой пары внешних ключей.
Есть пхпбыдлокодерская админка управления базой данных. В частности, страница редактирования связей между A и B. Если более конкретно, форма редактирования всех записей таблицы A_B с некоторым определенным a_id. В итоге получается около 20 записей на страницу, всё редактируется через инпуты с общим сабмитом.
ПРОБЛЕМА: php-скрипт, который обрабатывает форму по сабмиту, получает измененные связи и не знает, какими они были до редактирования, т.к. ключи, по которому они идентифицировались, изменились. То есть если пользователь меняет | a_id | b1_id | на | a_id | b2_id |, то скрипт знает, на что менять, но не знает, что изначально это была | a_id | b1_id |.
Я крепко задумался, нашел некоторые выходы, но они меня не устраивают:
1) хранить в форме hidden-поля с исходными значениями связей, отправлять их по сабмиту и по ним определять изменяемую запись БД. Не нравится: ощущение костыля; вопросы безопасности, хотя они, вообще говоря, волнуют меньше всего.
2) добавить в таблицу связи еще одно поле — суррогатный неменяемый id, по которому можно идентифицировать измененную связь. Как и в предыдущем пункте, хранить его в hidden-поле формы. Не нравится: см. предыдущий пункт + гнев богов реляционных моделей данных; необходимость лапатить структуру бд ради такой мелочи.
3) изменение = удаление + добавление. Зачищать все 20 записей и добавлять заново. Не нравится: ощущение костыля; удаление и добавление множества записей ради изменения единственной (это в данном случае 20, в другом будут все 120)
перед сохранением достаём из базы то что есть по ключу a_id и
сравниваем с тем что пришло от пользователя. Используя array_diff() и прочие ухищрение находим те строки, которые нужно удалить и те которые нужно добавить. Если их кол-во одинаковое (если форма подразумевает функции обновления то так оно и будет), то можно заменить одним запросом на update.
Усложняется логика, но нет массового удаление как вы описали в пункте 3)
Второй и третий варианты вполне жизненны.
Не написали, что за БД. Самое простое — поставить каскадное изменение (если есть). Или сделать каскад через триггеры (если есть).
Но если малой кровью, то третий вариант не плох, можно смело использовать.
Первый вариант совсем не годится.
MySQL MyISAM.
Про каскадное изменение не понятно: всегда думал, что оно для изменения значений ссылающихся по внешнему ключу полей при изменении «отца». Тут же изменение касается только одной таблицы связи.
не так понял, действительно каскад здесь ничем не поможет.
я бы вариант 3 сделал как самый простой, замедления бояться не стоит, по крайней мере до теста производительности
и почему же гнев на вариант 2) если он так и напрашивается постановкой задачи. Да, это уже будет не 3-я нормальная форма, но Вам от этого хуже не будет. А обновлять подобные строки станет очень удобно. Главное не забыть сделать пару a_id, b_id уникальной
1) совсем костыльный вариант
3) если много данных будет медленно работать
избыточность данных в варианте 2) скорее всего наименьшее из трех зол