Куда прятать методы, использующие данные форм?

Обсуждаем, как правильно строить приложения
Ответить
Stasgar
Сообщения: 77
Зарегистрирован: 2016.07.15, 14:08

Куда прятать методы, использующие данные форм?

Сообщение Stasgar »

У меня есть модуль, в котором я описал работу восстановления пароля.

У меня имеются две модели форм:
1) (PasswordResetRequestForm) Форма для ввода email, на который придет ссылка с восстановлением (типа /change-password?token=token_value, токен генерируется)
2) (PasswordChangeForm) Форма для смены пароля, в запрос принимается get-параметр токена

Как логика работы форм реализована на данный момент:

Контроллер:

Код: Выделить всё

    public function actionResetPasswordRequest()
    {
        $model = new PasswordResetRequestForm();

        if(Yii::$app->request->Post('PasswordResetRequestForm'))
        {
            $model->attributes = Yii::$app->request->Post('PasswordResetRequestForm');

            if($model->validate())
            {
                define("EXPIRE_TIME", 30); //время в минутах, после которого токен считается не валидным

                $user = User::findOne(['user_email' => $model->email]);//получаем пользователя по имейлу

                //формирование записи в таблице reset_password_token
                $resetPasswordToken = new ResetPasswordToken();
                $resetPasswordToken->user_id = $user->user_id;
                $resetPasswordToken->token = sha1(mt_rand(10000, 99999).time());//генерация рандомного токена
                $resetPasswordToken->expires = Date('Y-m-d h:i:s', strtotime("+".EXPIRE_TIME." minutes"));

                $resetUrl = Url::base(true).Url::toRoute(['/change-password', 'token' => $resetPasswordToken->token]);

                if($resetPasswordToken->save())
                {
                    SendEmail::sendResetPasswordMail($model->email, $resetUrl);
                }

                return $this->render('ResetSuccess');
            }
        }

        return $this->render('PasswordResetRequestForm', ['model'=>$model]);
    }

    public function actionChangePassword($token = false)
    {
        $message = 'Ваша ссылка восстановления не действительна';

        if($token) //если token получен, то производим восстановление пароля
        {
            $model = new PasswordChangeForm();
            $resetPasswordToken = ResetPasswordToken::findOne(['token' => $token]);
            
            //проверка наличия и валидности токена
            if($resetPasswordToken && ! $resetPasswordToken->expires())
            {
                if(Yii::$app->request->post('PasswordChangeForm'))
                {
                    $model->attributes = Yii::$app->request->post('PasswordChangeForm');

                    if($model->validate())
                    {
                        $model->changeUserPassword($resetPasswordToken);
                        return $this->render('ChangeSuccess');
                    }
                }
                
                return $this->render('PasswordChange', ['model' => $model]);
                
            }

            $message = 'Срок действия вашей уникальной ссылки истек. Срок действия - 30 минут с момента получения';
        }

        return $this->render('@app/views/site/error', 
            [
                'name'=>'Ошибка валидации', 
                'message'=>$message,
            ]
        );
    }
В actionResetPasswordRequest(), как видно - в открытую прописал логику формирование записи в таблице reset_password_token, верен ли такой подход, если нет, то куда лучше спрятать этот код?

В actionChangePassword(), происходит проверка "! $resetPasswordToken->expires()", этот метод прописан в AR модели таблицы reset_password_token:

Код: Выделить всё

    /*
        Возвращает true, если время действия токена истекло
    */
    public function expires()
    {
        $currentDate = Date('Y-m-d h:i:s');
        $expiresDate = $this->expires;
        if( $expiresDate < $currentDate )
        {
            return true;
        }

        return false;
    }
правильно ли прятать подобную логику в AR модели?
Аватара пользователя
yiijeka
Сообщения: 3103
Зарегистрирован: 2012.01.28, 09:14
Откуда: Беларусь
Контактная информация:

Re: Куда прятать методы, использующие данные форм?

Сообщение yiijeka »

Тут целую книжку можно написать по вашему запросу :)

Правильно - не правильно, и то и то верно и зависит от многого.

В данном случае: Есть один контроллер и два метода. По всем документациям и умным книжкам говорится, что в контроллере нельзя писать код с логикой работы базы данных, моделей. Сейчас у вас она так и описана - типа ошибка. Но я бы оспорил - в данном случае у вас два простых метода, которые при одном виде быстро изменяются под нужды проекта - это не вызывает боль. Поэтому можно и так оставлять и не тратить силы на надуманные проблемы. Единственное но, тут код сложно покрыть тестами, но так как вы их не пишите, то всё ОК :)

Другая сторона: Если же вы имеете контроллере и в нём эти два метода + ещё парочку с кодом, в котором трудно разобраться и при внесении изменений в одно место ломается что-то в другом, то тут начинается "ваш код попахивает". Вы начинаете писать тесты, чтобы хоть как то оградить себя от "наступления на грабли" при внесении измнениий. Тесты пишутся с болью, появляются всё больше и больше ошибок, а вам хотелось то исправить одну строчку и совершенно в другом месте, но вас накрыло волной. Вот только в этом случае!!!! Только в этом!!!! Ваш код атстой и надо всё это дело переделывать согласно умным высказываниям. Первое - код выностится в отдельные классы(сервисы), PasswordChanger например, тут хранится вся логика смены пароля. В контроллере остаётся лишь несколько строчек кода, типа таких:

Код: Выделить всё

public function actionResetPasswordRequest()
    {        
        try {
             $pc= PasswordChanger::fromPostData(Yii::$app->request->post('PasswordResetRequestForm'));
             $pc->newPass();             
        } catch {
             return $this->render('PasswordResetRequestForm');
        }
        
        EmailSender::sendResetPasswordMail(Yii::$app->user->email);        
        return $this->render('ResetSuccess');       
    }
и всё это должно быть покрыто тестами! Вот тогда это будет на 99% правильно, но только для "другой стороны".
Stasgar
Сообщения: 77
Зарегистрирован: 2016.07.15, 14:08

Re: Куда прятать методы, использующие данные форм?

Сообщение Stasgar »

Да, я вас понял. Думаю лучше контроллер зрительно упрощать, и такую логику прятать в другие классы.
Ответить