К содержанию

[СДЕЛАЙ САМ] Авторизация и регистрация по СМС

Всем привет. Я ни на что не претендую, прекрасно понимаю, что вариантов решения данной задачи много, и мой, наверное, не лучший, но меня попросили написать решение и эту инструкцию, поэтому поехали.
Нам понадобится:
  1. Console
  2. CMP Generator
  3. AjaxForm
  4. miniShop2
!!! ВАЖНО!!! Корзина и оформление заказа должны быть разными страницами.

Начнём с чанка корзины. Добавим туда ссылку на страницу оформления заказа для авторизованных пользователей и кнопку открытия формы авторизации/регистрации для неавторизованных пользователей.
{if $_modx->user.id > 0}           
    <a href="{13 | url}">  <!-- 13 это id ресурса с формой оформления заказа -->
         <button type="submit" class="knopka2">
             Оформить заказ
       </button>
    </a>          
{else}
    <button type="button" data-toggle="modal" data-target="#modalSMSAuth" class="knopka2">
        Оформить заказ
    </button>
{/if}

Сюда же добавим модальное окно с формой авторизации/регистрации
<!-- MODAL -->
<div class="modal" id="modalSMSAuth" tabindex="-1">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Modal title</h5>
        <button type="button" class="btn-close" data-dismiss="modal" aria-label="Close">X</button>
      </div>
      <div class="modal-body row">
            {'!AjaxForm' | snippet:[
                'snippet' =>'smsAuth',
                'form' =>'@FILE chunks/forms/checkcodeForm.html',
                'redirectTo' => 12 
            ]}
      </div>
    </div>
  </div>
</div>
<!-- /MODAL -->

Чанк с формой авторизации/регистрации checkcodeForm.html выглядит так
<form class="col-12">
  <div class="row">
    <label class="col-12">
      <input type="tel" class="d-block input_sms_nam" name="phone" placeholder="+7(999)888-77-66">
      <span class="error error_phone"></span>
    </label>
    <label class="col-12">
        <small class="modal__text">Нет пароля или Вы его забыли? 
 Оставьте поле пустым, и мы отправим новый пароль в СМС</small>
      <input type="text" class="d-block input_sms_cod" name="code" placeholder="Пароль">
      <span class="error error_code"></span>
    </label>
    <div class="col-lg-6 offset-lg-3">
      <button class="btn btn-primary">Войти</button>
    </div>
  </div>
</form>

Чтобы закончить с клиентской частью, создаем файл с названием, например, sms-auth.js, с таким контентом
// Маска ввода номера телефона
let phoneMask = document.querySelectorAll('input[type="tel"]');
for (let i = 0; i < phoneMask.length; i++) {
    phoneMask[i].addEventListener('keydown', function (event) {
        if (!(event.key == 'ArrowLeft' || event.key == 'ArrowRight' || event.key == 'Backspace' || event.key == 'Tab')) {
            event.preventDefault()
        }
        let mask = '+7 (111) 111-11-11'; // Задаем маску

        if (/[0-9\+\ \-\(\)]/.test(event.key)) {

            let currentString = this.value;
            let currentLength = currentString.length;

            if (/[0-9]/.test(event.key)) {
                for (let i = currentLength; i < mask.length; i++) {
                    let number = event.key;
                    if (mask[i] == '1') {
                        if (i == 4 && number != '9') {
                            number = '';
                        }
                        this.value = currentString + number;
                        break;
                    }
                    currentString += mask[i];
                }
            }
        }
    });
}

// Обработка ответа сервера
$(document).on('af_complete', function (e, response) {
    if(response.data.redirectTo){
        setTimeout(function(){
            document.location.href = response.data.redirectTo; // просто делаем редирект на нужную страницу
        },2000);
    } 
});

И внесём небольшую правку в исходники ajaxForm, а именно закомментируем в файле assets/components/ajaxform/js/default.js 66 строку, там должно быть это
form[0].reset();

