How to validate form field in PHP using Object Oriented Technique
Asked Answered
S

4

5

I have created a class 'validate' to validate two fields i.e 'firstname' and 'lastname'. It is not working fine, it is showing error when field is empty but when I submit the form with non-empty fields the error is still there. How to execute this on form submission?

 <?php

  class validation {

  public $firstName, $lastName, $errorFirstName = '', $errorLastName = '';

  function __constructor($fName, $lName){
    $this->firstName = $fName;
    $this->lastName = $lName;
  }

  function check(){

      if($_SERVER["REQUEST_METHOD"] == "POST"){
        if(empty($this->firstName)){
        $this->errorFirstName = 'First name is required';
       } else {
        $this->errorFirstName = 'Input is okay';
       }

     if(empty($this->lastName)){
         $this->errorLastName = 'Last name is required';
      }  else {
       $this->errorLastName = 'Input is okay';
      }
    }
   }
  }

 $obj = new validation($_POST['firstname'], $_POST['lastname']);
 $obj->check();
 $errorF = $obj->errorFirstName;
 $errorL = $obj->errorLastName;

 ?>

  <!DOCTYPE html>
  <html lang = "en-US" dir = "ltr">
   <head>
    <title>Home</title>
    <meta charset = "UTF-8"/>
   </head>
   <body>
   <form method = "POST" action="<?php echo $_SERVER["PHP_SELF"]?>">
    <label>First Name: </label>
    <input type = "text" name = "firstname" placeholder = "John"/>
    <p class = "error"><?php echo $errorF;?></p>
    <label>Last Name: </label>
    <input type = "text" name = "lastname" placeholder = "Doe"/>
    <p class = "error"><?php echo $errorL;?></p>
    <input type="submit">
   </form>
  </body>
 </html>
