ACL и контроллеры
Прежде всего: это разные вещи / слои чаще всего. Когда вы критикуете примерный код контроллера, он объединяет оба - очевидно, слишком жесткий.
Терешко уже обрисовал в общих чертах способ, которым вы могли бы отделить это больше с рисунком декоратора.
Я бы сначала пошел на шаг назад, чтобы найти исходную проблему, с которой вы столкнулись, и потом немного ее обсудить.
С одной стороны, вы хотите иметь контроллеры, которые просто выполняют ту работу, которую им предписано (команда или действие, давайте назовем это командой).
С другой стороны, вы хотите иметь возможность использовать ACL в своем приложении. Область работы этих ACL должна состоять в том, чтобы - если я правильно понял ваш вопрос - контролировать доступ к определенным командам ваших приложений.
Поэтому для такого типа контроля доступа требуется нечто другое, что объединяет эти два элемента. На основе контекста, в котором выполняется команда, включается ACL, и необходимо принять решение, может ли конкретная команда быть выполнена конкретным субъектом (например, пользователем).
Давайте подведем итог к этому моменту, что мы имеем:
Компонент ACL является здесь центральным: он должен знать хотя бы кое-что о команде (чтобы точно определить команду) и уметь идентифицировать пользователя. Пользователи обычно легко идентифицируются по уникальному идентификатору. Но часто в веб-приложениях есть пользователи, которые вообще не идентифицированы, часто называются гостями, анонимами, всеми и т. Д. В этом примере мы предполагаем, что ACL может потреблять пользовательский объект и инкапсулировать эти детали. Пользовательский объект привязан к объекту запроса приложения, и ACL может его использовать.
А как насчет идентификации команды? Ваша интерпретация шаблона MVC предполагает, что команда представляет собой соединение имени класса и имени метода. Если мы посмотрим более внимательно, то есть даже аргументы (параметры) для команды. Таким образом, уместно спросить, что именно идентифицирует команду? Имя класса, имя метода, количество или имена аргументов, даже данные внутри любого из аргументов или смесь всего этого?
В зависимости от того, какой уровень детализации вам необходим для определения команды в вашем ACL'инге, это может сильно варьироваться. Для примера давайте оставим это просто и укажите, что команда идентифицируется по имени класса и имени метода.
Так что контекст того, как эти три части (ACL, Command и User) принадлежат друг другу, теперь более понятен.
Можно сказать, что с мнимым компонентом 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 . Поскольку Routing уже имеет задание найти команду, почему бы не расширить его, чтобы проверить, действительно ли команда разрешена для каждого ACL? Например. расширив Router
до маршрутизатора с поддержкой ACL: RouterACL
. Если ваш маршрутизатор еще не знает User
, то Router
- не то место, потому что для работы ACL должна быть идентифицирована не только команда, но и пользователь. Так что это место может меняться, но я уверен, что вы легко сможете найти место, которое вам нужно расширить, потому что это место, которое удовлетворяет требованиям пользователя и команды:
User -> Browser -> Request (HTTP)
-> Request (Command)
Пользователь доступен с начала, сначала команда с Request(Command)
.
Таким образом, вместо того, чтобы помещать проверки ACL внутри каждой конкретной реализации команды, вы помещаете ее перед ней. Вам не нужны тяжелые шаблоны, магия или что-то еще, ACL выполняет свою работу, пользователь выполняет свою работу, и особенно команда выполняет свою работу: просто команда, ничего больше. Команде не интересно знать, применяются ли к ней роли, если она где-то охраняется или нет.
Так что просто держите вещи отдельно, которые не принадлежат друг другу. Используйте слегка перефразировку Принципа единой ответственности (SRP) : должна быть только одна причина для изменения команды - потому что команда изменилась. Не потому, что вы теперь вводите ACL'ing в своем приложении. Не потому, что вы переключаете объект User. Не потому, что вы переходите с интерфейса HTTP / HTML на интерфейс SOAP или интерфейс командной строки.
ACL в вашем случае контролирует доступ к команде, а не к самой команде.