Этот урок набрал набрал достаточно большое количество
комментариев и дальнейшее его комментирование отключено.
Если вы хотели убедиться в правильности выполнения ДЗ или у вас возник вопрос по уроку,
посмотрите ранее добавленные комментарии, кликнув по кнопке ниже. Скорее всего вы найдете там то, что искали.
Если это не помогло - задайте вопрос в чате в телеграме - https://t.me/php_zone
public static function checkActivationCode(User $user, string $code): bool
{
$db = Db::getInstance();
$result = $db->query(
'SELECT * FROM `' . self::TABLE_NAME . '` WHERE `user_id` = :user_id AND `code` = :code',
[
'user_id' => $user->getId(),
'code' => $code
]
);
if (!$result)
{
throw new UserActivationException('Ошибка активации. Проверочный код не валидный.');
}
return !empty($result);
}
public static function deleteActivationCode(int $userId, string $activationCode): void
{
$db = Db::getInstance();
$db->query(
'DELETE FROM `' . self::TABLE_NAME . '` WHERE `user_id` = :userId AND `code` = :activationCode;', [
':userId' => $userId,
':activationCode' => $activationCode,
]
);
}
Так же для проверки на "если в ссылку активации подставить несуществующего пользователя" в методе getById бросаю исключение при получении пустого массива после запроса в БД. \
Все верно?
public function activate(int $userId, string $activationCode)
{
try {
$user = User::getById($userId);
} catch (UserActivationException $e)
Неправильно - в процессе получения пользователя по id никак не должно бросаться исключение с типом UserActivationException. UserActivationException - только для ошибок, связанных с активацией. Само по себе получение пользователя из базы никак не связано с активацией.
echo 'OK!';
а где шаблон для успешного случая?
if (!$result)
{
throw new UserActivationException('Ошибка активации. Проверочный код не валидный.');
}
return !empty($result);
конкретно здесь !$result и !empty($result) - дадут просто противоположные значения. Не имеет смыла здесь кидать исключение - достаточно только вернуть true или false. Этот метод должен сказать, является ли код валидным или нет. То есть если он невалидный - это не исключительная ситуация, а вполне себе штатная. Поэтому исключение здесь не нужно.
Таким образом, в контроллере вам не нужно ничего ловить, нужно просто проверять пользователя на !== null и то что метод проверки кода вернул true.
public static function existCode($userId): bool
{
$db = Db::getInstance();
$code = $db->query(
'SELECT code FROM ' . self::TABLE_NAME . ' WHERE user_id = :user_id',
[
'user_id' => $userId
]
);
return !empty($code);
}
public static function deleteActivationCode(int $userId): void
{
$db = Db::getInstance();
$db->query(
'DELETE FROM ' . self::TABLE_NAME . ' WHERE user_id = :user_id',
[
'user_id' => $userId
]
);
}
Функцию delete провожу без сверки с кодом, так как мы запускаем её непосредственно после проверки кода на валидность, или это открывает возможность появления каких-либо ошибок?
UsrsController:
public function activate(int $userId, string $activationCode)
{
try {
$user = User::getById($userId);
if ($user === null) {
throw new ActivateException('Нет такого пользователя');
}
if ($user->getIsConfirmed() == 1) {
throw new ActivateException('Пользователь уже активирован');
}
if (!UserActivationService::existCode($userId)) {
throw new ActivateException('Не создан код активации');
}
$isCodeValid = UserActivationService::checkActivationCode($user, $activationCode);
if ($isCodeValid) {
$user->activate();
UserActivationService::deleteActivationCode($userId);
echo 'OK!';
} else {
echo 'Код активации не верен';
}
} catch (ActivateException $e) {
$this->view->renderHtml('errors/noId.php', ['error' => $e->getMessage()]);
return;
}
}
Я пытался учесть замечание Sparky, про то, что Activation-исключение бросается в процессе получения пользователя, но ума не приложу, как сделать правильно... нужно бросать другое исключение? или выносить catch в index?
Ещё не сделал шаблоны, ибо это не сложно, но времяёмко, а уже поздно и пора спать) Поэтому проторопился и сделал просто вывод месаджами(
1) Удаление кода без проверки кода в модели - норм. Ошибки вряд ли тут будут.
2) Логику, отвечающую за проверку существования кода можно перенести внутрь UserActivationService::checkActivationCode(). Если кода нет - просто возвращать false, исключение тут не нужно.
3) Исключения вы бросаете в нужном месте. Не надо ничего усложнять, у Вас все просто и понятно.
4) Когда код активации неверен - тоже можно бросить исключение.
5) Структуру кода можно переделать так, что сначала проверяются все исключительные ситуации и бросаются исключения, а затем, если все хорошо, просто работает код для успешного исхода. Суть такая:
if (что-то плохо) {
бросаем исключение
}
if (что-то другое плохо) {
бросаем исключение
}
если дошли до сюда, то просто рисуем шаблон, никаких if-ов уже не нужно.
Мне кажется есть пара нелогичных моментов, в том числе и в самом задание:
Сначала мы выводим сообщение о том, что нет юзера, причём код активации может быть любой. Можно так выяснить сколько есть пользователей и их id. Мне кажется, что лучше выводить менее информативное сообщение, вроде такого: "некорректная ссылка активации".
Проверка на то, что запись уже активна лишняя, так как иначе мы попросту не сможем активировать учётку. А если мы её активировали, то активационный код успешно удалится. Попросту в данном методе мы никогда не узнаем была ли активирована учётка ранее.
Отсюда вывод, что нужно проверять только 2 параметра, юзера и сам код активации.
public function activate(int $userId, string $activationCode): void
{
try {
$user = User::getById($userId);
if ($user === null) {
throw new ActivationException('User was not found');
}
if ($user->isActivated()) {
throw new ActivationException('User is already activated');
}
$isCodeValid = UserActivationService::checkActivationCode($user, $activationCode);
if (!$isCodeValid) {
throw new ActivationException('Code is not valid');
}
$user->activate();
UserActivationService::removeActivationCode($user);
$this->view->renderHtml('users/activationSuccess.php');
} catch (ActivationException $e) {
$this->view->renderHtml('errors/activationError.php', ['error' => $e->getMessage()], 422);
}
}
Вопросы:
Обязательно делать проверку на то что код уже активирован? В нашем случае это же невозможно и делается в виде подстраховки. Лишний код? Хотелось бы детальнее узнать, нужны ли эти дополнительный проверки на каждую функцию делать в будущем?
В методе checkActivationCode обязательно делать проверку на то, существует ли данный код? Ведь если запрос сам не найдет - значит не существует.
Такая проверка позволит узнать о возможных ошибках в логике работы программы в других местах. Если пользователь уже активирован, и его пытаются активировать еще раз, говорит о том, что где-то косяк.
А какая там проверка. Там только смотрим, что запрос вернул непустой результат. Как вы и написали.
public function activate(int $userId, string $activationCode)
{
try {
$user = User::getById($userId);
if ($user->getConfirmed() === 1) {
throw new InvalidActivationException('Пользователь уже авторизован');
}
$isCodeValid = UserActivationService::checkActivationCode($user, $activationCode);
if ($isCodeValid) {
$user->activate();
UserActivationService::deleteCode($user->getId());
$this->view->renderHTML('users/successValidation.php');
} else {
throw new InvalidActivationException('Произошла ошибка авторизации. Попробуйте снова');
}
} catch (InvalidActivationException $e) {
$this->view->renderHTML('users/UserError.php', ['error' => $e->getMessage()]);
}
}
далее, шаблон с ошибками(идентичен 500.php)
<h1>Что то пошло не так...</h1>
<?= $error; ?>
Ну и шаблон успешной авторизации
<?php include __DIR__ . '/../header.php'; ?>
<h1>Вы успешно подтвердили свою почту</h1>
<a href="/">Перейти к просмотру всех статей</a>
<?php include __DIR__ . '/../footer.php'; ?>
public static function deleteActivationCode(User $user, string $code)
{
$db = Db::getInstance();
$db->query(
'DELETE FROM ' . self::TABLE_NAME . ' WHERE `user_id` = :user_id AND `code` = :code;',
[
':user_id' => $user->getId(),
':code' => $code
]
);
}
UsersController
...
public function activate(int $userId, string $activationCode): void
{
try {
$user = User::getById($userId);
if ($user === null) {
throw new ActivationException('Пользователь не найден.');
}
if ($user->IsConfirmed()) {
throw new ActivationException('Пользователь уже активирован');
}
$isCodeValid = UserActivationService::checkActivationCode($user, $activationCode);
if (!$isCodeValid) {
throw new ActivationException('Неверный код активации');
}
if ($isCodeValid) {
$user->activate();
$this->view->renderHtml('users/successfulActivation.php');
UserActivationService::deleteActivationCode($user, $activationCode);
return;
}
} catch (ActivationException $e) {
$this->view->renderHtml('users/nonexistentCode.php', ['error' => $e->getMessage()]);
}
}
...
nonexistensCode
<?php include __DIR__ . '/../header.php'; ?>
<div style="text-align: center;">
<h1>Не удалось активировать пользователя.</h1><br>
<?= $error ?>
</div>
<?php include __DIR__ . '/../footer.php'; ?>
successfulActivation
<?php include __DIR__ . '/../header.php'; ?>
<div style="text-align: center;">
<h1>Ваша учётная запись успешно активирована!</h1>
Вы можете перейти на <a href="/">главную страницу</a>.
</div>
<?php include __DIR__ . '/../footer.php'; ?>
Подскажите, в чем проблема? Мой код соответствует коду урока но после перехода по ссылке в письме выдает ошибку:
( ! ) Fatal error: Uncaught TypeError: Argument 1 passed to MyProject\Models\Users\UserActivationService::checkActivationCode() must be an instance of MyProject\Models\Users\User, null given, called in W:\domains\newProject\src\MyProject\Controllers\UserController.php on line 26 and defined in W:\domains\newProject\src\MyProject\Models\Users\UserActivationService.php on line 31
( ! ) TypeError: Argument 1 passed to MyProject\Models\Users\UserActivationService::checkActivationCode() must be an instance of MyProject\Models\Users\User, null given, called in W:\domains\newProject\src\MyProject\Controllers\UserController.php on line 26 in W:\domains\newProject\src\MyProject\Models\Users\UserActivationService.php on line 31
Ожидается, что в метод прилетит объект класса User, а прилетел null. Используйте Xdebug, и учитесь читать ошибки, на этом уроке не должно возникать подобных вопросов.
Доброго времени суток автор, скажите а как все это проверить и настроить на linux ubuntu 19.04 я установил апач c php mysql но письма не отправляются. При поиске в интернет нашел статьи но они очень старые не подскажешь куда смортеть и как фиксить?
Поднимай теперь почтовый сервис с помощью sendmail, либо, еще вариант PEAR Mail и Net_SMTP. Reply от гугл почты можно использовать, как и в уроке по OpenServer.
Но, честно, не знаю зачем с локалки отправлять письма на реальный сервер, можно фейковый сервер поднять на файлах и также в почтовом клиенте они будут появляться. У меня конфиг xampp + Evolution. Погугли про xampp на линуксе, мануалы даже на русском есть.
public function activate(int $userId, string $activationCode)
{
$user = User::getById($userId);
if ($user === null){ //если пользователя нет - исключение
throw new \MyProject\Exceptions\UserNotFoundException();
}
$isCodeValid = UserActivationService::checkActivationCode($user, $activationCode);
$isConfirmed = $user->getConfirmed(); // активирован пользователь? - добавил метод в User
if ($isCodeValid) { //если код есть в базе, то активируем и ок
$user->activate();
echo 'OK!';
}
elseif ($isConfirmed == 1){ //если кода нет в базе(удален после активации??), но есть актив юзер
$this->view->renderHtml('users/alreadyActivated.php');//
}
else { //если юзер не активный и кода вб азе нет - исключение!
throw new \MyProject\Exceptions\CodeNotFoundException();
}
UserActivationService::deleteActivationCode($user); //добавил в UserActivationService Метод deleteActivationCode
}
а это метод deleteActivationCode :
public static function deleteActivationCode(User $user): void
{
$db = Db::getInstance();
$db->query(
'DELETE FROM `' . self::TABLE_NAME . '` WHERE user_id = :user_id',
[':user_id' => $user->getId() ]
);
}
В целом хорошо. Шаблоны ошибок все одинаковые, за исключением текста ошибки. Для такого случая можно сделать один шаблон, в котором выводить текст исключения.
Вот так я доработал метод, если ID юзера нет то будет ошибка и соответственно если код не правильный будет тоже ошибка. Шаблон ошибок тот же и в зависимости от ошибки будет меняться в переменную $error.
public function activate(int $userId, string $activationCode)
{
try {
$user = User::getById($userId);
if ($user === null) {
throw new UserNotFoundException('Пользователь не найден');
}
$isCodeValid = UserActivationService::checkActivationCode($user, $activationCode);
if ($isCodeValid) {
$user->activate();
echo 'OK!';
return;
} else {
throw new IncorrectUserActivationCode('Некорректный код активации');
}
} catch (UserNotFoundException $e) {
$this->view->renderHtml('errors/404.php', ['error' => $e->getMessage()], 404);
} catch(IncorrectUserActivationCode $e) {
$this->view->renderHtml('errors/404.php', ['error' => $e->getMessage()], 404);
}
}
Удаление производил сразу после активации пользователя при условии что активация произошла успешно:
public static function checkActivationCode(User $user, string $code): bool
{
$db = Db::getInstance();
$result = $db->query(
'SELECT * FROM ' . self::TABLE_NAME . ' WHERE user_id = :user_id AND code = :code',
[
'user_id' => $user->getId(),
'code' => $code
]
);
if (!empty($result)) {
$db->query(
'DELETE FROM ' . self::TABLE_NAME . ' WHERE user_id = :user_id AND code = :code',
[
'user_id' => $user->getId(),
'code' => $code
]
);
}
return !empty($result);
}
Сразу подумал и сделал отдельный метод для удаления
public static function DeleteUserActivationCode(int $userId, string $activationCode): void
{
$db = Db::getInstance();
$db->query(
'DELETE FROM ' . self::TABLE_NAME . ' WHERE user_id = :user_id AND code = :code',
[
'user_id' => $userId,
'code' => $activationCode
]
);
}
но не знал как будет правильнее, ведь здесь должны тоже получить какое нибудь булевое значение, и подумал что логичнее будет сразу же удалить если активация уже прошла, как прислал выше. Поступил ли я правильно? Если нет, хотелось бы совет в этом направлении. Спасибо))
А что значит проверять? Тест написать хотите или что?
Как мне кажется, после того как этот функционал написали, достаточно убедиться что код удаляется в базе после активации аккаунта.
public function activate(int $userId, string $activationCode): void
{
$user = User::getById($userId);
if ($user === null) {
throw new UserActivationException('Пользователь не найден');
}
if ($user->isConfirmed()) {
throw new UserActivationException('Пользователь уже активирован');
}
$isCodeValid = UserActivationService::checkActivationCode($user, $activationCode);
if (!$isCodeValid) {
throw new UserActivationException('Неверный код активации');
}
$user->activate();
UserActivationService::deleteActivationCode($user);
$this->view->renderHtml('users/activation.php');
}
Ну и объединил в один шаблон activation.php:
<?php include __DIR__ . '/../header.php'; ?>
<div style="text-align: center;">
<?php if (!empty($error)): ?>
<h1>Не удалось активировать аккаунт</h1>
<?= $error ?>
<?php else: ?>
<h1>Активация аккаунта прошла успешно!</h1>
<?php endif; ?>
</div>
<?php include __DIR__ . '/../footer.php'; ?>
Нет. В index.html его выносить тоже не имело смысла. Такого рода исключения должны бросаться в слое модели и обрабатываться в слое контроллера. Но конкретно здесь не нужно вообще исключений, достаточно if-else использовать для рендеринга разных шаблонов.
Добрый день. По технологии ORM мы не должны были создать класс users_activation_codes со свойствами как у таблици? Или если это не нужно, то не обязательно создавать класс под каждую таблицу в БД.
public function activate(int $userId, string $activationCode): void
{
try {
$user = User::getById($userId);
if ($user === null) {
throw new ActivationException('Пользователь не найден');
}
if ($user->getConfirmed() === true) {
throw new ActivationException('Пользователь уже подтвержден');
}
$isCodeValid = UserActivationService::checkActivationCode($user, $activationCode);
if (!$isCodeValid) {
throw new ActivationException('Неверный код активации');
} else {
$user->activate();
$this->view->renderHtml('users/activationSuccessful.php');
UserActivationService::deleteActivationCode($userId);
}
} catch (ActivationException $error) {
$this->view->renderHtml('users/activationError.php', ['error' => $error->getMessage()]);
}
}
UserActivationService.php
// удаление кода активации из бд
public static function deleteActivationCode(int $userId): void
{
$db = Db::getInstance();
$delete = $db->query(
'DELETE FROM `' . self::TABLE_NAME . '` WHERE user_id = :user_id',
[':user_id' => $userId]
);
}
templates/users/activationError.php
<?php include __DIR__ . '/../header.php'; ?>
<div style="text-align: center;">
<h1>Не удалось активировать учетную запись!</h1>
<?= $error ?>
</div>
<?php include __DIR__ . '/../footer.php'; ?>
templates/users/activationSuccessful.php
<?php include __DIR__ . '/../header.php'; ?>
<div style="text-align: center;">
<h1>Учетная запись успешно активирована!</h1>
</div>
<?php include __DIR__ . '/../footer.php'; ?><?php
Не имеет смысла сравнивать true с true, чтобы получить true :)
if (!$isCodeValid) {
throw new ActivationException('Неверный код активации');
} else {
$user->activate();
$this->view->renderHtml('users/activationSuccessful.php');
UserActivationService::deleteActivationCode($userId);
}
Если уж бросили исключение, зачем тогда писать else? Код после выбрасывания исключения уже не будет дальше выполняться. А если исключение не возникло, то он просто будет выполняться строчка за строчкой и оборачивать его в блок else опять-таки не имеет смысла, только усложняет код за счёт лишней вложенности.
Не имеет смысла также бросать исключения в контроллере и тут же их ловить. Суть в том чтобы их бросать в одном слое, а ловить в другом.
метод следует назвать isConfirmed, он должен возвращать булево значение.
Писать блок try и прямо внутри него бросать исключения не имеет смысла. Их имеет смысл бросать и кидать в разных слоях приложения.
$this->view->renderHtml('errors/success.php', ['msg' => 'Пользователь успешно подтверждён'], 200);
Ошибка с названием success - это что-то невероятное :)
Для чего в шаблон передается это сообщение, если можно его просто в шаблоне захардкодить?
Следует создать в шаблонах папку activation, а в ней файл success.php. В нём прописать текст об успешной активации. При рендеринге передавать пустой массив с параметрами. По-хорошему метод renderHtml должен иметь второй и третий аргументы по-умолчанию: пустой массив и 200 соответственно.
Файл success.php должен быть в другой папке. Это моя не внимательность. А сообщение передаётся,потому что думал, что это страница успеха общая. Для всех случаев жизни. И чтоб можно было передать соответствующее сообщение
Вместо такой формы, корректно ли будет использовать разжеванную, типа:
return !empty($result)? true : false;
?
А то первую как то неохотно мозг воспринимает..
2.
Из комментария, относительно размещения в коде исключений и места их отлова:
Такого рода исключения должны бросаться в слое модели и обрабатываться в слое контроллера. Но конкретно здесь не нужно вообще исключений, достаточно if-else использовать для рендеринга разных шаблонов.
Почему тогда в уроке про статьи в качестве примера, как раз так и было - ислючения бросали в контроллере, а ловили в индексе?
public function activate(int $userId, string $activationCode)
{
try{
$user = User::getById($userId);
if($user === null) {
throw new \MyProject\Exceptions\NotFoundException('Такого пользователя не существует');
}
$isCodeValid = UserActivationService::checkAutorisationCode($user, $activationCode);
if($user->getIsConfirmed()) {
throw new InvalidArgumentException('Пользователь уже активирован, ничего не могу поделать!');
}
if(!$user->getIsConfirmed() && !$isCodeValid) {
throw new InvalidArgumentException('Пользователя действительно нужно активировать, но код активации подкачал! Попробуйте перейти по ссылке из сообщения, отправленного Вам на почту.');
}
if($isCodeValid){
$user->activate();
$this->view->renderHtml('users/activationSuccessful.php');
UserActivationService::cleanCodeFromDb($userId);
} else {
throw new InvalidArgumentException('Данная ссылка неактивна. Вероятно, вы попали сюда случайно.');
}
} catch(\MyProject\Exceptions\NotFoundException $e) {
$view = new \MyProject\View\View(__DIR__ . '/../../../templates/errors');
$view->renderHtml('404.php', ['error' => $e->getMessage()], 404);
} catch(\MyProject\Exceptions\InvalidArgumentException $e) {
$view = new \MyProject\View\View(__DIR__ . '/../../../templates/errors');
$view->renderHtml('404.php', ['error' => $e->getMessage()], 404);
}
}
activationSuccessful
<h1>Ну, добро пожаловать! Будем рады!</h1>
<a href="/">Вернуться на главную</a>
...
public function activate(int $userId, string $activationCode)
{
$user = User::getById($userId);
if ($user === null) {
throw new ActivationException('Такого пользователя не существует');
}
if ($user->isConfirmed()) {
throw new ActivationException('Пользователь уже активирован');
}
$isCodeValid = UserActivationService::checkActivationCode($user, $activationCode);
if (!$isCodeValid) {
throw new ActivationException('Код подтверждения авторизации не верен');
}
$user->activate();
UserActivationService::deleteActivationCode($userId);
$this->view->renderHtml('users/activationSuccessful.php',
['nick' => $user->getNickname()]);
}
...
...
public function activate(int $userId, string $activationCode)
{
$user = User::getById($userId);
if ($user === null) {
throw new ActivationException('Пользователя не существует');
}
if ($user->getIsConfirmed()) {
throw new ActivationException('Пользователь уже активирован');
}
$isCodeValid = UserActivationService::checkActivationCode($user, $activationCode);
if (!$isCodeValid) {
throw new ActivationException('Неверный код активации');
}
$user->activate();
UserActivationService::deleteActivationCode($userId);
$this->view->renderHtml('users/activationSuccessful.php', ['userName' => $user->getNickname()]);
}
...
Тогда придется поменять область видимости для isConfirmed в User.php, сейчас стоит protected.
Сделал по аналогии с другими геттерами в User.php.
Чем руководствоваться при определении области видимости для свойств?
1.
почему шаблон для активации нового пользователя находится в отдельной папке mail? Это же относится к юзерам, можно было его в папку users положить.
2.
Для чего нужен отдельный класс UserActivationService? Нельзя методы этого класса было оформить в классе User? Это же так же относится к юзеру. При этом метод activate находится в классе User. Почему тогда и этот метод не в классе UserActivationService? Или как вариант - разместить в папке сервисов, раз в названии присутствует слово Service. Запутанной структура воспринимается...
Я так понимаю, сама структуризация в этом плане - субъективна?
3.
В классе EmailSender мы передаем в письме готовую страничку-шаблон с линком. А в чем преимущество/недостаток, да и вообще принципиальные отличия от варианта - передать в письме просто текст со ссылкой. Мне второй вариант кажется намного проще и компактнее. Недостатков в нем по сравнению с первым не вижу.
По домашке:
Оставил всю логику в классе UserActivationService:
/**
* Class UserActivationService
* @package MyProject\Models\Users
*/
class UserActivationService
{
/**
*
*/
private const TABLE_NAME = 'users_activation_codes';
/**
* @param User $user
* @return string
* @throws DbException
*/
public static function activationCodeCreate(User $user): string
{
$code = bin2hex(random_bytes(16));
$db = Db::getDb();
$sql = 'INSERT INTO `'.self::TABLE_NAME.'` SET user_id=:user_id, code=:code;';
$db->queryWithoutGettingData($sql, [':user_id' => $user->getId(), ':code' => $code]);
return $code;
}
/**
* @param int $userId
* @param string $code
* @return bool
* @throws DbException
*/
public static function activationCodeCheck(int $userId, string $code): bool
{
$db = Db::getDb();
$sql = 'SELECT * FROM `' . self::TABLE_NAME . '` WHERE user_id=:user_id AND code=:code;';
$result = $db->queryWithGettingData($sql, [':user_id' => $userId, ':code' => $code]);
return !empty($result);
}
/**
* @param int $userId
* @throws DbException
*/
public static function activationCodeDelete(int $userId)
{
$db = Db::getDb();
$sql = 'DELETE FROM `' . self::TABLE_NAME . '` WHERE user_id=:user_id;';
$db->queryWithoutGettingData($sql, [':user_id' => $userId]);
}
/**
* @param int $userId
* @throws DbException
*/
public static function activate(int $userId): void
{
$user = User::getById($userId);
$user->setIsConfirmed(1);
$user->save();
}
}
Вызываю из метода activate в классе UsersController:
/**
* @param int $userId
* @param string $code
* @throws DbException
*/
public function activate(int $userId, string $code)
{
if (UserActivationService::activationCodeCheck($userId, $code)) {
UserActivationService::activate($userId);
$message = 'Your account is activated now';
UserActivationService::activationCodeDelete($userId);
$this->view->renderHtml('successful/successful.php', ['message' => $message]);
return;
} else {
$message = 'Something wrong with activation data. Check your activation link.';
$this->view->renderHtml('unsuccessful/unsuccessful.php', ['message' => $message]);
return;
}
}
Шаблон самый простой, с возможностью дальнейшего расширения:
Идентичный как для удачной, так и для неудачной активации.
Для выброса PDO исключений использую штатный механизм. DbExceptions выбрасываю на уровне запросов в БД в классе Db, а ловлю в index.php
Проверка на корректность активационных данных происходит при проверке кода и идентификатора юзера. Можно их конечно отдельно делать и выбрасывать исключения поотдельности, но смысла наверное в этом особого нет, пользователю вообще не нужно показывать какие конкретно данные активации не прошли. Пользователь же должен оперировать только на уровне готовой ссылки?
Потому что это письмо, а не веб-страничка. Но если хотите, можете и там положить, или вообще отдельную папку mail_templates завести, ничего от этого не изменится особо.
В ActiveRecord модель - это одна таблица. Чтобы в таблице юзеров не хранить токены был сделан отдельный сервис для работы с ними.
По ДЗ - отлично
Пользователь же должен оперировать только на уровне готовой ссылки?
Потому что это письмо, а не веб-страничка. Но если хотите, можете и там положить, или вообще отдельную папку mail_templates завести, ничего от этого не изменится особо.
Но шаблон для письма формируется же в формате странички? Я в своем третьем вопросе как раз про формат передачи пользователю кода активации спрашивал. Можно же не создавать отдельный класс EmailSender, не создавать шаблон в письмо, а просто в юзер-контролере, если регистрация успешна, отправить ссылку активации на указанную почту? Отредактировал ваш пример класса регистрации из урока:
public function signUp()
{
if (!empty($_POST)) {
try {
$user = User::signUp($_POST);
} catch (InvalidArgumentException $e) {
$this->view->renderHtml('users/signUp.php', ['error' => $e->getMessage()]);
return;
}
if ($user instanceof User) {
$code = UserActivationService::createActivationCode($user);
$receiver = $user->getEmail();
$subject = 'Activation on Blog';
$link = 'http://myproject.loc/users/'.$user->getId().'/activate/'.$code;
$body = 'To finish registration on Blog follow this link: '.$link;
mail($receiver, $subject, $body);
$this->view->renderHtml('users/signUpSuccessful.php');
return;
}
}
$this->view->renderHtml('users/signUp.php');
}
В чем может быть недостаток такого способа на данный момент, в будущем?
Потому что у более-менее серьезного письма всегда будет шаблон, в нем будет шапка и футер, которые тоже будут где-то храниться.
Когда логика отправки писем инкапсулирована внутри отдельного класса это даёт возможность изменять реализацию отправки. К примеру, захотите использовать PhpMailer, возьмете и поменяете вызов mail() в одном месте. А так придется по всему проекту менять. Более того, реализация отправки через phpMailer займёт далеко не одну строку, поэтому эту логику в дальнейшем всё равно придется выносить в отдельный класс, чтобы не было дублирования кода в 20 строк везде, где нужно отправить письмо.
...
public function activate(int $userId, string $activationCode)
{
try {
$user = User::getById($userId);
if ($user === null) {
throw new UserActivationException('Пользователь не найден.');
}
if ($user->isConfirmed()) {
throw new UserActivationException('Пользователь с таким email уже был подтвержден.');
}
$isCodeValid = UserActivationService::checkActivationCode($user, $activationCode);
if (!$isCodeValid) {
throw new UserActivationException('Неверный код активации!');
}
UserActivationService::deleteActivationCode($user);
$user->activate();
$this->view->renderHtml('users/userActivated.php');
return;
} catch (UserActivationException $e) {
$this->view->renderHtml('users/userActivationFail.php', ['error' => $e->getMessage()]);
}
}
...
Точно) исправил. Поменял название метода с getActivationStatus на isConfirmed так более понятно.
Просто я сомневался стоит ли его так называть это же геттер. Название isConfirmed здесь очень уместно подходит, сразу становится понятно что он возвращает булево значение, чего не скажешь про getActivationStatus не поймешь пока не увидишь)
Используйте строгое сравнение с null. Именно это значение лежит в переменной, когда пользователь неавторизован.
try {
throw new IdNotFoundException('ID был не найден!', 404);
} catch (IdNotFoundException $e) {
$this->view->renderHtml('errors/idNotFound.php', ['error' => $e->getCode(), 'eMessage' => $e->getMessage()], 404);
}
Для чего всё это?
if($isCodeValid == false)
Во-первых, вместо этого стоит писать
if(!$isCodeValid)
Во-вторых, вы уже выше проверили кейс когда код валидный. Там можно сразу удалить его и сделать return, а здесь уже не делать никаких условий.
С темой исключений у вас проблема. Рекомендую открывать урок по ним и перечитать еще пару раз. И начиная с того урока смотреть все комментарии, изучать другие решения учеников. С текущим уровнем понимания исключений дальше учиться нет смысла.
public function activate(int $userId, string $activationCode)
{
try {
$user = User::getById($userId);
if(empty($user)) {
throw new DbException('Пользователь не найден.');
}
if($user->getIsConfirmed()) {
throw new InvalidArgumentException();
}
if (UserActivationService::checkActivationCode($user, $activationCode)) {
$user->activate();
UserActivationService::removeActivationCode($user, $activationCode);
$this->view->renderHtml('/Users/ActivationSuccess.php');
}
}
catch (DbException $e) {
$this->view->renderHtml('/Users/ActivationError.php', ['error' => $e->getMessage()]);
}
catch (InvalidArgumentException $e) {
$this->view->renderHtml('/Users/ActivationAlready.php');
}
}
UserActivationService.php
public static function checkActivationCode(User $user, string $code): bool
{
$db = Db::getInstance();
$result = $db->query(
'select * from ' . self::TABLE_NAME . ' where user_id = :user_id and code = :code;',
[':user_id' => $user->getId(), ':code' => $code]
);
if (empty($result)) {
throw new DbException('Неверный код активации.');
}
return true;
}
Шаблоны: ActivationSuccess.php
<?php include __DIR__.'/../header.php' ?>
Поздравляем! Все прошло успешно, ваш email подтвержден.
<br>
<a href="/">На главную.</a>.
<?php include __DIR__.'/../footer.php' ?>
ActivationError.php
<?php include __DIR__.'/../header.php' ?>
<?= $error ?> Проверьте правильность ссылки для активации.
<?php include __DIR__.'/../footer.php' ?>
ActivationAlready.php
<?php include __DIR__.'/../header.php' ?>
Пользователь уже активирован.
<?php include __DIR__.'/../footer.php' ?>
...
<h1>Хьюстон, у нас очень большая проблема!</h1>
<?= $error ?>
...
P.S.
Возникла проблемы с отправкой письма на e-mail. В чем именно проблема разобраться не могу - настройки smtp менял, настройки безопасности аккаунта менял, но письмо как не приходило, так и не приходит. Пробовал ставить разные порты, создавать разные почты, гуглить разумеется, ответа пока не нашел.
if ($user === null){ //если пользователя нет - исключение
throw new \MyProject\Exceptions\UserNotFoundException('Такого пользователя не существет!');
}
От подобных комментариев пользы нет. Это и так понятно.
Проблема с форматированием. Делайте отступы и переносы как в уроках. Для этого в шторме можно нажать Ctrl+Alt+L
if (!$isCodeValid) {
throw new FailException('Невалидный код активации');
}
if ($isCodeValid) {//это условие бессмысленно, иной ситуации быть не может, мы ее обработали в условии выше
Потому что в реляционной базе данных у каждой записи должен быть уникальный идентификатор. ID пользователя конкретно в этом случае не подходит, потому что для одного пользователя можно сгенерить много кодов. Если же переписать так, что код для пользователя будет всегда один, то можно в качестве первичного ключа использовать user_id
public static function deleteActivationCode(User $user)
{
$db = Db::getInstance();
$result = $db->query(
'DELETE FROM `' . self::TABLE_NAME . '` WHERE `' . self::TABLE_NAME . '` . `user_id` = :user_id',
[
'user_id' => $user->getId()
]
);
if (empty($result)) {
echo 'Ссылки, по которой вы только что перешли, больше не существует. <br/>';
}
return !empty($result);
}
UsersController
public function activate(int $userId, string $activationCode)
{
try {
$user = User::getById($userId);
if ($user === null) {
throw new ActivateUserException('Пользователя не существует');
}
if ($user->getConfirmed() === 1) {
throw new ActivateUserException('Пользователь уже авторизован');
}
$isCodeValid = UserActivationService::checkActivationCode($user, $activationCode);
if ($isCodeValid) {
$user->activate();
UserActivationService::deleteActivationCode($user);
echo 'Ваш аккаунт активирован!';
}
if (!$isCodeValid) {
echo 'Ссылки не существует';
}
} catch (ActivateUserException $e) {
$this->view->renderHtml('/../templates/errors/500.php', ['error' => $e->getMessage()], 500);
}
}
Приветствую!
Все-таки где правильно бросать и ловить исключения. Я бросал в UsersController.php, а ловил в index.php. В коментах смотрю кто-то сделал так же, а кто-то бросал и ловил в UsersController.php.
Из ваших ответов четко не понял как правильно или как правильнее ) Хотя работает получается так и так.
Спасибо.
То, что бросается в модели, должно обработаться в контроллере. То, что бросается в контроллере, должно обработаться во фронт-контроллере (index.php).
Можно бросить исключение в модели, поймать его в контроллере, и дальше уже бросить ещё одно и поймать его во фронт-контроллере. То есть исключения уровня модели не должны лететь напрямую во фронт-контроллер. Модель не должна бросать исключения вроде "вернуть 404 ошибку", она ничего не должна знать о таких вещах.
Модель может кинуть исключение вроде "код активации некорректен". Контроллер должен его поймать и отправить юзеру ошибку с кодом 400, например, и с текстом, взятым из исключения модели.
UsersController
UserActivationService
Так же для проверки на "если в ссылку активации подставить несуществующего пользователя" в методе getById бросаю исключение при получении пустого массива после запроса в БД. \
Все верно?
Неправильно - в процессе получения пользователя по id никак не должно бросаться исключение с типом UserActivationException. UserActivationException - только для ошибок, связанных с активацией. Само по себе получение пользователя из базы никак не связано с активацией.
а где шаблон для успешного случая?
конкретно здесь !$result и !empty($result) - дадут просто противоположные значения. Не имеет смыла здесь кидать исключение - достаточно только вернуть true или false. Этот метод должен сказать, является ли код валидным или нет. То есть если он невалидный - это не исключительная ситуация, а вполне себе штатная. Поэтому исключение здесь не нужно.
Таким образом, в контроллере вам не нужно ничего ловить, нужно просто проверять пользователя на !== null и то что метод проверки кода вернул true.
Согласен, еще не до конца понимаю где кидать и ловить нужно, с простым рендерингом ошибок было легче. Буду разбираться
Ничего, я тоже поначалу не понимал, даже когда устраивался на работу - это придет с опытом.
UsersActivationService:
Функцию delete провожу без сверки с кодом, так как мы запускаем её непосредственно после проверки кода на валидность, или это открывает возможность появления каких-либо ошибок?
UsrsController:
Я пытался учесть замечание Sparky, про то, что Activation-исключение бросается в процессе получения пользователя, но ума не приложу, как сделать правильно... нужно бросать другое исключение? или выносить catch в index?
Ещё не сделал шаблоны, ибо это не сложно, но времяёмко, а уже поздно и пора спать) Поэтому проторопился и сделал просто вывод месаджами(
1) Удаление кода без проверки кода в модели - норм. Ошибки вряд ли тут будут.
2) Логику, отвечающую за проверку существования кода можно перенести внутрь UserActivationService::checkActivationCode(). Если кода нет - просто возвращать false, исключение тут не нужно.
3) Исключения вы бросаете в нужном месте. Не надо ничего усложнять, у Вас все просто и понятно.
4) Когда код активации неверен - тоже можно бросить исключение.
5) Структуру кода можно переделать так, что сначала проверяются все исключительные ситуации и бросаются исключения, а затем, если все хорошо, просто работает код для успешного исхода. Суть такая:
Мне кажется есть пара нелогичных моментов, в том числе и в самом задание:
Сначала мы выводим сообщение о том, что нет юзера, причём код активации может быть любой. Можно так выяснить сколько есть пользователей и их id. Мне кажется, что лучше выводить менее информативное сообщение, вроде такого: "некорректная ссылка активации".
Отсюда вывод, что нужно проверять только 2 параметра, юзера и сам код активации.
Вопросы:
UserController
далее, шаблон с ошибками(идентичен 500.php)
Ну и шаблон успешной авторизации
В условии проверяется другое.
В остальной логике все ок.
Добрый день! Решение д/з:
UserActivationService
UsersController
nonexistensCode
successfulActivation
Получается вот так?
getIsConfirmed() лучше просто isConfirmed(), и возвращаться должно булево значение, а не единичка.
В остальном - отличная домашка!
Подскажите, в чем проблема? Мой код соответствует коду урока но после перехода по ссылке в письме выдает ошибку:
Никак не могу разобраться.
Ожидается, что в метод прилетит объект класса User, а прилетел null. Используйте Xdebug, и учитесь читать ошибки, на этом уроке не должно возникать подобных вопросов.
Проблема была в двух опечатках в sql запросе. Невнимательность = минус день.
Со временем научитесь разбираться быстрее)
1
2
3
Правильно что надо два шаблона?
Тут этого быть не должно. Должно быть в вызывающем этот метод коде.
Исключения бросать и тут же ловить в этом же методе - бесполезное занятие. Можно сразу вызывать рисование шаблона с ошибкой и делать return.
2 шаблона - это ок
Доброго времени суток автор, скажите а как все это проверить и настроить на linux ubuntu 19.04 я установил апач c php mysql но письма не отправляются. При поиске в интернет нашел статьи но они очень старые не подскажешь куда смортеть и как фиксить?
Погуглите, готовой инструкции на этот случай у меня нет.
Поднимай теперь почтовый сервис с помощью sendmail, либо, еще вариант PEAR Mail и Net_SMTP. Reply от гугл почты можно использовать, как и в уроке по OpenServer.
Но, честно, не знаю зачем с локалки отправлять письма на реальный сервер, можно фейковый сервер поднять на файлах и также в почтовом клиенте они будут появляться. У меня конфиг xampp + Evolution. Погугли про xampp на линуксе, мануалы даже на русском есть.
а это метод deleteActivationCode :
вот добавил ловлю исключений в index :
и вот такие шаблоны добавил для каждого случая :
В целом хорошо. Шаблоны ошибок все одинаковые, за исключением текста ошибки. Для такого случая можно сделать один шаблон, в котором выводить текст исключения.
Спасибо, понял, исправлю
Вот так я доработал метод, если ID юзера нет то будет ошибка и соответственно если код не правильный будет тоже ошибка. Шаблон ошибок тот же и в зависимости от ошибки будет меняться в переменную $error.
Тут можно инвертировать условие, и код станет с меньшей вложенностью:
Почему сразу не подумал, спасибо))
Придет с практикой, не за что
Сразу подумал и сделал отдельный метод для удаления
но не знал как будет правильнее, ведь здесь должны тоже получить какое нибудь булевое значение, и подумал что логичнее будет сразу же удалить если активация уже прошла, как прислал выше. Поступил ли я правильно? Если нет, хотелось бы совет в этом направлении. Спасибо))
Всё ок, удалять можно сразу после активации. А вот имена методов пишутся с маленькой буквы.
Надо ли проверять результат удаления или нет и каким образом?
А что значит проверять? Тест написать хотите или что?
Как мне кажется, после того как этот функционал написали, достаточно убедиться что код удаляется в базе после активации аккаунта.
UserActivationService.php:
UsersController.php:
Шаблон activationFailed.php:
Шаблон activationSuccessful.php:
Нет смысла бросать исключение и тут же его ловить в том же методе. В остальном всё ок
Понял, спасибо большое! Вынес catch в index.php:
Экшн activate приобрел следующий вид:
Ну и объединил в один шаблон activation.php:
Нет. В index.html его выносить тоже не имело смысла. Такого рода исключения должны бросаться в слое модели и обрабатываться в слое контроллера. Но конкретно здесь не нужно вообще исключений, достаточно if-else использовать для рендеринга разных шаблонов.
Ладно, спасибо за ответы! Видимо я не до конца понимаю когда надо использовать исключения, а когда нет. Позже попытаюсь разобраться)
Ну, в принципе, первый вариант допустим. Исключения бросаются в исключительных ситуациях - когда дальнейшее выполнение кода не имеет смысла.
Так допустим или недопустим?)
Допустим, хоть и коряв, потому что проще будет смотреться конструкция с if-else.
Попробую еще раз) Убрал исключения, пользовался только if-else. Так норм?
UsersController.php:
UserActivationService.php:
Шаблон activation.php:
Отлично)
Добрый день. По технологии ORM мы не должны были создать класс users_activation_codes со свойствами как у таблици? Или если это не нужно, то не обязательно создавать класс под каждую таблицу в БД.
Можно было сделать и так. Но здесь была настолько простая бизнес-логика, что слой ORM не добавит простоты, а только всё усложнит.
То есть не стоит делать ORM для таблицы, где атрибутов так мало или как?
Однозначного ответа на такой вопрос нет. Но в данном конкретном случае орм будет лишней
Спасибо за урок!
UsersController.php
UserActivationService.php
templates/users/activationError.php
templates/users/activationSuccessful.php
Просто
Не имеет смысла сравнивать true с true, чтобы получить true :)
Если уж бросили исключение, зачем тогда писать else? Код после выбрасывания исключения уже не будет дальше выполняться. А если исключение не возникло, то он просто будет выполняться строчка за строчкой и оборачивать его в блок else опять-таки не имеет смысла, только усложняет код за счёт лишней вложенности.
Не имеет смысла также бросать исключения в контроллере и тут же их ловить. Суть в том чтобы их бросать в одном слое, а ловить в другом.
UsersController.php
UserActivationService.php
somethingWrong.php
success.php
метод следует назвать isConfirmed, он должен возвращать булево значение.
Писать блок try и прямо внутри него бросать исключения не имеет смысла. Их имеет смысл бросать и кидать в разных слоях приложения.
Ошибка с названием success - это что-то невероятное :)
Для чего в шаблон передается это сообщение, если можно его просто в шаблоне захардкодить?
Следует создать в шаблонах папку activation, а в ней файл success.php. В нём прописать текст об успешной активации. При рендеринге передавать пустой массив с параметрами. По-хорошему метод renderHtml должен иметь второй и третий аргументы по-умолчанию: пустой массив и 200 соответственно.
В целом по логике неплохо.
Файл success.php должен быть в другой папке. Это моя не внимательность. А сообщение передаётся,потому что думал, что это страница успеха общая. Для всех случаев жизни. И чтоб можно было передать соответствующее сообщение
Нуу, в целом, не вижу препятствий сделать так.
1.
Вместо такой формы, корректно ли будет использовать разжеванную, типа:
?
А то первую как то неохотно мозг воспринимает..
2.
Из комментария, относительно размещения в коде исключений и места их отлова:
Почему тогда в уроке про статьи в качестве примера, как раз так и было - ислючения бросали в контроллере, а ловили в индексе?
Домашка
Красота)
UsersController
activationSuccessful
Нет смысла бросать исключения в том же слое, что и ловить их. Проще сразу рендерить шаблон с ошибкой.
Файл UsersController.php:
Файл UserActivationService.php:
activationError.php:
activationSeccessful.php:
У меня вопрос:
Надо ли после удаления кода из базы сбрасывать счетчик - автоматический индекс?
Нет смысла бросать исключения в том же слое, что и ловить их. Проще сразу рендерить шаблон с ошибкой.
Нет, не рекомендую вообще когда-либо его трогать
Значит так должно быть?
UsersController.php:
Или все-таки с исключениями, только ловить их в index.php:
Ловить их в index.php, конечно. У вас же там уже есть обработка их даже.
Исправил. UsersController.php:
index.php:
Отлично!
Мне кажется index.php вообще ничего не должен знать о регистрации и активации пользователей.
Согласен. Правильнее добавить обработку в конкретном контроллере.
src/MyProject/Controllers/UserController.php
src/MyProject/Models/Users/UserActivationService.php
templates/errors/ActivationError.php
templates/users/activationSuccessful.php
getIsConfirmed() стоит переименовать в isConfirmed
Тогда придется поменять область видимости для isConfirmed в User.php, сейчас стоит protected.
Сделал по аналогии с другими геттерами в User.php.
Чем руководствоваться при определении области видимости для свойств?
Не не, метод с таким именем сделать. Свойство по-прежнему private. Методы, возвращающие булево значение стоит именовать с is... has... are и т.п.
Понятно, спасибо!
Отлично!
1.
почему шаблон для активации нового пользователя находится в отдельной папке mail? Это же относится к юзерам, можно было его в папку users положить.
2.
Для чего нужен отдельный класс UserActivationService? Нельзя методы этого класса было оформить в классе User? Это же так же относится к юзеру. При этом метод activate находится в классе User. Почему тогда и этот метод не в классе UserActivationService? Или как вариант - разместить в папке сервисов, раз в названии присутствует слово Service. Запутанной структура воспринимается...
Я так понимаю, сама структуризация в этом плане - субъективна?
3.
В классе EmailSender мы передаем в письме готовую страничку-шаблон с линком. А в чем преимущество/недостаток, да и вообще принципиальные отличия от варианта - передать в письме просто текст со ссылкой. Мне второй вариант кажется намного проще и компактнее. Недостатков в нем по сравнению с первым не вижу.
По домашке:
Оставил всю логику в классе UserActivationService:
Вызываю из метода activate в классе UsersController:
Шаблон самый простой, с возможностью дальнейшего расширения:
Идентичный как для удачной, так и для неудачной активации.
Для выброса PDO исключений использую штатный механизм. DbExceptions выбрасываю на уровне запросов в БД в классе Db, а ловлю в index.php
Проверка на корректность активационных данных происходит при проверке кода и идентификатора юзера. Можно их конечно отдельно делать и выбрасывать исключения поотдельности, но смысла наверное в этом особого нет, пользователю вообще не нужно показывать какие конкретно данные активации не прошли. Пользователь же должен оперировать только на уровне готовой ссылки?
Да
Но шаблон для письма формируется же в формате странички? Я в своем третьем вопросе как раз про формат передачи пользователю кода активации спрашивал. Можно же не создавать отдельный класс EmailSender, не создавать шаблон в письмо, а просто в юзер-контролере, если регистрация успешна, отправить ссылку активации на указанную почту? Отредактировал ваш пример класса регистрации из урока:
В чем может быть недостаток такого способа на данный момент, в будущем?
Потому что у более-менее серьезного письма всегда будет шаблон, в нем будет шапка и футер, которые тоже будут где-то храниться.
Когда логика отправки писем инкапсулирована внутри отдельного класса это даёт возможность изменять реализацию отправки. К примеру, захотите использовать PhpMailer, возьмете и поменяете вызов mail() в одном месте. А так придется по всему проекту менять. Более того, реализация отправки через phpMailer займёт далеко не одну строку, поэтому эту логику в дальнейшем всё равно придется выносить в отдельный класс, чтобы не было дублирования кода в 20 строк везде, где нужно отправить письмо.
UsersController.php
что возвращает этот метод? какой тип данных?
текст ошибки не про то
Точно) исправил. Поменял название метода с getActivationStatus на isConfirmed так более понятно.
Просто я сомневался стоит ли его так называть это же геттер. Название isConfirmed здесь очень уместно подходит, сразу становится понятно что он возвращает булево значение, чего не скажешь про getActivationStatus не поймешь пока не увидишь)
Отличное название)
И в целом дз тоже)
В принципе работает :/
?
Используйте строгое сравнение с null. Именно это значение лежит в переменной, когда пользователь неавторизован.
Для чего всё это?
Во-первых, вместо этого стоит писать
Во-вторых, вы уже выше проверили кейс когда код валидный. Там можно сразу удалить его и сделать return, а здесь уже не делать никаких условий.
С темой исключений у вас проблема. Рекомендую открывать урок по ним и перечитать еще пару раз. И начиная с того урока смотреть все комментарии, изучать другие решения учеников. С текущим уровнем понимания исключений дальше учиться нет смысла.
Хорошо, понял. Согласен с исключением у меня проблемы(
Буду перечитывать исключение...
Ничего страшного, шаг назад, чтобы потом сделать два вперед
Всегда используйте фигурные скобки для блоков if-else.
аналогично
Почему 404 код?
И почему сравниваете с null? Там ведь булево значение возвращается.
И опять бросаете исключение и ловите в том же слое. Я уже несколько раз писал вам об этом.
UsersController.php
UserActivationService.php
Шаблоны: ActivationSuccess.php
ActivationError.php
ActivationAlready.php
С исключениями полная ерунда получилась. Смысл бросать их и ловить в одном и том же слое?
При чем тут Db? Это ошибка базы?
Где контекст? Как понять, что произошло?
UserActivationService.php
UsersController
index.php
ActivationError.php
P.S.
Возникла проблемы с отправкой письма на e-mail. В чем именно проблема разобраться не могу - настройки smtp менял, настройки безопасности аккаунта менял, но письмо как не приходило, так и не приходит. Пробовал ставить разные порты, создавать разные почты, гуглить разумеется, ответа пока не нашел.
От подобных комментариев пользы нет. Это и так понятно.
Проблема с форматированием. Делайте отступы и переносы как в уроках. Для этого в шторме можно нажать Ctrl+Alt+L
Письма приходят не на реальный майл, а в папку ОpenServer\OSPanel\userdata\temp\email это норм? или что -то неправильно сделано?
UserActivationService
UsersController
successfulActivation
activationFailed
Отлично
домашка, очень туго )
UsersController.php
UserActivationService.php
failActivation.php
goodActivation.php
FailExceptoin.php
В остальном отлично
точно, нет смысла, проверка же уже есть, спасибо
А зачем нам столбец id в таблице users_activation_codes, если мы нигде по нему не обращаемся к значениям из этой таблицы?
Потому что в реляционной базе данных у каждой записи должен быть уникальный идентификатор. ID пользователя конкретно в этом случае не подходит, потому что для одного пользователя можно сгенерить много кодов. Если же переписать так, что код для пользователя будет всегда один, то можно в качестве первичного ключа использовать user_id
UserActivationService
UsersController
Почему в контроллерах echo? Используйте шаблоны.
Отлично. В целом для таких вещей можно завести единственный шаблон, в который будет прокидываться текст сообщения для вывода.
Приветствую!
Все-таки где правильно бросать и ловить исключения. Я бросал в UsersController.php, а ловил в index.php. В коментах смотрю кто-то сделал так же, а кто-то бросал и ловил в UsersController.php.
Из ваших ответов четко не понял как правильно или как правильнее ) Хотя работает получается так и так.
Спасибо.
То, что бросается в модели, должно обработаться в контроллере. То, что бросается в контроллере, должно обработаться во фронт-контроллере (index.php).
Можно бросить исключение в модели, поймать его в контроллере, и дальше уже бросить ещё одно и поймать его во фронт-контроллере. То есть исключения уровня модели не должны лететь напрямую во фронт-контроллер. Модель не должна бросать исключения вроде "вернуть 404 ошибку", она ничего не должна знать о таких вещах.
Модель может кинуть исключение вроде "код активации некорректен". Контроллер должен его поймать и отправить юзеру ошибку с кодом 400, например, и с текстом, взятым из исключения модели.
Ок. Спасибо за ответ.
UsersController
UserActivationService
activationSuccessful
activationFailed
если он НЕнекорректный, то он точно корректный, второе условие лишнее
SQL запрос на создание таблицы
Необходимо ли проверять результат удаления или нет?
И как это сделать, у меня возвращается массив почему-то.
?В чем отличие между?
и
return($result);
Моя домашняя работа.
UsersController.php
Модель User.php
UserActivationService.php
activateFailed.php
activateSuccessful