Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Чим погані поліморфні зв'язки з погляду бази даних і які є альтернативи?

Поліморфний зв'язок - одна колонка посилається на рядки різних таблиць, а друга колонка каже, на яку:

comments: id | commentable_type | commentable_id | body
          1  | App\Models\Post  | 42             | ...
          2  | App\Models\Video | 42             | ...

У Laravel це morphTo / morphMany - зручно: один механізм коментарів, тегів, вкладень для будь-яких моделей.

Проблеми з погляду бази:

  • Немає зовнішнього ключа. База не може гарантувати, що commentable_id = 42 справді існує в таблиці, зазначеній у commentable_type. Видалили пост - коментарі лишилися «сиротами»; каскадне видалення - лише кодом.
  • Тип - рядок з іменем класу застосунку. Перейменували модель чи простір імен - дані в базі зламалися (у Laravel рятує Relation::enforceMorphMap() з короткими псевдонімами).
  • З'єднання незручні: не можна зробити один JOIN - лише окремі для кожного типу, UNION чи CASE.
  • Індекс потрібен складений (commentable_type, commentable_id), і він більший через рядок типу.

Альтернативи:

1. Окремі колонки на кожен тип з перевіркою, що заповнена рівно одна:

CREATE TABLE comments (
    id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    post_id  bigint REFERENCES posts (id) ON DELETE CASCADE,
    video_id bigint REFERENCES videos (id) ON DELETE CASCADE,
    body text NOT NULL,
    CHECK (num_nonnulls(post_id, video_id) = 1)
);

Справжні зовнішні ключі й каскади. Підходить, коли типів кілька й їх перелік стабільний.

2. Окремі проміжні таблиці: post_comments, video_comments - сам коментар без зв'язку, а зв'язок - у таблиці для кожного типу.

3. Спільний «батьківський» запис: таблиця commentable_entities, на яку посилаються і пости, і відео, а коментарі посилаються на неї. Елегантно, але складніше.

Коли поліморфні зв'язки нормальні: «легкі» універсальні речі (теги, лайки, журнал активності), де цілісність не критична, а типів багато й їх кількість зростає. Для критичних даних (платежі, документи) - краще варіанти зі справжніми ключами.

Докладніше в документації: Обмеження

Схожі питання