Для отправки СМС будем использовать api SMSЦентр. Скачиваем файл smsc_api.php и в начале файла меняем
// SMSC.RU API (smsc.ru) версия 3.8 (03.07.2019)
define("SMSC_LOGIN", "login");            // логин клиента
define("SMSC_PASSWORD", "password");    // пароль
define("SMSC_POST", 0);                    // использовать метод POST
define("SMSC_HTTPS", 0);                // использовать HTTPS протокол
define("SMSC_CHARSET", "windows-1251");    // кодировка сообщения: utf-8, koi8-r или windows-1251 (по умолчанию)
define("SMSC_DEBUG", 0);                // флаг отладки
define("SMTP_FROM", "api@smsc.ru");     // e-mail адрес отправителя
на
// Подключаем Modx API
define('MODX_API_MODE', true);
require_once $_SERVER['DOCUMENT_ROOT'] . '/index.php';

// SMSC.RU API (smsc.ru) версия 3.8 (03.07.2019)

define("SMSC_LOGIN", $modx->getOption('smsc_login'));		// логин клиента
define("SMSC_PASSWORD", $modx->getOption('smsc_password'));	// пароль
define("SMSC_POST", $modx->getOption('smsc_post'));			// использовать метод POST
define("SMSC_HTTPS", $modx->getOption('smsc_https'));		// использовать HTTPS протокол
define("SMSC_CHARSET", $modx->getOption('smsc_charset'));	// кодировка сообщения: utf-8, koi8-r или windows-1251 (по умолчанию)
define("SMSC_DEBUG", $modx->getOption('smsc_debug'));		// флаг отладки
define("SMTP_FROM", $modx->getOption('smsc_smtp_from'));    // e-mail адрес отправителя
Все параметры указанные в методах $modx->getOption надо создать в системных настройках, думаю это понятно.

Теперь можно создать сниппет, который будет запускать отправку смс, назовём его codeSender
<?php
$file = MODX_CORE_PATH . 'components/codesender/smsc_api.php';
if(!file_exists($file)){
    $modx->log(1, 'FILE NOT FOUND');
}
include_once $file;

if(!isset($translit)){
    $translit = 1; // по умолчанию отправляем транслит, там можно впихнуть больше текста
}
$send = send_sms($number, $message, $translit);

return $send;
Чтобы можно было проверять отправленные коды, нужно их куда-то сохранять. Для этого создадим в базе данных новую таблицу modx_codesender на 5 полей с настройками как на скриншоте ниже

Теперь надо рассказать modx как с этой таблицей работать, для этого воспользуемся дополнением CMPGenerator. Открываем его интерфейс и нажимаем Create Package. Появившуюся форму заполняем как наследующем скрине

Потом жмём сохранить. Переходим в Console и выполняем вот такой код
$modx->addExtensionPackage('codesender', '[[++core_path]]components/codesender/model/');
Очищаем кэш и проверяем всё ли у нас получилось, выполнив следующий код
$sms = $modx->newObject('Codesender', array('username' => '79998887766', 'code' => '0000', 'text' => 'Test'));
$sms->save();
Если ошибок нет, значит всё хорошо, если есть внимательно проверяем пути к файлам.

Можем переходить к сниппету smsAuth, основные моменты я прокомментировал.
<?php
if(!($username = preg_replace('/[^0-9]/', '', trim($_POST['phone'])))){ // проверяем введен ли номер телефона
    return  $AjaxForm->error('Исправьте ошибки в форме',  array('phone' => 'Введите номер телефона'));
}
$user = $modx->getObject('modUser', array('username'=> $username)); // получаем пользователя
$inputCode = (int)$_POST['code']; // получаем введенный код
$codesender = $modx->getObject('Codesender', array('username' => $username)); // получаем данные об отправленном коде из БД
if(!$inputCode){ // если код не ввели
    if($codesender){ // и если мы его уже отправили
        $timeOut = 120; // задаем задержку повторной отправки 
        $sendTime = strtotime($codesender->get('date')); // получаем дату предыдущей отправки кода
        $now = time(); // получаем текущее время
        if(($now - $sendTime) < $timeOut){ // не даем слишком часто запрашивать пароль
            return $AjaxForm->error('Повторно запросить код Вы сможете через ' .($timeOut - ($now - $sendTime)) . ' cек.' ,   array());
        }
    }
    $inputCode = $modx->runSnippet('genPassword', array('length' => 6, 'type' => 'code')); // генерируем код
    $text =  'Пароль для сайта ' . $modx->getOption('http_host').': ' . $inputCode; // формируем сообщение
    $send = $modx->runSnippet('codeSender', array('number' => $username, 'message' => $text)); //отправляем СМС с кодом      
        
    // если отправка не удалась
    if(count($send) == 2){
        switch ($send[1]){
            case -7:
                $message = 'Неверный формат номера телефона. Код подтверждения не отправлен.';
            break;
            case -8:
                $message = 'Сообщение на указанный номер не может быть доставлено. Введите другой номер и повторите попытку';
            break;
            default:
                $message = 'Код подтверждения не отправлен. Введите другой номер и повторите попытку';
            break;
        }
        return  $AjaxForm->error($message,  array());
    }
        
    // записываем код в БД
    $sms = $modx->newObject('Codesender', array('username' => $username, 'code' => $inputCode, 'text' => $text));
    $sms->save();
    if($user){
        // обновляем пароль пользователя
        $user->set('password', $inputCode); 
        $user->save();
    }    
    return $AjaxForm->success('Пароль успешно отправлен', array());
}

