Этот урок набрал набрал достаточно большое количество
комментариев и дальнейшее его комментирование отключено.
Если вы хотели убедиться в правильности выполнения ДЗ или у вас возник вопрос по уроку,
посмотрите ранее добавленные комментарии, кликнув по кнопке ниже. Скорее всего вы найдете там то, что искали.
Если это не помогло - задайте вопрос в чате в телеграме - https://t.me/php_zone
Этого достаточно, чтобы знать, что пользователь залогинен? А если в этой куке ерунда какая-нибудь бессмысленная записана? Тогда $user будет null, и при попытке вызвать у него метод произойдет ошибка.
А зачем проверять $user!==null? Если пользователь не залогинен то у него точно не может быть прав админа! Поэтому как мне кажется данная проверка избыточна, можно просто проверить является ли пользователь админом или нет. <?php if ($user->isAdmin()==true): ?> Или я ошибаюсь?
Проверил свой код несколько раз. Всё так, как вы написали. Но при редактировании, если передавать какое-то поле пустым, то вылетает ошибка о том, что $article->getId() становится null в шаблоне edit.php. Решил проблему передачей вместе с ошибкой еще и массива $article.
Была ошибка, исправил. Спасибо. В шаблоне использовалась $article, я почему-то подумал, что если произойдет ошибка, то в шаблоне мы статью не будем выводить.
При попытке обновления статьи НЕ админом – бросайте исключение ForbiddenException, как в прошлом уроке.
public function edit(int $articleId): void
{
$article = Article::getById($articleId);
if ($article === null) {
throw new NotFoundException();
}
if ($this->user === null) {
throw new UnauthorizedException();
}
if (!$this->user->isAdmin()) {
throw new ForbiddenException('Для доступа к данной странице необходимы права администратора!');
}
......
Добавьте ссылку на странице показа статьи с текстом «Редактировать», которая будет вести на страницу редактирования этой статьи.
Сделайте так, чтобы эта ссылка показывалась только если пользователь залогинен и он админ.
public function allowEdit(): bool
{
if ($this === null || $this->getRole() !== 'admin') {
return false;
}
return true;
}
Сокращается до
public function allowEdit(): bool
{
return $this->getRole() === 'admin';
}
А вообще, метод для проверки возможности редактирования должен быть у статьи, а не у пользователя. В этот метод аргументом должен передаваться объект пользователя, права которого нужно проверить.
В шаблон лучше передавать уже готовую переменную, которая будет говорить о том, разрешено ли редактирование статьи, например, isEditable. Эта переменная должна формироваться в контроллере и передаваться во view.
public function edit(int $articleId): void
{
...
if (!$this->user->isAdmin()) {
throw new ForbiddenException('Статьи могут редактировать только администраторы');
}
...
public function view(int $articleId): void
{
...
if ($this->user === null) {
$isEditable = false;
} else {
$isEditable = ($this->user->isAdmin());
}
$this->view->renderHtml('articles/view.php', [
'article' => $article,
'isEditable' => $isEditable
]);
...
Тут вопрос: можно ли в экшене view проверку заменить на
if ($this->user !== null) {
$isEditable = ($this->user->isAdmin());
}
В таком случае в методе $this->view->renderHtml высвечивается предупреждение, что переменная $isEditable "might have not been defined". При этом ошибок в рендере шаблона нет. Как лучше поступить?
Вопрос не по уроку: немного неудобно было при отладке проверки на авторизацию постоянно заново заходить в одиночную статью, т.к. после входа редиректит на главную. Каким образом можно сделать редирект на ту страницу, с которой ты перешел на страницу авторизации? В гугле были предложения джавасриптом
header("location:javascript://history.go(-1)");
или через глобальную переменную (во-первых оно меня вернуло на страницу авторизации, вместо нужной страницы, а во-вторых пишут что это нехорошая практика в принципе)
Я бы использовал второй вариант - при переходе на страницу логина устанавливал бы куку с реферером и после логина переходил по ней. Хорошая практика или нет - решать вам) Если проблем она вам не создает, значит не такая уж и плохая.
В контоллере статей есть повторяющиеся проверки в разных методах:
if ($this->user === null){
throw new UnAuthorizedException();
}
if (!$this->user->isAdmin()){
throw new ForbiddenException('You don\'t have Admin rights');
}
в модели статьи так же:
if (empty($fields['name'])){
throw new InvalidArgumentException('Name field cannot be empty');
}
if (empty($fields['text'])){
throw new InvalidArgumentException('Text field cannot be empty');
}
Если такие небольшие повторяющиеся участки встречаются всего два-три раза, стоит их выделять в отдельные общие методы? А то вроде получается масло масляное - повторяющийся код убираем, но делаем нагромождение методов на каждый чих. Или все таки лучше любую повторяемость обобщать отдельно , чтобы потом было легче и быстрее при необходимости модифицировать код?
Такие небольшие кусочки можно вынести, но только если это даст реальное упрощение кода. Ну и есть "правило трёх" - если требуется такой же код в еще одном месте - возьми и скопируй, но если уже в трёх местах - вынеси в отдельный компонент (класс, метод, функцию и пр.)
У меня вопрос: Когда происходит обработка исключений, то почему-то отображаются ссылки для входа и регистрации, хотя пользователь уже зарегистрирован и выполнен вход, при переключении на страницу главная все в порядке - пользователь авторизован, как это исправить?
Исключения удобны тем, что можно бросать их в одном слое (например, модели) а ловить в другом (контроллере). Или в вашем случае, бросили в контроллере, а поймали во фронт-контроллере.
public function isAdmin(User $user) {
if ($user->role !== 'admin') {
throw new Forbidden('Недостаточно прав');
} else {
return true;
}
}
Это не исключительная ситуация, и исключение здесь бросать не к месту. Метод называется isAdmin, значит он должен вернуть булево значение true/false, в зависимости от того, является ли пользователь админом или нет. Если не является - возвращайте false, ничего исключительного здесь нет.
В то же время, для того чтобы бросать исключения, когда какое-то условие не выполняется, применяются методы с префиксом ensure...
Например:
public function ensureIsAdmin(): void
{
if (!$user->isAdmin()) {
throw ...
}
}
Обратите внимание, что такие методы ничего не возвращают, лишь бросают исключение, когда что-то не так.
Ensure можно перевести как "убедись что..." и далее пишем, в чем нужно убедиться. Если не убедились - это уже исключительная ситуация.
...
public function edit(int $articleId): void
{
$article = Article::getById($articleId);
if ($article === null) {
throw new NotFoundException('Не найдена такая статья');
}
if ($this->user === null) {
throw new UnauthorizedException();
}
if (!$this->user->isAdmin()) {
throw new ForbiddenException('Для редактирования статьи необходимы права администратора');
}
...
Нет, в PHP && и || это ленивые операторы. Выполняются слева направо. Если левый операнд оператора && имеет значение false, дальше проверять не имеет смысла. С || наоборот, будет выполнять пока не встретит первый true.
При попытке обновления статьи НЕ админом – бросайте исключение ForbiddenException, как в прошлом уроке.
ArticlesController.php
...
public function edit(int $articleId)
{
$article = Article::getById($articleId);
if ($article === null) {
throw new NotFoundException();
}
if ($this->user === null) {
throw new UnauthorizedException();
}
if (!$this->user->isAdmin()) {
throw new ForbiddenException();
}
...
Добавьте ссылку на странице показа статьи с текстом «Редактировать», которая будет вести на страницу редактирования этой статьи.
Сделайте так, чтобы эта ссылка показывалась только если пользователь залогинен и он админ.
use MyProject\Exceptions\ForbiddenException;
...
public function edit(int $articleId): void
{
$article = Article::getById($articleId);
if ($article === null) {
throw new NotFoundException();
}
if (!$this->user->isAdmin()) {
throw new ForbiddenException();
}
При попытке обновления статьи НЕ админом – бросайте исключение ForbiddenException, как в прошлом уроке.
public function edit(int $articleId): void
...
if (!$this->user->isAdmin()) {
throw new Forbidden('Ошибка доступа. Нужны права администратора.');
}
...
Добавьте ссылку на странице показа статьи с текстом «Редактировать», которая будет вести на страницу редактирования этой статьи.
Сделайте так, чтобы эта ссылка показывалась только если пользователь залогинен и он админ.
public function edit(int $articleId)
{
$article = Article::getById($articleId);
if ($article === null) {
throw new NotFoundException();
}
if ($this->user === null) {
throw new UnauthorizedException();
}
if (!$this->user->isAdmin()) {
throw new ForbiddenException('Для редактирования статьи нужно обладать правами администратора!');
}
...
}
ArticlesController/edit:
Templates/articles/view:
Этого достаточно, чтобы знать, что пользователь залогинен? А если в этой куке ерунда какая-нибудь бессмысленная записана? Тогда $user будет null, и при попытке вызвать у него метод произойдет ошибка.
Хм... тогда, наверное, так:
Громоздко, конечно, как то получается...
Да нет, все проще. Если пользователь не авторизован, то в $user будет null.
Достаточно проверить:
Наверное перед этим стоит делать проверку юзера на isset?
У нас $user всегда в шаблоне есть же.
А зачем проверять $user!==null? Если пользователь не залогинен то у него точно не может быть прав админа! Поэтому как мне кажется данная проверка избыточна, можно просто проверить является ли пользователь админом или нет. <?php if ($user->isAdmin()==true): ?> Или я ошибаюсь?
Если пользователь не авторизован, то в $user будет null, при попытке вызова метода не у объекта а у null будет фатальная ошибка.
Проверил свой код несколько раз. Всё так, как вы написали. Но при редактировании, если передавать какое-то поле пустым, то вылетает ошибка о том, что $article->getId() становится null в шаблоне edit.php. Решил проблему передачей вместе с ошибкой еще и массива $article.
Тогда все работает. В чем проблема, не подскажите?
Была ошибка, исправил. Спасибо. В шаблоне использовалась $article, я почему-то подумал, что если произойдет ошибка, то в шаблоне мы статью не будем выводить.
При попытке обновления статьи НЕ админом – бросайте исключение ForbiddenException, как в прошлом уроке.
Добавьте ссылку на странице показа статьи с текстом «Редактировать», которая будет вести на страницу редактирования этой статьи.
Сделайте так, чтобы эта ссылка показывалась только если пользователь залогинен и он админ.
Отлично
Добавил метод в модель Юзера:
В ArticleControllers при попытке переходить по ссылке ред. :
И в шаблоне в зависимости от значения $user->allowEdit() решаем показать <<Редактировать>> или нет :
$this === null - такого не бывает. Если есть $this, значит есть объект. null там быть не может
Сокращается до
А вообще, метод для проверки возможности редактирования должен быть у статьи, а не у пользователя. В этот метод аргументом должен передаваться объект пользователя, права которого нужно проверить.
ок, поправлю
В шаблон лучше передавать уже готовую переменную, которая будет говорить о том, разрешено ли редактирование статьи, например, isEditable. Эта переменная должна формироваться в контроллере и передаваться во view.
1.ArticlesConroller.php
2-3.view.php
if(!$this->user->IsAdmin()) - имена методов пишутся с маленькой буквы!
Не уследил, спасибо.
articles/view.php
ArticlesController
Тут вопрос: можно ли в экшене view проверку заменить на
В таком случае в методе $this->view->renderHtml высвечивается предупреждение, что переменная $isEditable "might have not been defined". При этом ошибок в рендере шаблона нет. Как лучше поступить?
Для чего скобки?
Ответ на твой вопрос - нет, нельзя. Потому что если пользователь будет не залогинен, возникнет ошибка.
Спасибо за урок!
ArticlesController::edit
templates/articles/view.php
Вопрос не по уроку: немного неудобно было при отладке проверки на авторизацию постоянно заново заходить в одиночную статью, т.к. после входа редиректит на главную. Каким образом можно сделать редирект на ту страницу, с которой ты перешел на страницу авторизации? В гугле были предложения джавасриптом
или через глобальную переменную (во-первых оно меня вернуло на страницу авторизации, вместо нужной страницы, а во-вторых пишут что это нехорошая практика в принципе)
Я бы использовал второй вариант - при переходе на страницу логина устанавливал бы куку с реферером и после логина переходил по ней. Хорошая практика или нет - решать вам) Если проблем она вам не создает, значит не такая уж и плохая.
спасибо большое за совет)
Пожалуйста)
В контоллере статей есть повторяющиеся проверки в разных методах:
в модели статьи так же:
Если такие небольшие повторяющиеся участки встречаются всего два-три раза, стоит их выделять в отдельные общие методы? А то вроде получается масло масляное - повторяющийся код убираем, но делаем нагромождение методов на каждый чих. Или все таки лучше любую повторяемость обобщать отдельно , чтобы потом было легче и быстрее при необходимости модифицировать код?
Такие небольшие кусочки можно вынести, но только если это даст реальное упрощение кода. Ну и есть "правило трёх" - если требуется такой же код в еще одном месте - возьми и скопируй, но если уже в трёх местах - вынеси в отдельный компонент (класс, метод, функцию и пр.)
Домашка
В шаблоне правильнее тоже использовать имя isAdmin, потому что это не сама сущность админа, а только флажок.
У меня вопрос: Когда происходит обработка исключений, то почему-то отображаются ссылки для входа и регистрации, хотя пользователь уже зарегистрирован и выполнен вход, при переключении на страницу главная все в порядке - пользователь авторизован, как это исправить?
А в какой момент прокидывается пользователь в шаблон?
Задание (ArticleController)
Там же:
templates/articles/View
Сделал так, чтобы только автор статьи мог редактировать её, а не любой admin)
Что это за обработка исключений такая? Нужно завести соответствующие шаблоны и показывать информацию об ошибках пользователю.
Также нет смысла бросать исключения и ловить их в одном и том же слое приложения. Можно же сразу написать
Исключения удобны тем, что можно бросать их в одном слое (например, модели) а ловить в другом (контроллере). Или в вашем случае, бросили в контроллере, а поймали во фронт-контроллере.
ArticlesController.php
User.php
articles/view.php
Это не исключительная ситуация, и исключение здесь бросать не к месту. Метод называется isAdmin, значит он должен вернуть булево значение true/false, в зависимости от того, является ли пользователь админом или нет. Если не является - возвращайте false, ничего исключительного здесь нет.
В то же время, для того чтобы бросать исключения, когда какое-то условие не выполняется, применяются методы с префиксом ensure...
Например:
Обратите внимание, что такие методы ничего не возвращают, лишь бросают исключение, когда что-то не так.
Ensure можно перевести как "убедись что..." и далее пишем, в чем нужно убедиться. Если не убедились - это уже исключительная ситуация.
В прошлом уроке ничего не сказали по-поводу этого метода, потому так и оставил
ArticlesController.php:
view.php:
Отлично!
view.php
ArticlesController.php
Я на жену столько времени не трачу, сколько потратил на эту задачу. Спасибо тебе, Артём!)
На самом деле задача простейшая, но эти мелочи...)
:D
src\MyProject\Controllers\ArticlesController.php
templates\articles\view.php
Отлично
src/MyProject/Controllers/ArticlesController.php view()
src/MyProject/Controllers/ArticlesController.php edit()
templates/articles/view.php
Работает все!
А если зайти на страницу просмотра неавторизванным юзером?
Изменил.
Вопрос:
В выражении if (!empty($this->user) && $this->user->isAdmin()) если первое условие не выполняется, то второе проверяется?
Нет, в PHP && и || это ленивые операторы. Выполняются слева направо. Если левый операнд оператора && имеет значение false, дальше проверять не имеет смысла. С || наоборот, будет выполнять пока не встретит первый true.
Ясно, спасибо.
ArticlesController.php
templates\articles\view.php
Супер!
Update! +
Лучше проверять в шаблоне
ArticlesController
view.php
Отлично!
домашка
1 пункт
ArticlesController.php
index.php
2 пункт и 3 пункт
view.php
Огонь!
Добавьте ссылку на странице показа статьи с текстом «Редактировать», которая будет вести на страницу редактирования этой статьи.
Сделайте так, чтобы эта ссылка показывалась только если пользователь залогинен и он админ.
view.php
Супер!
Моя попытка решить домашнее задание.
ArticlesController.php
/templates/articles/view.php
Про это уже писал
Странная формулировка получилась)
Controllers/ArticlesController.php
templates/articles/view.php
Супер!