За последние 24 часа нас посетили 22166 программистов и 1145 роботов. Сейчас ищут 866 программистов ...

Нужен совет по написанию класса валидатора

Тема в разделе "PHP для новичков", создана пользователем GTank, 21 июл 2018.

  1. GTank

    GTank Новичок

    С нами с:
    21 июл 2018
    Сообщения:
    31
    Симпатии:
    1
    Привет. Пишу свой велосипед т.е. OOP, MVC фреймворк. В процессе решил не писать проверки в моделе Аккаунта, а написать отдельный класс валидации форм (в итоге я его стащил с github =)).

    Идея заключалась в том, что к полю можно применить несколько методов валидации, допустим:
    1. Поле логин обязательно к заполнению.
    2. Логин должен состоять только из русскиз букв.
    3. Логин должен состоять не менее чем из 3-х символов.

    То есть чтобы для каждого неверного действия вываливалось по 1-му сообщению, а не пачкой или вообще в одну строчку.

    Код, который я нарыл рабочий на 100%, но в итоге столкнулся с тем, что не могу с помощью этого класса проверить, скажем email на существовании в БД…

    В ситуации, когда в базе данных уже есть такой пароль, email или логин нужно показать сообщение, что таковые имеются, но я не доганяю, что нужно делать. Лезть в БД в этом класе нет смысла, потому что это задача модели аккаунта.

    В этом классе возможно сделать такие проверки? Может подскажите другой класс примерно такой же сложности.

    В общем буду рад любому дельному совету.

    Код валидатора
    PHP:
    1. <?php
    2.  
    3. namespace components\libs;
    4.  
    5. class Validator
    6. {
    7.     private $field_name;
    8.     private $message;
    9.     private $type_of_rule;
    10.     private $param;
    11.     private static $rules = [];
    12.     private static $fields = [];
    13.     private static $errors = [];
    14.  
    15.  
    16.     public function __construct($field_name, $message, $type_of_rule, $param)
    17.     {
    18.         $this->field_name = $field_name;
    19.         $this->message = $message;
    20.         $this->type_of_rule = $type_of_rule;
    21.         $this->param = $param;
    22.     }
    23.  
    24.     public static function addRule($field_name, $message, $type_of_rule, $param = null)
    25.     {
    26.         self::$rules[] = new Validator($field_name, $message, $type_of_rule, $param);
    27.     }
    28.  
    29.     public static function addEntries($fields) {
    30.         foreach ($fields as $fieldname => $value) {
    31.             self::$fields[$fieldname] = self::sanitize($value);
    32.         }
    33.     }
    34.  
    35.     public static function validate() {
    36.         foreach (self::$rules as $rule) {
    37.             self::testRule($rule);
    38.         }
    39.     }
    40.  
    41.     public static function sanitize($text)
    42.     {
    43.         $text = trim(strip_tags($text));
    44.  
    45.         if (get_magic_quotes_gpc()) {
    46.             $text = stripslashes($text);
    47.         }
    48.         return $text;
    49.     }
    50.    
    51.     public static function getErrors()
    52.     {
    53.         if (count(self::$errors)) {
    54.             return self::$errors;
    55.         }
    56.         return false;
    57.     }
    58.  
    59.     public static function longerThan($value, $min)
    60.     {
    61.         if (strlen($value) >= $min) {
    62.             return true;
    63.         }
    64.         return false;
    65.     }
    66.  
    67.     public static function shorterThan($value, $max)
    68.     {
    69.         if (strlen($value) <= $max) {
    70.             return true;
    71.         }
    72.         return false;
    73.     }
    74.  
    75.     public static function asEmail($value)
    76.     {
    77.         if (filter_var($value, FILTER_VALIDATE_EMAIL)) {
    78.             return true;
    79.         }
    80.         return false;
    81.     }
    82.  
    83.     public static function asPhoneNumber($value)
    84.     {
    85.         if (preg_match("/^\(?[0-9]{3}\)? *-? *[0-9]{3} *-? *[0-9]{4}$/", $value)) {
    86.             return true;
    87.         }
    88.         return false;
    89.     }
    90.  
    91.     private static function testRule($rule)
    92.     {
    93.  
    94.         if (isset(self::$errors[$rule->field_name])) {
    95.             return;
    96.         }
    97.  
    98.         if (isset(self::$fields[$rule->field_name])) {
    99.             $value = self::$fields[$rule->field_name];
    100.         }
    101.         else {
    102.             $value = null;
    103.         }
    104.  
    105.         switch ($rule->type_of_rule) {
    106.             case 'required' :
    107.                 if (empty($value)) {
    108.                     self::$errors[$rule->field_name] = $rule->message;
    109.                     return;
    110.                 }
    111.                 break;
    112.             case 'minlength' :
    113.                 if (!(self::longerThan($value, $rule->param))) {
    114.                     self::$errors[$rule->field_name] = $rule->message;
    115.                     return;
    116.                 }
    117.                 break;
    118.             case 'maxlength' :
    119.                 if (!(self::shorterThan($value, $rule->param))) {
    120.                     self::$errors[$rule->field_name] = $rule->message;
    121.                     return;
    122.                 }
    123.                 break;
    124.             case 'email' :
    125.                 if (!(self::asEmail($value))) {
    126.                     self::$errors[$rule->field_name] = $rule->message;
    127.                     return;
    128.                 }
    129.                 break;
    130.             case 'phonenumber' :
    131.                 if (!(self::asPhoneNumber($value))) {
    132.                     self::$errors[$rule->field_name] = $rule->message;
    133.                     return;
    134.                 }
    135.                 break;
    136.         }
    137.     }
    138.  
    139. }

    В контролере прописивается всё следующим образом:
    PHP:
    1. public function indexAction()
    2.     {
    3.         if (isset($_POST['submit'])){
    4.  
    5.             $name = $_POST['name'];
    6.  
    7.             Validator::addRule(
    8.                 'name',
    9.                 'Поле name обязательное',
    10.                 'required');
    11.             Validator::addRule(
    12.                 'name',
    13.                 'Имя должно состоять не менне чем из 2-х символов',
    14.                 'minlength',
    15.                 3);
    16.  
    17.             Validator::addEntries($_POST);
    18.             Validator::validate();
    19.  
    20.             $errors = Validator::getErrors();
    21.  
    22.         }
    23.     }
     
  2. _ne_scaju_

    _ne_scaju_ Старожил

    С нами с:
    25 ноя 2016
    Сообщения:
    2.149
    Симпатии:
    118
    Дак у тебя есть проверка на email
    PHP:
    1. public static function asEmail($value)
    2.     {
    3.         if (filter_var($value, FILTER_VALIDATE_EMAIL)) {
    4.             return true;
    5.         }
    6.         return false;
    7.     }
    Как раз вот в этой строке if (filter_var($value, FILTER_VALIDATE_EMAIL)){} у тебя проверка.
     
  3. GTank

    GTank Новичок

    С нами с:
    21 июл 2018
    Сообщения:
    31
    Симпатии:
    1
    Мне нужна не проверка электронки на правильность, а проверка её существования в БД. То есть чтобы с такой же зарегистрироваться было невозможно.

    На данный момент я сделал просто:
    PHP:
    1.     public function registerAction(){
    2.  
    3.         if(isset($_POST['submit'])){
    4.  
    5.             $model = new Account;
    6.  
    7.             $name = $_POST['name'];
    8.             $email = $_POST['email'];
    9.             $password = $_POST['password'];
    10.  
    11.             $errors = false;
    12.  
    13.             if(!$model->checkName($name)){
    14.                 $errors[] = 'Имя не должно быть короче 2-х символов';
    15.             }
    16.             if(!$model->checkEmail($email)){
    17.                 $errors[] = 'Неправильный email';
    18.             }
    19.             if(!$model->checkPassword($password)){
    20.                 $errors[] = 'Пароль не должен быть короче 6-ти символов';
    21.             }
    22.             if($model->checkEmailExists($email)){
    23.                 $errors[] = 'Такой email уже исспользуется';
    24.             }
    25.             if ($errors == false){
    26.                 $model->register($name,$email,$password);
    27.                 header("Location: /");
    28.             }
    29.         }
    30.         var_dump($_POST);
    31.         $this->set(compact('errors','name','email','password'));
    32.     }

    Вот этот кусок кода
    PHP:
    1.            
    2. if($model->checkEmailExists($email)){
    3.     $errors[] = 'Такой email уже исспользуется';
    4. }
    ищет в таблице users электронку и возвращает её id, а в моделе это выглядит так:

    PHP:
    1.  public function checkEmailExists($email)
    2.     {
    3.         $params = [
    4.             'email' => $email
    5.         ];
    6.  
    7.         return $this->db->row('SELECT id FROM users WHERE email = :email', $params);
    8.     }
    Нужно написать функцию которая будет получать результат работы метода из модели и в массив getErrors() должно попасть что-то типа "Такой email уже исспользуется" и не пропускать до тех пор пока не будет введён email которого нет в БД.

    В общем хочется так, но пока что не получается. Не понимаю я как это всё связать. Ведь что-то простое он обрабатывает: правильность введённого того же email или длину логина, сложность пароля и т.п.

    Вот как проверить существование какой-то записи в БД и выдать ошибку... В общем не знаю как такое написать. По этому и обратился к вам.

    Помогут любые советы =)
     
  4. _ne_scaju_

    _ne_scaju_ Старожил

    С нами с:
    25 ноя 2016
    Сообщения:
    2.149
    Симпатии:
    118
    PHP:
    1.  public function checkEmailExists($email)
    2.     {
    3.         $params = [
    4.             'email' => $email
    5.         ];
    6.         return $this->db->row('SELECT id FROM users WHERE email = :email', $params);
    7.     }
    этот кусок кода можно заменить на:
    PHP:
    1.  public function checkEmailExists($email)
    2.     {
    3.         return $this->db->row('SELECT id FROM users WHERE email = :email', $email);
    4.     }
    ну а так верно делаешь запрос в бд, проверяешь есть ли там такой email если да сообщаешь, такой email занят другим пользователем.
     
  5. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.752
    Симпатии:
    1.322
    Адрес:
    Лень
    Сорри, это дичь полная.
    Ты какую версию пхп используешь ?
     
  6. GTank

    GTank Новичок

    С нами с:
    21 июл 2018
    Сообщения:
    31
    Симпатии:
    1
    Да нет =) У меня вот так, не прокатит:
    PHP:
    1.     public function checkEmailExists($email)
    2.     {
    3.         return $this->db->row('SELECT id FROM users WHERE email = :email', $email);
    4.     }
    Ругается.

    Код БД:
    PHP:
    1. class DB
    2. {
    3.  
    4.     use Singleton;
    5.  
    6.     protected $db;
    7.  
    8.     /**
    9.      * DB constructor.
    10.      */
    11.     protected function __construct(){
    12.         $db = require ROOT . '/components/config/config_db.php';
    13.  
    14.         $opt = [
    15.             PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    16.             PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    17.             PDO::ATTR_EMULATE_PREPARES   => false,
    18.         ];
    19.  
    20.         $this->db = new PDO($db['dsn'],$db['user'],$db['pass'],$opt);
    21.     }
    22.  
    23.     public function query($sql, $params = []) {
    24.         $stmt = $this->db->prepare($sql);
    25.         if (!empty($params)) {
    26.             foreach ($params as $key => $val) {
    27.                 if (is_int($val)) {
    28.                     $type = PDO::PARAM_INT;
    29.                 } else {
    30.                     $type = PDO::PARAM_STR;
    31.                 }
    32.                 $stmt->bindValue(':'.$key, $val, $type);
    33.             }
    34.         }
    35.         $stmt->execute();
    36.         return $stmt;
    37.     }
    38.  
    39.     public function row($sql, $params = []) {
    40.         $result = $this->query($sql, $params);
    41.         return $result->fetchAll();
    42.     }
    43.  
    44.     public function column($sql, $params = []) {
    45.         $result = $this->query($sql, $params);
    46.         return $result->fetchColumn();
    47.     }
    48.  
    49.     public function lastInsertId() {
    50.         return $this->db->lastInsertId();
    51.     }
    52.  
    53. }
     
  7. _ne_scaju_

    _ne_scaju_ Старожил

    С нами с:
    25 ноя 2016
    Сообщения:
    2.149
    Симпатии:
    118
    если появился мишка зверь, то это очень хорошо, он в этом шарит офигено.
     
  8. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.752
    Симпатии:
    1.322
    Адрес:
    Лень
    в PDO значения завернуты в массив, а если и плейсхолдеры юзаем, то добавочно придаем ключ ко значению
     
  9. GTank

    GTank Новичок

    С нами с:
    21 июл 2018
    Сообщения:
    31
    Симпатии:
    1
    7.0 х-64
     
  10. _ne_scaju_

    _ne_scaju_ Старожил

    С нами с:
    25 ноя 2016
    Сообщения:
    2.149
    Симпатии:
    118
    @GTank ясно в классе БД у тебя принимается массив, значит верни как было)
    @MouseZver давно не тренировался программировать)
     
  11. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.752
    Симпатии:
    1.322
    Адрес:
    Лень
    Раз и на всю жизнь.
    http://phpfaq.ru/pdo/pdo_wrapper
    http://phpfaq.ru/pdo/fetch
    --- Добавлено ---
    Уязвимость топ1
    --- Добавлено ---
    валидация всякой грязи приводил недавно:
    https://php.ru/forum/threads/proverit-unikalnost-imeni-v-baze-dannyx.71764/#post-576484
    --- Добавлено ---
    Забил на пхп и ушел в майнкрафт мочить свой сервер :D
     
  12. GTank

    GTank Новичок

    С нами с:
    21 июл 2018
    Сообщения:
    31
    Симпатии:
    1
    Всё настолько плохо? Хорошо обязательно прочитаю, но не сегодня =)
     
  13. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.752
    Симпатии:
    1.322
    Адрес:
    Лень
    ваш метод public function query
    можно заменить одной строкой с использованием той обвертки:
    PHP:
    1. prepare ( 'SELECT .... = :email', [ 'email' => $value ] ) -> rowCount();
     
  14. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    Хреновый класс валидации нарыл, поэтому с ним каши и не сваришь. Даже если ты в него добавишь проверку e-mail, потом тебе надо будет добавить ещё какую-нибудь комплексную проверку, и опять что-ли расширять этот класс? rules должны быть экземплярами классов, или же должен приниматься callable
     
    GTank нравится это.
  15. _ne_scaju_

    _ne_scaju_ Старожил

    С нами с:
    25 ноя 2016
    Сообщения:
    2.149
    Симпатии:
    118
    @mkramer
    еще один четкий программер, тоже говорит всегда все по делу.
     
    GTank нравится это.
  16. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    GTank и MouseZver нравится это.