$params = array('username' => $username, 'password' => $inputCode, 'group' => 2, 'active' => true, 'blocked' => false); // массив с данными для авторизации/регистрации
if(!$user){ // проверяем существует ли пользователь
    $dbcode = (int)$codesender->get('code'); // получаем отправленный код
    if($inputCode != $dbcode){ // проверяем совпадают ли коды
        return  $AjaxForm->error('Исправьте ошибки в форме',   array('code'=> 'Введен неверный пароль'));
    }
    $codesender->remove(); // удаляем код из БД если проверка прошла успешно

    // если нет - регистрируем
    $user = $modx->runSnippet('Registration', array('params' => $params)); 
    $user->set('password', $inputCode);
    $user->save();
}

$response = $modx->runSnippet('Authorization', array('params' => $params));
        
if(!$response['success']){
    return  $AjaxForm->error($response['message'],  array());
}
$redirectId = $scriptProperties['redirectTo'] ?: 1;
$redirectUrl = $modx->makeUrl($redirectId, 'web', '', 'full');
return $AjaxForm->success($response['message'], array('redirectTo' => $redirectUrl));

Тут вы вероятно заметили, что есть сниппет genPassword, изнутри он выглядит так
<?php
if(!$length){
    $length = $modx->getOption('password_min_length');
}
if(!$type){
    $type = 'pass';
}
//$modx->log(1, 'LENGTH ' . $length);
//$modx->log(1, 'TYPE ' . $type);
/* Более сложный генератор паролей с любыми символами */
$password = "";
/* Массив со всеми возможными символами в пароле */
switch ($type){
    case 'pass':
        $arr = array(
            'a', 'b', 'c', 'd', 'e', 'f',
            'g', 'h', 'i', 'j', 'k', 'l',
            'm', 'n', 'o', 'p', 'q', 'r',
            's', 't', 'u', 'v', 'w', 'x',
            'y', 'z', 'A', 'B', 'C', 'D',
            'E', 'F', 'G', 'H', 'I', 'J',
            'K', 'L', 'M', 'N', 'O', 'P',
            'Q', 'R', 'S', 'T', 'U', 'V',
            'W', 'X', 'Y', 'Z', '1', '2',
            '3', '4', '5', '6', '7', '8',
            '9', '0', '#', '!', "?", "&"
            );
        break;
    case 'hash':
        $arr = array(
            'a', 'b', 'c', 'd', 'e', 'f',
            'g', 'h', 'i', 'j', 'k', 'l',
            'm', 'n', 'o', 'p', 'q', 'r',
            's', 't', 'u', 'v', 'w', 'x',
            'y', 'z', 'A', 'B', 'C', 'D',
            'E', 'F', 'G', 'H', 'I', 'J',
            'K', 'L', 'M', 'N', 'O', 'P',
            'Q', 'R', 'S', 'T', 'U', 'V',
            'W', 'X', 'Y', 'Z', '1', '2',
            '3', '4', '5', '6', '7', '8',
            '9', '0'
            );
        break;    
    case 'code':
        $arr = array('1', '2', '3', '4', '5', '6', '7', '8', '9', '0');
    break;
}