Systemic answered 12/4, 2017 at 15:27 Comment(3)
If I were to make a validation class, I would not put all the checking in just one method. Instead I would make different methods for different validations with different rules. I would write my own validation.php library just like CodeIgniter (based on my own preference) or I would use CI's library (which is also great). It makes validation a lot easier and reusable.Faction
You should remove if($_SERVER["REQUEST_METHOD"] == "POST"){ check in your class and have it wrapped around where you instantiate the object.Maximinamaximize
Thanks alistaircol. I am new to PHP and OOP. I removed the constructor and wrote a set function instead. It works.Systemic
H
6

Everyone always makes "database class" and "validation class". Ehh .... whaaaaay?

Don't make a validation class. It never works. The most .. emm ... sustainable options for validating user input are:

With using entities for validation, it is quite simple. In your case you would have class Profile where you have method setFirstName(string $name). Then within this method you do the validation and on error throw a custom made exception, like InvalidFirstName .. or something like that.

Using value objects is a bit trickier, but it prevents the code duplication. For example, you need to validate email address. So, the way you would want to use it would look something like:

try {
    $profile = new Profile;
    $profile->setEmail(new EmailAddress($_POST['email']));
} catch (InvalidArgumentException $e){
    // validation failed
}

Therefore, to get this behavior, you would have the class defined kinda like this:

class EmailAddress
{
    private $email;


    public function __construct(int $emailId = null, string $email = null)
    {
        if (!$this->isValid($email)) {
            throw new InvalidArgumentException('Not valid email address');
        }
        $this->email = $email;
    }


    private function isValid($email)
    {
        return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
    }


    public function __toString()
    {
        return $this->email;
    }
}

This approach is a lot more expressive, but it tends to become a big gnarly, when interacting with persistence layer.

In practice, the best option is to use combination of these both solutions:

  • keep the validation in entities, for rules, that are unique
  • use value objects for often repeating constraints
Housebreak answered 28/11, 2017 at 15:49 Comment(4)
Why does the constructor receive $emailId but never use it?Demosthenes
Because this was copied from real code, with extra bits removed .... did not remove all of them apparently. Honestly, I have no idea why even $emailId existed in that class.Biddie
I'm new to this; would the initial try block go in the main PHP form? Because otherwise, I'm not sure how you would get field error messages back to the main form after submit if you use a separate PHP file like action="form_logic.php". (I'm just trying to sus the data movement out atm) Thanks.Aromaticity
@Aromaticity no, it would not. What you need to look up is "php templating", to separate the mostly-HTML part of your code from the "business logic".Biddie
C
2

Do make a validator class.

Forms usually consist of multiple fields meaning that in suggested approach where you try/catch error while setting up the object will fail on a single field, when you might be having multiple invalid fields and on form submit you want that all fields are validated and all errors returned so they can be presented at the same time.

<?php

class MyValidator
{
    /**
     * Is form valid;
     *
     * @var bool
     */
    private $isValid = true;
    /**
     * List of errors, assoc array with error messages one per fieldName
     *
     * @var array
     */
    private $errors = [];

    /**
     * Check if form is valid
     *
     * @return bool
     */
    public function isValid(): bool
    {
        return $this->isValid;
    }

    /**
     * Get error message
     *
     * @param $fieldName
     * @return mixed|string
     */
    public function getError($fieldName)
    {
        return isset($this->errors[$fieldName]) ? $this->errors['fieldName'] : '';
    }

    /**
     * @param array $rules list of rules
     * @param array $payload list of form parameters
     * @return bool Return validation result, same as isValid
     */
    public function validate(array $rules, array $payload)
    {
        foreach ($rules as $rule) {
            if (!$this->validateRequired($rule, $payload)) {
                continue;
            }
            switch ($rule['type']) {
                case 'string':
                    $this->validateString($rule, $payload);
                    break;
                case 'email':
                    $this->validateEmail($rule, $payload);
                    break;
                    //extend with other validation rules as needed
            }
        }

        return $this->isValid();
    }

    public function validateRequired(array $rule, array $payload)
    {
        if (true === $rule['required'] && !isset($payload[$rule['fieldName']])) {
            $this->isValid = false;
            $this->errors[$rule['fieldName']] = 'This field is required';

            return false;
        }

        return true;
    }

    public function validateString($rule, $payload)
    {
        // Checkup logic, set $this->isValid to false if not valid, add
        // See add $this->errors[$rule['fieldname']] = 'your message';
    }

    public function validateEmail($rule, $payload)
    {
        // Checkup logic, set $this->isValid to false if not valid, add
        // See add $this->errors[$rule['fieldname']] = 'your message';
    }

}

// Call validator by giving validator ruleset in the format

$rules = [
    [
        'fieldName' => 'firstName',
        'type' => 'string',
        'minLength' => 10,
        'maxLength' => 20,
        'required' => true,
    ],
    [
        'fieldName' => 'email',
        'type' => 'email',
        'required' => true,
    ]
];

$validator = new MyValidator();
$isValid = $validator->validate($rules, $_POST);

// if false do repeat form with error messages shown
// use $validator->getError('firstName'); to get error message for a field.
Cierracig answered 18/2, 2020 at 10:6 Comment(0)
C
2

In the code mentioned, it is written in a way that the form values will be passed to the class while creating it, and inside the constructor, class variables will be initialized with the values provided.

After that check() method will check whether the fields are empty or not.

But this is not working as expected because the values passed from the form could not be initialized to $firstName and $lastName.

The reason for this is,

function __constructor() 

is not a constructor of a class, it should be

function __construct()

which is the reason why the class variables are not assigned the values given from the form and the error is showing up.

Once you modify it, the issue will be solved.

Coheman answered 5/4, 2021 at 19:36 Comment(1)
Thank you for your answer, Rajen. Unfortunately, I opened the bounty only to reward the answer of tereško. But I appreciate your effort to write your answer. P.S: In general, the motive for which a bounty is opened by someone is presented in its description text.Ruyter
P
0

I’m trying to validate from pages this way.

 public static function validation($rules)
{
    $errors = [];
    $all_data = [];
    try {
        if ($_SERVER['REQUEST_METHOD'] === 'GET') {
            $result = str_replace('"', '', $_GET);
        } elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $result = str_replace('"', '', $_POST);
        } elseif ($_SERVER['REQUEST_METHOD'] === 'PUT') {
            $result = str_replace('"', '', $_GET);
        } elseif ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
            $result = str_replace('"', '', $_POST);
        }
        foreach ($rules as $key => $data) {
            $value = explode('|', $data);
            $required = preg_match('/required/', $data, $matches) ? $matches[0] : null;
            $max = preg_match('/max:(\d+)/', $data, $matches) ? $matches[1] : 256;
            $min = preg_match('/min:(\d+)/', $data, $matches) ? $matches[1] : 1;
            $unique = preg_match('/unique:(\w+)/', $data, $matches) ? $matches[1] : null;
            $email = preg_match('/email/', $data, $matches) ? $matches[0] : null;


            if ($required === $value[0]) {
                if (empty($result[$key])) {
                    $errors[$key] = $key . " Is required";
                } else {
                    if ((int)$min >= strlen($result[$key]) || (int)$max <= strlen($result[$key])) $errors[$key] = $key . "Min required or Max required";

                    if ($email=== $value[0]) {
                        if (!filter_var($result[$key], FILTER_VALIDATE_EMAIL)) {
                            $errors[$key] = $key . "Is Email Not Valid";
                        }
                    }
                    if ($unique !== null) {
                        $data = (new Database())->query("select * from $unique where $key = '$result[$key]'")->get();

                        if ($data === null) {
                            $errors[$key] = $key . "Is Unique Value Exists";
                        }
                    }
                    $all_data[$key]= $result[$key];
                }
            }
        }
    } catch (\Exception $e) {
        echo 'Caught exception: ', $e->getMessage(), "\n";
    }
   return [
       'errors' => $errors,
       'data' => $all_data
   ];
}

call the class,

  $validations = Validation::validation([
        'name' => 'required|unique:shops|min:1|max:255',
        'email' => 'required|unique:shops|min:1|max:255|email'          
    ]);

    if ($validations['errors']) {
        echo $validations['errors'];
        return header('location: /');
    }
Polysyllable answered 20/2 at 14:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.