Laravel & MySQL объединяют таблицы с полиморфной ассоциацией - PullRequest
0 голосов
/ 22 октября 2018

Обновление: Следующая схема является просто упрощенным примером одного модуля (комментариев), который должен иметь полиморфное отношение к другим модулям.Приложение имеет много модулей (более 30), и пользователи могут добавлять новые модули через администратора.Следовательно, каждый модуль может быть «связан» с другим модулем, но мы не знаем имен модулей и имен таблиц во время кодирования.Поэтому нам нужны полиморфные отношения.

Я пытаюсь выполнить соединение с динамическими именами таблиц с использованием полиморфной ассоциации.

Предполагая, что следующие таблицы MySQL

modules table
module_id | name    | table_name
1         | Quote   | quotes
2         | Order   | orders
3         | Product | products

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

quotes table
id | name
22 | "Quote #Q-22"
23 | "Quote #Q-23"

orders table
id  | name
122 | "Order #O-122"
123 | "Order #O-123"

products table
id  | name
55 | "Product #P-55"
56 | "Product #P-56"

comments table
id | module_id | record_id | text
1  | 1         | 23        | hello
2  | 2         | 122       | big
3  | 3         | 55        | world

Я хочу запустить запрос, который будет возвращать следующие строки:

comments
id | name          | text
1  | Quote #Q-23   | hello
2  | Order #O-122  | big
3  | Product #P-55 | world

Laravel позволяет полиморфные отношения путем сохранения имени класса ORM или имени таблицыпрямо на родительской таблице.

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

  1. Значительные накладные расходы.Для 1000 строк будет создан красноречивый объект для каждого.Мне нравится Eloquent, но его производительность намного ниже идеальной, когда дело доходит до извлечения большого количества строк.Query Builder больше подходит для этого, но не поддерживает полиморфизм.
  2. Поскольку связь выполняется на уровне Eloquent, я думаю, что это потребует дополнительного запроса.Я не проверял это, но, в противном случае, как Eloquent узнает, к каким таблицам присоединиться?Я буду рад ошибиться в этом.
  3. Это также может иногда приводить к другим проблемам обслуживания, таким как обновление имен таблиц или изменение имени / пространства имен класса ORM (хотя есть решения для этого).
  4. Это обходит ограничения внешнего ключа.

Мне вообще не нравится идея связывать имя таблицы или модель напрямую с таблицей записей.Другой важный (по крайней мере для меня) недостаток этого подхода заключается в том, что он тесно связывает схему базы данных с приложением.Или, другими словами, опирается на «магию» Eloquent.Если в какой-то момент мы захотим использовать Node.js, Python или GoLang (в настоящее время мы реализуем архитектуру микросервисов), мы, вероятно, не сможем его использовать и придется реструктурировать код и базу данных.

Я прочитал несколько других статей и вопросов, таких как этот или этот .

Один из подходов, который я имел в виду, - это использовать функции MySQL или хранимые процедуры.чтобы получить имя таблицы динамически и присоединиться к результату.Мне удалось использовать хранимые процедуры и функции для динамического получения имени таблицы, но, кажется, невозможно присоединить результат хранимой процедуры.Функции, похоже, не решают и эту проблему.

Второй подход состоял в том, чтобы сохранить имена записей в отдельной таблице.Это не работает для получения всей записи (Цитата, Заказ и т. Д.), Но так как мне нужно было только имя записи, она работает нормально (с правильной индексацией, конечно).

record_name table
module_id | record_id | name
1         | 23        | Quote #Q-23
2         | 122       | Order #O-122
3         | 55        | Product #P-55

Этот подход имеетНесколько недостатков - во-первых, он работает только для получения выбранных ключей (имя в данном случае).Вторым является то, что он дублирует данные и нарушает другие принципы базы данных.И, наконец, требуется обновление для обновления таблицы имя_модуля.

Поэтому у меня следующие вопросы: *

  1. Существуют ли альтернативные способы моделирования этой взаимосвязи на уровне базы данных (MySQL) или это нужно делать на уровне приложений?
  2. Может ли хранимая процедура или функция MySQL решить эту проблему?

1 Ответ

0 голосов
/ 22 октября 2018

Ваша схема неверна, и вы заметите, что когда вы проверяете ограничение внешнего ключа comments.record_id.

, исправьте ограничения внешнего ключа, и в результате должно получиться что-то вроде этого:

comments table
id | quotes_id | orders_id | products_id | text
1  | 22        | null      | null        | Text comment
2  | null      | 122       | null        | Text comment
3  | null      | null      | 55          | Text comment

Правильная схема базы данных (отношение) важнее, чем магия Laravel / Eloquent.На самом деле, с меньшим количеством магии легче и лучше работать.Вам также следует подумать о том, чтобы вообще не использовать подобную абстракцию базы данных, это не облегчает ситуацию и только создает проблемы.

Другой, может быть, даже лучший подход будет использовать таблицы отношений:

- comments table
id | text
1  | hello
2  | world

- quote_has_comment table
quote_id | comment_id
22       | 1
(with constraint quote_id+comment_id beeing unique

)

...