for ($i = 0; $i < $length; $i++){
    $password .= $arr[mt_rand(0, count($arr) - 1)]; // Берём случайный элемент из массива
}

return $password;

Регистрацию я сделал так (Registration)
<?php
$user = $modx->newObject('modUser'); // создаем пользователя
$user->set('username', $params['username']); // задаем имя пользователя
$user->set('password', $params['password']); // задаем пароль

$profile = $modx->newObject('modUserProfile'); // создаем профиль
if(!$params['email']){
    $params['email'] = $params['username'] . '@' . $_SERVER['HTTP_HOST']; 
}
   
if(!$params['phone']){
    $params['phone'] = preg_replace('/(\d)(\d{3})(\d{3})(\d{2})(\d{2})$/', '+7(\2)\3-\4-\5', $params['username']);
}    

$params['extended'] = $extended;
// инициализируем поля
$profile->fromArray($params);
$user->addOne($profile); // добавляем профиль к пользователю
$profile->save(); // сохраняем профиль
$user->save(); // сохраняем пользователя
$user->joinGroup($params['group'],1); // добавляем пользователя в группу только после сохранения объекта пользователя
setcookie('user_id', $user->get('id'));
if(!$user){
    return false;
}
return $user;

Авторизацию вот так (Authorization)
<?php
$loginData = array(
    'username' => $params['username'],
    'password' => $params['password'],
    'rememberme' => 1,
    'login_context' =>  'web'
);
$loginResponse = $modx->runProcessor('/security/login', $loginData);
if ($loginResponse->isError()) {
    $modx->log(modX::LOG_LEVEL_ERROR, 'login error. Username: '.$params['username'].', Message: '.$loginResponse->getMessage());
    return array('success' => false, 'message' => $loginResponse->getMessage());
}
return array('success' => true, 'message' => 'Вы успешно авторизованы');

Теперь нужно сделать так, чтобы minishop2 использовал уже зарегистрированного пользователя в качестве получателя заказа. Для этого немного расширим класс core/components/minishop2/model/minishop2/msorderhandler.class.php, а точнее метод submit. Для этого создаем файл core/elements/code/customOrderHandler.class.php с вот таким содержимым
<?php
if(!class_exists('msOrderInterface')) {
    require_once dirname(dirname(dirname(__FILE__))) . '/components/minishop2/model/minishop2/msorderhandler.class.php';
}

