Folks,
В течение n-го раза подряд я снова повторяю старую проблему. Речь идет о том, «как мне легко сопоставить структуры ООП с таблицами базы данных».
Вот сценарий: у меня есть несколько типов «актеров» в моей системе – рабочие, работодатели, контакты. У них есть определенные общие функциональные возможности; другие части сильно различаются. Существами, с которыми сталкиваются все акторы, являются «сообщения», «заметки» (админы любят оставлять заметки для клиентов) и еще несколько. Существует множество типов других объектов, к которым относится каждый тип актора, а другие – нет.
В настоящее время моя схема базы данных содержит таблицы для:
Актеры:
Объекты:
Таблицы ассоциаций между субъектами и субъектами:
Это похоже на «кодовый запах» для меня. Всякий раз, когда клиент меняет свою роль (т. Е. Продвигается с «контакта» на «работодателя»), необходимо запустить кучу сумасшедших сценариев. Yuck … С другой стороны, если бы я работал в мире с чисто OOP-движком, это было бы намного проще – иметь базовый класс для всех объектов с общими свойствами и с ним делать …
В мире БД этот вариант кажется теоретически возможным, но звучит очень грязно … То есть, если я пойму это правильно, у меня будет новая таблица base_actor, а у каждого другого будет base_actor_id, а затем ассоциации будут между base_actor и сущности … Но тогда, как мне делать обратные запросы? Т.е. «показать мне все связи с просто актерами рабочего типа»?
Любой совет? Любые общие мысли по теме «сопоставление структур ООП с реляционной БД»?
Вот решение, которое я придумал около 10 лет назад. Система, использующая этот проект, все еще работает, поэтому она достаточно хорошо работает, чтобы выжить дольше, чем большая часть моего кода. 😉 Сегодня я могу использовать один из пакетов ORM, о которых упоминает Скотт, но действительно нет больших проблем, просто используя SQL напрямую.
Моделируйте все ваши отношения наследования как соединения между таблицами. Каждая таблица в вашей системе будет содержать атрибуты определенного класса.
Используйте идентификатор синтетического объекта (oid) в качестве основного ключа для всех объектов. Для генерации значений oid необходим генератор последовательности или столбец автоинкремента.
Все унаследованные классы должны использовать тот же тип OID, что и их родитель. Определите oid как внешний ключ с каскадным удалением. Родительская таблица получает столбец autoincrement oid, а дети получают простые столбцы oid.
Запросы по заключительным классам производятся в соответствующей таблице. Вы можете либо соединить все таблицы родительского класса в запрос, либо просто ленить загрузку необходимых вам атрибутов. Если ваша иерархия наследования глубока и у вас много классов, пакет ORM может действительно упростить ваш код. Моя система имела менее 50 классов с максимальной глубиной наследования 3.
Запросы по дочерним классам (например, запросы родительского класса) могут либо ленить загружать дочерние атрибуты на основе каждого экземпляра, либо повторять запрос для каждого дочернего класса, связанного с базовыми классами. Ленивая загрузка дочерних атрибутов на основе запроса родительского класса требует, чтобы вы знали тип объекта. У вас может быть достаточно информации в родительских классах, но если нет, вам нужно будет добавить информацию о типе. Опять же, здесь может помочь пакет ORM.
Виртуальные классы без атрибутов членов могут быть пропущены в структуре таблицы, но вы не сможете запросить их на основе этих классов.
Вот что «покажут мне все коммуникации с просто актерами рабочего типа».
select * from comm c, worker w where c.actor=w.oid;
Если у вас есть подклассы связи, и вы хотите сразу загрузить все атрибуты дочернего класса (возможно, ваша система не разрешает частичную конструкцию), самым простым решением является стремление присоединиться ко всем возможным классам.
select * from comm c, worker w, missive m where c.actor=w.oid and c.oid=m.oid; select * from comm c, worker w, shoutout s where c.actor=w.oid and c.oid=s.oid;
Одна последняя вещь. Убедитесь, что у вас хорошая база данных и правильные индексы. Производительность может быть серьезной проблемой, если база данных не может оптимизировать эти объединения.
.. Это о том, «как мне сопоставить структуры ООП с таблицами базы данных безболезненно».
Вы этого не сделаете.
Объектно-ориентированная и реляционная алгебра – две принципиально разные парадигмы. Вы не можете переходить между ними без субъективной интерпретации. Это называется несоответствием импеданса и получило название «Вьетнам компьютерных наук» .
Это называется ORM или реляционное сопоставление объектов . Существуют десятки продуктов, которые призваны помочь вам сопоставить структуры OO с реляционными таблицами. Например, Ruby on Rails предлагает Active Record для преодоления разрыва. Для PHP у вас есть Propel, Doctrine и Porte и многие другие.
То, что вы ищете, это Dissoint-subtypes … ORM – это взлом.
Предположение, которое вы предлагаете, кажется мне оправданным. Вы можете добавить столбец actortype в свою основную таблицу для различения между различными типами участников. ПК каждой таблицы конкретных актеров будет FK в таблице actorbase, чтобы избежать «волосатых» запросов и эмулировать отношение, подобное наследованию «is-a».
лучший ответ, который я когда-либо видел для этого, был: http://en.wikipedia.org/wiki/The_Third_Manifesto
К сожалению, это не то, что подходит в одном единственном ответе здесь, в stackoverflow. Я попытаюсь сократить его здесь, но предупреждаю вас, что такая аббревиатура не будет точным отражением третьего манифеста. Пожалуйста, перенаправляйте все критические замечания по этому решению, фактически просматривая эту проклятую вещь, вместо того, чтобы предполагать, что вы полностью ее понимаете, читая аббревиатуру. Ладно, вот оно.
определите три новых типа столбцов, названных работником, работодателем и контактом. Храните объекты каждого из этих типов в столбцах их соответствующих типов. Следуйте стандартным нормам нормализации для остальной части вашей модели данных.
Я чувствую, что нынешняя популярная технология баз данных фактически не поддерживает «правильный» способ сделать это (в частности, многие системы баз данных не позволяют определять новые типы). так что неважно, что вы делаете, вас всегда будут принуждать к компромиссной ситуации. Но после прочтения третьего манифеста, по крайней мере, вы узнаете, на ком вы рискуете.
ORM в настоящее время является в подавляющем большинстве популярным решением проблемы на данный момент, но я не верю, что это правильное решение.
Вероятно, стоит потратить время на ознакомление с Object Role Modeling, как обсуждалось в этом вопросе . Самая большая проблема, которую я вижу, заключается в том, что нет существующей принятой методологии для обсуждения концептуального дизайна реляционных данных. Лучшее, что вы можете сделать, это логическое моделирование (обычно ERM). Моделирование роли объекта является основой для обсуждения. Надеюсь, вы увидите узнаваемые артефакты из подобного обсуждения дизайна ООП, которое у вас может быть.
Многие РСУБД предлагают функцию наследования таблиц, которая связывает родительские таблицы с дочерними таблицами так же, как и наследование классов. реализация немного варьируется от поставщика к поставщику, но может возникнуть боль от реализации подобных концепций.
Кроме того, большинство СУБД имеют некоторую комбинацию триггеров, хранимых представлений и хранимых процедур, которые могут отделить поведение от реализации. Во многих случаях, например, правила PostgreSQL (обобщение представлений) предлагают очень сложную инкапсуляцию и довольно просты в использовании.
Пара людей заметила несоответствие объектно-реляционного импеданса. Лучшее решение – просто отказаться от РСУБД в пользу OODBMS , которая недавно завоевала популярность.
Тем не менее, по-моему, нет никаких объектных баз данных с API-интерфейсами в чистом PHP. Быстрый поиск дал этот результат, но он не обновлялся годами. С другой стороны, я слышал о множестве объектных баз данных для других языков, включая Hibernate , db4o и ZODB .
Я предлагаю вам использовать LINQ для PHP. Я знаю о. NET LINQ, но я думаю, PHPLinq стоит попробовать.
Для меня это похоже на то, что ваша модель данных отсутствует на уровне. Я бы поставил его более как это:
Таблица людей – (Только информация о реальных людях)
Таблица ролей – (Типы ролей, которые могут иметь люди, т.е. Работник, Работодатель, Контакт – и информация, специфичная для этой роли)
Таблица PeopleRoles – (people_id, role_id, возможно начало / изменение дат и т. Д.)
Таблица сущностей – (Определить различные типы объектов)
Таблица RoleEntities – (role_id, entity_id и т. Д.)
Затем изменение Person из одной роли в другую (или предоставление им нескольких ролей) – это простое обновление.