Полиморфни връзки за най-малките

Наскоро, докато правех друга функция на един от проектите, попаднах на малко необичайни връзки в релационни СУБД, които, както се оказа по-късно, имат сложно име - Полиморфни отношения. Какво е това, как и къде да ги прилагате, ще се опитам да обясня в тази статия.

Темата за полиморфните релации вече е била повдигана повече от веднъж на Habré („Rails и полиморфни релации“, „Полиморфни асоциации от край до край в Ruby on Rails“, „Полиморфни релации“), но беше повдигната в контекста на Ruby и за тези, които вече имат известен опит в проектирането на бази данни. За начинаещи (аз бях) малко е ясно от тези статии, така че в тази статия ще се опитам да разкажа всичко на пръсти, абстрахирайки се от езика, с изключение на това, че ще докосна малко ORM на популярни уеб рамки. Всеки разбира обичайните "връзки" на таблиците в релационните бази данни: едно към едно, едно към много, много към много. И ако не разбирате, ето прости примери за тяхното прилагане.

Един към един. Един запис от първата таблица отговаря само на един запис от втората таблица. Тук всичко е просто. Най-често срещаният пример е таблицата user и user_profile (всеки потребител има един профил).

Много към много. Връзка се реализира, когато един ред от една таблица може да съответства на няколко записа от друга и обратно. Добър пример е, че има таблица със статии (статии), има таблица с тагове (тагове), те са свързани чрез междинна таблица (пивот таблица или съединителна таблица) tags_articles, която има article_id, tag_id. Изглежда, че тук всичко е просто и ясно.

Откъде идват някои полиморфни връзки, ако предишните връзки са съвсем логични и, така да се каже, не изискват допълнения?

Предишни релации (един към един, един към много, много към много) се създават за статичниобекти от таблици, на които можете да налагате ограничения (ограничения), предоставени от СУБД.

Нека се върнем към примера за връзката "един към много".

Същността на полиморфните отношения

Полиморфните релации са динамични релации между таблици, използващи тип обект. За да стане ясно, нека променим малко нашите таблици и да направим полиморфни връзки между тях.

Другата ни маса е новина:

Полиморфните връзки могат да бъдат реализирани като много към много.Няма смисъл да показвам таблици с данни, ще покажа само приблизителна структура. статии: id - цяло число text - текст

публикации: id - цяло число text - текст

тагове: id - цяло число име - низ

tags_entities tag_id - цяло число tag_entity_id - цяло число tag_entity_type - низ (postarticle)

Минуси на полиморфните връзки

Не всичко е толкова перфектно, колкото може да изглежда на пръв поглед. Поради динамичния характер на полиморфните връзки между полетата на свързаните таблици, е невъзможно да се запишат външни ключови връзки (чужд ключ) с помощта на СУБД и още повече ограничения (ограничения) за промяна или изтриване на записи. Това всъщност е най-големият недостатък на полиморфните връзки. Ще трябва или да напишете свои собствени тригери (процедури или нещо друго) за самата СУБД, или, което се прави по-често, да прехвърлите работата по синхронизирането на низове и налагането на ограничения между таблиците в ORM и езика за програмиране.

Второто, вече по-малко значимо минус на полиморфните връзки е типът на обекта. Необходимо е по някакъв начин да се опише какъв тип към коя таблица принадлежи. Това може да не е очевидно, ако например името на някоя таблица се е променило или ако сте задали типа обект на числа. Можете да разрешите този проблем, например, като създадете отделна таблица или като напишете в кода на проекта асоциативенмасив със съпоставяне тип към обект.

Работа с ORM с полиморфни връзки

Трябва да се каже, че съвременните рамки и техният ORM могат да работят с тези връзки без много затруднения. Например, както бе споменато по-горе, Ruby on Rails ги поддържа веднага. PHP рамката на Laravel също има удобни методи за такива типове релации в своята ORM реализация (morphTo, morphMany и т.н.), а като тип entity използва пълното име на моделния клас. В рамката на Yii2 няма специфични методи за такива релации, но те могат да бъдат внедрени чрез обичайните методи hasOne, hasMany с допълнителни условия при посочване на релации.

От всичко по-горе, начинаещите трябва да обърнат внимание кога да използват полиморфни връзки. Не бива да ги бутате надясно и наляво, от проект на проект, само защото е готино. Необходимо е да прецените малко предварително и дали утре ще се появят нови таблици, нови обекти със същата функционалност и изисквания, които могат да бъдат извадени и динамични и въз основа на отговора да проектирате вашите бази данни.

И тук можете да получите грант за тестов период на Yandex.Cloud. Необходимо е само да въведете "Habr" в полето "секретна парола".