class customOrderHandler extends msOrderHandler implements msOrderInterface{
    /**
     * @param array $data
     *
     * @return array|string
     */
    public function submit($data = array())
    {
        $response = $this->ms2->invokeEvent('msOnSubmitOrder', array(
            'data' => $data,
            'order' => $this,
        ));
        if (!$response['success']) {
            return $this->error($response['message']);
        }
        if (!empty($response['data']['data'])) {
            $this->set($response['data']['data']);
        }

        $response = $this->getDeliveryRequiresFields();
        if ($this->ms2->config['json_response']) {
            $response = json_decode($response, true);
        }
        if (!$response['success']) {
            return $this->error($response['message']);
        }
        $requires = $response['data']['requires'];

        $errors = array();
        foreach ($requires as $v) {
            if (!empty($v) && empty($this->order[$v])) {
                $errors[] = $v;
            }
        }
        if (!empty($errors)) {
            return $this->error('ms2_order_err_requires', $errors);
        }

        // custom code
        $user_id = $_COOKIE['user_id'] ?: $this->ms2->getCustomerId();
        if (empty($user_id) || !is_int($user_id)) {
            return $this->error(is_string($user_id) ? $user_id : 'ms2_err_user_nf');
        }

        $cart_status = $this->ms2->cart->status();
        if (empty($cart_status['total_count'])) {
            return $this->error('ms2_order_err_empty');
        }

        $delivery_cost = $this->getCost(false, true);
        $cart_cost = $this->getCost(true, true) - $delivery_cost;
        $createdon = date('Y-m-d H:i:s');
        /** @var msOrder $order */
        $order = $this->modx->newObject('msOrder');
        $order->fromArray(array(
            'user_id' => $user_id,
            'createdon' => $createdon,
            'num' => $this->getNum(),
            'delivery' => $this->order['delivery'],
            'payment' => $this->order['payment'],
            'cart_cost' => $cart_cost,
            'weight' => $cart_status['total_weight'],
            'delivery_cost' => $delivery_cost,
            'cost' => $cart_cost + $delivery_cost,
            'status' => 0,
            'context' => $this->ms2->config['ctx'],
        ));

        // Adding address
        /** @var msOrderAddress $address */
        $address = $this->modx->newObject('msOrderAddress');
        $address->fromArray(array_merge($this->order, array(
            'user_id' => $user_id,
            'createdon' => $createdon,
        )));
        $order->addOne($address);

        // Adding products
        $cart = $this->ms2->cart->get();
        $products = array();
        foreach ($cart as $v) {
            if ($tmp = $this->modx->getObject('msProduct', array('id' => $v['id']))) {
                $name = $tmp->get('pagetitle');
            } else {
                $name = '';
            }
            /** @var msOrderProduct $product */
            $product = $this->modx->newObject('msOrderProduct');
            $product->fromArray(array_merge($v, array(
                'product_id' => $v['id'],
                'name' => $name,
                'cost' => $v['price'] * $v['count'],
            )));
            $products[] = $product;
        }
        $order->addMany($products);

        $response = $this->ms2->invokeEvent('msOnBeforeCreateOrder', array(
            'msOrder' => $order,
            'order' => $this,
        ));
        if (!$response['success']) {
            return $this->error($response['message']);
        }

        if ($order->save()) {
            $response = $this->ms2->invokeEvent('msOnCreateOrder', array(
                'msOrder' => $order,
                'order' => $this,
            ));
            if (!$response['success']) {
                return $this->error($response['message']);
            }

            $this->ms2->cart->clean();
            $this->clean();
            if (empty($_SESSION['minishop2']['orders'])) {
                $_SESSION['minishop2']['orders'] = array();
            }
            $_SESSION['minishop2']['orders'][] = $order->get('id');

            // Trying to set status "new"
            $response = $this->ms2->changeOrderStatus($order->get('id'), 1);
            if ($response !== true) {
                return $this->error($response, array('msorder' => $order->get('id')));
            }

            // Reload order object after changes in changeOrderStatus method
            $order = $this->modx->getObject('msOrder', array('id' => $order->get('id')));

            /** @var msPayment $payment */
            if ($payment = $this->modx->getObject('msPayment',
                array('id' => $order->get('payment'), 'active' => 1))
            ) {
                $response = $payment->send($order);
                if ($this->config['json_response']) {
                    @session_write_close();
                    exit(is_array($response) ? json_encode($response) : $response);
                } else {
                    if (!empty($response['data']['redirect'])) {
                        $this->modx->sendRedirect($response['data']['redirect']);
                    } elseif (!empty($response['data']['msorder'])) {
                        $this->modx->sendRedirect(
                            $this->modx->context->makeUrl(
                                $this->modx->resource->id,
                                array('msorder' => $response['data']['msorder'])
                            )
                        );
                    } else {
                        $this->modx->sendRedirect($this->modx->context->makeUrl($this->modx->resource->id));
                    }

                    return $this->success();
                }
            } else {
                if ($this->ms2->config['json_response']) {
                    return $this->success('', array('msorder' => $order->get('id')));
                } else {
                    $this->modx->sendRedirect(
                        $this->modx->context->makeUrl(
                            $this->modx->resource->id,
                            array('msorder' => $response['data']['msorder'])
                        )
                    );

                    return $this->success();
                }
            }
        }

        return $this->error();
    }
}
В Console запускаем следующий код
if ($miniShop2 = $modx->getService('miniShop2')) {
    $miniShop2->addService('order', 'CustomOrderHandler',
        '{core_path}elements/code/customOrderHandler.class.php'
    );
}
В системных настройках меняем значение ms2_order_handler_class на customOrderHandler.

На этом всё!
Артур Шевченко
Артур Шевченко
17 мая 2021, 00:31
modx.pro
6 792
Поблагодарить автора Отправить деньги
Реклама

Комментарии: 25

