Я много читаю на уровне модели в PHP MVC и разрабатываю Data Mapper (да, я заново изобретаю колесо, оно учится).
Могу сказать, что понимаю связь между даннымиMappers и доменные объекты.
Я пытаюсь согласованно реализовать способ извлечения вложенных отношений в Data Mapper, не делая Mapper зависимым от других Mappers.
ВотПример некоторых связанных доменов:
- Пользователь
- Роль
- Разрешения
Таким образом, Роль имеет много разрешений, а пользователь имеетмного ролей:
User -> Role(s) -> Permission(s)
При звонке UserMapper -> fetch($id)
я должен получить домен пользователя.Ради (абсурдной) простоты домен пользователя имеет одно свойство, roles
, набор доменов ролей.
Пока все хорошо.В UserMapper я могу присоединить таблицу roles
к users
, сопоставить первичный ключ, получить все роли, связанные с каждым пользователем, и создать домен ролей для каждого из них.Коллекция ролей добавлена пользователю.
Проблема
Эта коллекция ролей не завершена.Опять же, ради абсурдной простоты, у каждой роли есть одно свойство, permissions
, набор доменов разрешений.
Все, что мы до сих пор делали, - это заполняем пользовательский домен коллекцией ролей, но у нас нетне получили разрешения, связанные с каждой ролью в коллекции.Коллекция ролей не завершена.
Мой вопрос: как заполнить коллекцию разрешений для каждой роли у каждого пользователя?
Идея 1
Это тривиально простая идея (с точки зрения реализации PHP).UserMapper начинает выглядеть так:
class UserMapper implements Mapper {
public function __construct(RoleMapper $mapper)
}
А в RoleMapper:
class RoleMapper implements Mapper {
public function __construct(PermissionMapper $mapper)
}
В UserMapper мы выбираем подходящих пользователей.Затем мы повторим коллекцию User и вызовем RoleMapper -> fetch_by_user($user -> id)
.Role Mapper получает соответствующие роли, выполняет итерацию полученной коллекции ролей и вызывает PermissionMapper -> fetch_by_role($role -> id)
.
Это легко реализовать.Слепо очевидно, что происходит.Но это кажется неправильным и, вероятно, будет ужасно неэффективным в использовании (по крайней мере, для чего-либо с более сложными отношениями, чем этот пример).
Мне не нравится, что преобразователи данных зависят от других преобразователей данных - каждыйData Mapper больше не является автономным и не всегда выполняет свое собственное отображение.UserMapper не знает, что именно будет в возвращаемой коллекции - мы предполагаем, что можем доверять RoleMapper, но концептуально мы нарушили инкапсуляцию.
Идея 2
С чего я начал.UserMapper использует объединение для получения ролей, связанных с каждым пользователем.
Изначально это выглядит нормально.Это эффективно - мы просто выполняем одну инструкцию SQL.Data Mappers не нужно ограничивать одной таблицей - мне это удобно.
Но ... что происходит, когда нам нужно получить отношения рекурсивно?
Что я меньше - это идея о том, что UserMapper должен не только знать, что пользователи имеют роли (это нормально), но и что роли имеют разрешения .Эта деталь, по-видимому, находится вне сферы интересов UserMapper.
Что, если в дальнейшем у Ролей также будет много "Шоколадных Foobars?"Я не хочу обновлять UserMapper для правильного получения всех этих Chocolate Foobars.
Возможно, мы также добавим новый домен "Pet", и у Pets тоже могут быть роли.Так что теперь я должен обновить мой UserMapper и мой PetMapper для поддержки этих сумасшедших «шоколадных батончиков», связанных с ролями.Идея 1 полностью устранена, но у нее есть свои проблемы, описанные выше.
Заключение
Я начал писать этот пост в надежде, чтовсе просто дало бы мне решение (не могу сказать, сколько раз я набирал в Stack Overflow и в итоге не нажимал enter).Пока я не нашел.
Ни один из этих подходов не кажется "правильным".С одной стороны, идея 1 поддерживает согласованность отдельных картографических данных и, по-видимому, обеспечивает более высокий уровень обслуживания.Но это также приведет к очень большому количеству запросов (даже если они являются высокооптимизированными запросами на основе индексов) и потребует, чтобы у преобразователей данных были другие сопоставители данных в качестве зависимостей, что кажется неправильным.
Но идея 2 - это малолучше.Объединение в устройстве отображения данных верхнего уровня работает нормально ... если только домен, к которому вы присоединяетесь, не требует присоединений.
Мне было бы интересно услышать подходы к решению этой проблемы.Как вы управляете вложенными отношениями между доменами в сценарии Data Mapper?
В конечном итоге, вызывая UserMapper -> fetch()
, мы получаем домены пользователей, заполненные коллекциями ролей, которые сами заполняются коллекциями разрешений.Пользователи - MTM с ролями, роли - MTM с разрешениями.
Это всего лишь пример сценария.Другие включают Blog post -> Image(s) -> Category(ies)
, или Workstation -> Phone(s) -> Camera(s) -> Component(s)
.
Если взять последний пример, WorkstationMapper-> fetch()
должен вернуть WorkstationCollection
, где мы можем сделать: Workstation -> get_phone("TA-1033") -> get_camera("front") -> has_component("1080p video capture component")
.