У меня есть веб-приложение MVC с объектами домена и данными. Методы класса данных отображает всю логику запросов к базе данных. Я пытаюсь избежать зеркалирования любой структуры базы данных и, следовательно, для достижения максимальной гибкости при построении операторов sql. Итак, в принципе, я стараюсь не использовать любую структуру ORR или ActiveRecord / ВСЕ.
Позвольте мне привести вам пример. Обычно я мог бы иметь абстрактный класс AbstractDataMapper
унаследованный всеми конкретными классами сопоставления данных, например класс UserDataMapper
. И тогда я мог бы определить метод findById()
в AbstractDataMapper
, чтобы получить запись конкретных users
похожих на таблицу, – с помощью заданного значения id
, например идентификатора пользователя. Но это означало бы, что я всегда получал запись из одной таблицы без возможности использования каких-либо левых соединений для получения других данных из других таблиц, соответствующих данному id
id.
Итак, мой вопрос: в этих условиях, к которым я сам обязан, должен ли я реализовать абстрактный класс картографирования данных, или каждый класс mapper данных должен содержать свою собственную полностью «проприетарную» реализацию уровня доступа к данным?
Надеюсь, я могу выразить свою идею понятной. Скажите, пожалуйста, если бы я был как-то неясен или у вас возникли вопросы.
Большое вам спасибо за ваше время и терпение.
Если бы я понял вашу мысль …
Имея все ваши конкретные картографы, наследующие SQL от общего класса, есть несколько проблем, которые вы пропустили:
id
как имя для всех столбцов PRIMARY KEY
Теперь я попытаюсь распаковать каждый из них.
Чтобы создать общий findById()
, единственным прагматичным подходом является построение его вокруг чего-то вроде этого:
"SELECT * FROM {$this->tableName} WHERE id = :id"
Основной проблемой на самом деле является символ подстановки *
.
Существует два основных подхода к заполнению объекта с использованием устройства отображения данных: использование сеттеров или использование рефлексии. В обоих случаях «имена» параметров / сеттеров подразумеваются столбцами, которые вы выбрали.
В обычном запросе вы можете сделать что-то вроде SELECT name AS fullName FROM ...
, которое позволяет использовать запрос для повторного присвоения имен полям. Но с «унифицированным подходом» нет хороших вариантов.
id
? Итак, дело в том, что если у вас нет структуры mapper-per-table (в этом случае активная запись начинается, как прагматичная опция), вы получите несколько (реально распространенных) сценариев «крайних случаев» для ваших картографов:
Ваша оригинальная идея будет прекрасно работать в небольшом проекте (с одним или двумя картографами, являющимися «краевым случаем»). Но с большим проектом использование findById()
будет исключением, а не нормой.
Чтобы на самом деле получить этот findById()
в суперклассе, вам понадобится способ сообщить ему имя таблицы. Это означало бы, что у вас есть что-то вроде protected $tableName
в определении класса.
Вы можете смягчить его, используя abstract function getTableName()
в вашем классе абстрактного mapper, которая при реализации возвращает значение глобальной константы.
Но что происходит, когда ваш картограф должен работать с несколькими таблицами.
Мне кажется, что у меня запах кода , потому что информация фактически пересекает две границы (из-за отсутствия лучшего слова). Когда этот код сломается, ошибка будет показана для SQL в суперклассе, в котором не возникает ошибка (особенно, если вы идете с константами).
Это немного более спорное мнение 🙂
Насколько я могу судить, практика вызова всех первичных id
столбцов поступает из разных ORM. Штраф, который он несет, применяется только к читаемости (и обслуживанию кода). Рассмотрим эти два вопроса:
SELECT ar.id, ac.id FROM Articles AS ar LEFT JOIN Accounts AS ac ON ac.id = ar.account_id WHERE ar.status = 'published' SELECT ar.article_id, ac.account_id FROM Articles AS ar LEFT JOIN Accounts AS ac USING(account_id) WHERE ar.status = 'published'
По мере того как схема БД растет и запросы становятся более сложными, становится все труднее и сложнее отслеживать, что означает «id» в каком случае.
Моя рекомендация состояла бы в том, чтобы попытаться использовать одно и то же имя для столбца, когда оно является основным, как когда это внешний ключ (когда это возможно, потому что в некоторых случаях, например, для «закрывающих таблиц», это нежизнеспособно). В принципе, все столбцы, в которых хранятся идентификаторы одного и того же типа, должны иметь одно и то же имя.
В качестве незначительного бонуса вы получаете синтаксический синтаксис USING()
.
Плохая идея. Вы в основном ломаете LSP .