Евгений Генералов
17 мая 2021, 07:41
Хорошая статья.
От себя могу добавить, сниппет genPassword можно не писать. У класса modUser есть метод generatePassword. В который тоже можно передать длину пароля
    Артур Шевченко
    17 мая 2021, 07:53
    Спасибо за комментарий, но причина появления сниппета genPassword не в длине пароля, а в его составе, в своём сниппите я могу генерировать числовой код, а если использовать стандартный механизм там будут разные символы, а не только числа. Людям удобнее вводить числа, как показала практика и надёжность пароля им не важна. Ещё я думаю, что наверное после авторизации надо менять пароль ещё раз на более сложный, чтоб никто другой войти не смог. Хотя возможно это излишняя предосторожность.
    Павел Бигель
    Павел Бигель Эксперт
    17 мая 2021, 10:48
    Простыня кода, три строчки текста, никаких объяснений, нулевая информативность.
      Артур Шевченко
      17 мая 2021, 11:55
      Что именно тебе непонятно в коде?
        Павел Бигель
        Павел Бигель Эксперт
        17 мая 2021, 12:29
        Мне как раз все понятно.
        {if $_modx->user.id > 0}           
            <a href="[[~13]]">  <!-- 13 это id ресурса с формой оформления заказа -->
                 <button type="submit" class="knopka2">
                     Оформить заказ
               </button>
            </a>          
        {else}
            <button type="button" data-toggle="modal" data-target="#modalSMSAuth" class="knopka2">
                Оформить заказ
            </button>
        {/if}
        Помесь стандартного и fenom синтаксиса это дурной тон.

        {'!AjaxForm' | snippet:[
                        'snippet' =>'custConfirm',
                        'form' =>'@FILE chunks/forms/confirmForm.html',
                        'validate'=>'phone:required'
                    ]} 
                    {'!AjaxForm' | snippet:[
                        'snippet' =>'custCheckCode',
                        'customValidators' => 'validateCode'
                        'form' =>'@FILE chunks/forms/checkcodeForm.html',
                        'validate'=>'phone:required,code:validateCode',
                        'redirectTo' => 13 
                    ]}
        Два некешированных вызова AjaxForm. А че ж не 5? Эту задачу можно решить одним вызовом сниппета.

        <?php
        $file = MODX_CORE_PATH . 'components/codesender/smsc_api.php';
        if(!file_exists($file)){
            $modx->log(1, 'FILE NOT FOUND');
        }
        include_once $file;
        
        if(!isset($translit)){
            $translit = 1; // по умолчанию отправляем транслит, там можно впихнуть больше текста
        }
        $send = send_sms($number, $message, $translit);
        
        return $send;
        Такого рода задачи решаются не сниппетами. Сниппеты это вообще про другое.
        Более того, ты же ниже создаешь пакет, какая проблема примутить к нему класс-контроллер?

        Тут вы вероятно заметили, что есть сниппет genPassword, изнутри он выглядит так
        Опять же а) Не решаются такие задачи сниппетами б) mt_rand не является безопасным для задач уровня генерации паролей. Ты бы это знал, если бы писал это сам.

        Итак, выходим на финишную прямую. Сниппет custCheckCode отвечает за проверку кода, авторизацию и регистрацию пользователей
        Снова, такие задачи не решаются сниппетами.

        // если ошибок нет
            if($_POST['phone']){
                $username = preg_replace('/[^0-9]/', '', trim($_POST['phone']));
                $params = array('username' => $username, 'password' => $_POST['code'], 'group' => 2, 'active' => true, 'blocked' => false);
                $params = array_merge($_POST, $params);
                $user = $modx->getObject('modUser', array('username' => $username));
                if(!$user){
                    $modx->log(1, 'Регистрация');
                    $user = $modx->runSnippet('Registration', array('params' => $params));
                }
                $user->set('password', $_POST['code']);
                $user->save();
                $response = $modx->runSnippet('Authorization', array('params' => $params));
                if(!$response['success']){
                    return  $AjaxForm->error($response['message'],  array());
                }
                $redirectId= $scriptProperties['redirectTo'] ?: 1; // при вызове сниппета мы передаём id ресурса, на который нужно сделать редирект, если не передаем, то редирект будет на главную.
                $redirectUrl = $modx->makeUrl($redirectId , 'web', '', 'full'); 
                return $AjaxForm->success($response['message'], array('redirectTo' => $redirectUrl)); 
            }
        }
        Из поста данные не валидируются. Пахнет безопасностью! Либо впадлу, либо не знаешь что это такое.
        Тут кстати гораздо логичнее была бы проверка на false.

        Кастомный валидатор validateCode содержит такой код:
        Опять никакой валидации. Еще и сырой запрос (вызов которого не оправдан ничем)

        Регистрацию я сделал так (Registration)
        Ни единой проверки на то что модель не сохранилась. Неверное сохранение аггеративных моделей.

        Итого: куски сомнительного кода не несущие абсолютно никакой ценности.
        Я бы ни слова ни сказал, даже плюсик бы залепил если бы тут было хоть какое-то объяснение происходящего.
        Почему так, а не иначе, почему это лучше, а это хуже.

        Итого ты не учишь, а даешь людям копировать плохие практики.
        Абсолютно заслуженный минус
          Артур Шевченко
          17 мая 2021, 13:00
          Люди предупреждены. А за ревью спасибо.
            Артур Шевченко
            17 мая 2021, 13:02
            Из поста данные не валидируются.
            Хочешь сказать FormIt их не проверяет?
              Павел Бигель
              Павел Бигель Эксперт
              17 мая 2021, 13:04
              Валидирует.
              У тебя валидация уровня required, я туда могу тебе прислать все что угодно.
                Артур Шевченко
                17 мая 2021, 13:12
                Согласен. Надо добавить к коду isNumber.
              Денис Мижеревич
              17 мая 2021, 13:05
              Раскритиковали…
              Напишите и выложите свой подобный кейс, а то желающих поделиться разработкой не много
                Павел Бигель
                Павел Бигель Эксперт
                17 мая 2021, 13:09
                Без проблем!
                100$ и я готов написать решение многократно элегантнее этого с пояснением каждого шага для новичков. Готовы проспонсировать статью?
                Дорого?
                Можете использовать компонент Office который стоит половину стоимости и реализует задачу гораздо лучше
                modstore.pro/packages/users/office
              Артур Шевченко
              17 мая 2021, 13:08
              Итого ты не учишь, а даешь людям копировать плохие практики.
              1. Не тот у меня уровень, чтобы кого-то учить.
              2. Я во вступлении написал, что это не лучший из возможных вариантов.
              3. Есть ты и твои комментарии, кто-то проникнется и не станет это копировать, кто-то допилит, а кому-то и так сойдет.
                Павел Бигель
                Павел Бигель Эксперт
                17 мая 2021, 14:04
                1. Ну так назови цикл статьей «Скопируй сам». Почему «Сделай»?
                2. Это не лучший, а прям наихудший способ. Кстати если так хотелось прикрутить авторизацию, то можно было доработать Login.
                  Артур Шевченко
                  17 мая 2021, 14:58
                  Это твоё личное мнение, мне нравится моё решение, оно простое, понятное и работает. Да оно далеко от идеала, но это не отменяет всего вышесказанного.
          Алекксандр
          Алекксандр Старожил
          01 сентября 2021, 19:33
          Извиняюсь. Только что столкнулся с modx. Пытаюсь решить задачу авторизации через СМС.
          Вполне логичный и читабельный проект, только хотелось поподробнее про пути к файлам.(где их создавать?).
            Артур Шевченко
            07 сентября 2021, 23:26
            Про какие пути?
              Алекксандр
              Алекксандр Старожил
              08 сентября 2021, 11:44
              Если ошибок нет, значит всё хорошо, если есть внимательно проверяем ПУТИ К ФАЙЛАМ. Про эти.
                Артур Шевченко
                08 сентября 2021, 11:49
                Я думал из контекста понятно, что речь идёт о пути к файла «компонента» codesender, вот про это [[++core_path]]components/codesender/model/
                  Алекксандр
                  Алекксандр Старожил
                  08 сентября 2021, 12:09
                  Как видно, не всем понятно из контекста.)))
                Алекксандр
                Алекксандр Старожил
                08 сентября 2021, 11:47
                При проверке выдает ошибку, о том что не может создать экземпляр класса.
                  Артур Шевченко
                  08 сентября 2021, 11:52
                  Что я могу сказать, у меня тоже так было нужно проверять пути, название файлов и классов.
              Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
              25