ACL и контроллеры
Прежде всего: это чаще всего разные вещи / слои. Когда вы критикуете примерный код контроллера, он объединяет и то, и другое - очевидно, слишком тесно.
Терешко уже наметил способ, как вы можете отделить это больше с помощью шаблона декоратора.
Я бы сначала отступил на шаг назад, чтобы найти исходную проблему, с которой вы столкнулись, и затем немного ее обсудить.
С одной стороны, вы хотите иметь контроллеры, которые просто выполняют ту работу, которую им приказывают (команда или действие, назовем это командой).
С другой стороны, вы хотите иметь возможность добавлять ACL в свое приложение. Область работы этих ACL должна заключаться - если я правильно понял ваш вопрос - для управления доступом к определенным командам ваших приложений.
Таким образом, для такого контроля доступа необходимо что-то еще, что объединяет эти два аспекта. В зависимости от контекста, в котором выполняется команда, срабатывает ACL, и необходимо принимать решения, может ли конкретная команда быть выполнена конкретным субъектом (например, пользователем).
Подведем итог тому, что у нас есть:
Компонент ACL здесь является центральным: он должен знать хотя бы что-то о команде (чтобы идентифицировать команду, чтобы быть точным), и он должен иметь возможность идентифицировать пользователя. Обычно пользователей легко идентифицировать по уникальному идентификатору. Но часто в веб-приложениях есть пользователи, которые вообще не идентифицируются, их часто называют гостями, анонимами, всеми и т. Д. В этом примере мы предполагаем, что ACL может использовать объект пользователя и инкапсулировать эти детали. Пользовательский объект привязан к объекту запроса приложения, и ACL может использовать его.
А как насчет определения команды? Ваша интерпретация шаблона MVC предполагает, что команда состоит из имени класса и имени метода. Если присмотреться, для команды есть даже аргументы (параметры). Итак, уместно спросить, что именно определяет команду? Имя класса, имя метода, количество или имена аргументов, даже данные внутри любого из аргументов или смесь всего этого?
В зависимости от того, какой уровень детализации вам нужен для идентификации команды в вашем ACL, это может сильно различаться. Для примера давайте оставим его просто и укажем, что команда идентифицируется по имени класса и имени метода.
Таким образом, контекст того, как эти три части (ACL, команда и пользователь) принадлежат друг другу, теперь более ясен.
Можно сказать, что с воображаемым компонентом ACL мы уже можем делать следующее:
$acl->commandAllowedForUser($command, $user);
Просто посмотрите, что здесь происходит: сделав и команду, и пользователя идентифицируемыми, ACL может выполнять свою работу. Работа ACL не связана с работой как пользовательского объекта, так и конкретной команды.
Не хватает только одной части, она не может жить в воздухе. И это не так. Итак, вам нужно найти место, где должен сработать контроль доступа. Давайте посмотрим, что происходит в стандартном веб-приложении:
User -> Browser -> Request (HTTP)
-> Request (Command) -> Action (Command) -> Response (Command)
-> Response(HTTP) -> Browser -> User
Чтобы найти это место, мы знаем, что оно должно быть до того, как будет выполнена конкретная команда, поэтому мы можем сократить этот список, и нам нужно только изучить следующие (потенциальные) места:
User -> Browser -> Request (HTTP)
-> Request (Command)
В какой-то момент в вашем приложении вы знаете, что конкретный пользователь запросил выполнение конкретной команды. Здесь вы уже выполняете своего рода ACL: если пользователь запрашивает команду, которой не существует, вы не разрешаете выполнение этой команды. Поэтому, где бы это ни происходило в вашем приложении, может быть хорошим местом для добавления «настоящих» проверок ACL:
Команда найдена, и мы можем создать ее идентификацию, чтобы ACL мог с ней справиться. В случае, если команда не разрешена для пользователя, команда не будет выполнена (действие). Может быть, CommandNotAllowedResponse
вместо этого CommandNotFoundResponse
запрос не может быть преобразован в конкретную команду.
Место, где отображение конкретного HTTPRequest отображается на команду, часто называется маршрутизацией . Поскольку у Routing уже есть задание по поиску команды, почему бы не расширить его, чтобы проверить, действительно ли команда разрешена для ACL? Например , посредством расширения Router
к маршрутизатору осведомлены ACL: RouterACL
. Если ваш маршрутизатор еще не знает User
, то Router
это не подходящее место, потому что для работы ACL необходимо идентифицировать не только команду, но и пользователя. Таким образом, это место может быть разным, но я уверен, что вы можете легко найти место, которое нужно расширить, потому что это место, которое выполняет требования пользователя и команды:
User -> Browser -> Request (HTTP)
-> Request (Command)
Пользователь доступен с самого начала, сначала команда с Request(Command)
.
Поэтому вместо того, чтобы помещать проверки ACL в конкретную реализацию каждой команды, вы помещаете ее перед ней. Вам не нужны какие-либо сложные шаблоны, магия или что-то еще, ACL выполняет свою работу, пользователь выполняет свою работу, и особенно команда выполняет свою работу: просто команда, ничего больше. Команде неинтересно знать, применимы ли к нему роли, охраняется он где-то или нет.
Так что просто держите отдельно вещи, которые не принадлежат друг другу. Используйте небольшую переформулировку принципа единой ответственности (SRP) : должна быть только одна причина для изменения команды - потому что команда изменилась. Не потому, что вы сейчас вводите ACL в свое приложение. Не потому, что вы переключаете объект User. Не потому, что вы переходите с интерфейса HTTP / HTML на интерфейс SOAP или командной строки.
ACL в вашем случае управляет доступом к команде, а не самой командой.
if($user->hasFriend($other_user) || $other_user->profileIsPublic()) $other_user->renderProfile()
(иначе, отобразите «У вас нет доступа к профилю этого пользователя» или что-то в этом роде? Я не понимаю.