Is it possible to validate before creating an object in PHP?

3

I have the following question: is it possible to validate the form of an array before creating an object?

I have the following scenario. I am creating an apiRest and the programmer who consumes my API sends me a post with the following JSON:

[
  {
    "Codigo":"1",
    "Nombre":"NESTEA",
    "Presentacion":"1.5 L",
    "Foto":"http://localhost/api/public/img/pepsi.jpg",
    "MarcaId":"1",
    "FamiliaId":"1",
    "ProveedorId":"2",
    "Rating":"5",
    "Estado":"0"
  }
]

In the apiRest I implemented a post method and must create a new object with the JSON converted into an array:

 if($_SERVER['REQUEST_METHOD']=='POST'){
     $postBody = file_get_contents("php://input");
     $jsonToArray = json_decode($postBody,true);

     $producto = new productos($jsonToArray);

     print_r($producto->getCodigo());
     http_response_code(200);
  }

Class code:

class productos {
     //atributos
     private $Codigo;
     private $Nombre;
     private $Presentacion;
     private $Foto;
     private $MarcaId;
     private $FamiliaId;
     private $ProveedorId;
     private $Rating;
     private $Estado;

     //constructor
     public function productos($array){
            $this->Codigo = $array[0]['Codigo'];
            $this->Nombre = $array[0]['Nombre'];
            $this->$Presentacion = $array[0]['Presentacion'];
            $this->Foto = $array[0]['Foto'];
            $this->MarcaI = $array[0]['MarcaId'];
            $this->FamiliaId = $array[0]['FamiliaId'];
            $this->ProveedorId = $array[0]['ProveedorId'];
            $this->Rating = $array[0]['Rating'];
            $this->Estado = $array[0]['Estado'];
     }

     public function GuardarProducto(){
         return $this->Codigo;
     }
}

So far everything is fine. The problem arises when they stop sending me a parameter. For example:

[
  {
    "Codigo":"1",
    "Nombre":"NESTEA",
    "Presentacion":"1.5 L",
    "Foto":"http://localhost/api/public/img/pepsi.jpg",
    "MarcaId":"1",
    "FamiliaId":"1",
    "ProveedorId":"2",  
  }
]

I get the following error:

  

Notice : Undefined variable: Presentation in C: \ xampp \ htdocs \ api \ objects \ products.php on line 21

     

Fatal error : Can not access empty property in C: \ xampp \ htdocs \ api \ objects \ products.php online 21 < br>

The only thing I can think of is to use if(isset(array[0]['Estado']){ //validar } , but I want all the fields to be required and send an error 400 to the programmer if the POST is not right.

How could I do it?

    
asked by Wilfredo Aleman 03.04.2018 в 16:42
source

4 answers

3

As you said, with isset() and then the function header() to send you the error response.

In the file where you receive the data you could use a block try/catch and there handle the error status. Something like this:

try {
   $postBody = file_get_contents("php://input");
   $jsonToArray = json_decode($postBody,true);

   $producto = new productos($jsonToArray);

   print_r($producto->getCodigo());
   http_response_code(200);

} catch (Exception $e) {
   header('HTTP/1.1 400 ' . $e->getMessage());
}

And in the class send an exception when some validation fails:

if (!isset($array[0]['Estado'])) {
   throw new Exception('El campo Estado es obligatorio');
}
    
answered by 03.04.2018 / 18:25
source
3

As I see the most optimal way to do what you want is to add a property to your product object or what you think later called unused, along with its getter, in which you will be adding properties that you are not interested in appearing or not when you pick up the arrangement in the json, by default all the properties you have will be required, and only for them is it checked to verify if your arrangement is valid or not. In my case I made a class called Validator to put there the functionalities that I used to validate. This is the code:

class Validator{

    //Obtiene un arreglo con todas las propiedades de un objeto
    static function getProperties($class)
    {
        $reflection = new ReflectionClass($class);
        $vars = $reflection->getProperties(ReflectionProperty::IS_PRIVATE);
        $result = [];
        //Agrega todas menos 'unused'
        array_walk($vars, function($val, $key) use(&$result){ 
            if($val->name !== 'unused'){
                $result[] = $val->name;
            }   
        });
        return $result;
    }

    //Verifica si el arreglo es correcto basado en las propiedades del objeto obj
    static function checkProperties($jsonArray, $obj, &$missed){
        $props = self::getProperties($obj);
        $unused = call_user_func(array($obj, 'getUnused'),array());
        //Propiedades del objeto que no estan en el arreglo unused
        $real = array_diff($props, $unused);
        //Propiedades que faltan con respecto a las que posee el objeto y son requeridas
        $missed = array_diff($real, array_keys($jsonArray[0]));
        /*Aqui lo que verifico es que no exista ninguna diferencia entre las claves de tu arreglo y las
        propiedades de tu clase*/
        return count(array_intersect($real, array_keys($jsonArray[0]))) == count($props);
    }
}

This is your class products :

class productos {
     //atributos
     private $Codigo;
     private $Nombre;
     private $Presentacion;
     private $Foto;
     private $MarcaId;
     private $FamiliaId;
     private $ProveedorId;
     private $Rating;
     private $Estado;
     //Esta es la propiedad donde vas a ir agregando las que no vas a comprobar
     private $unused = [];

     //constructor
     public function productos($array){
            $faltan = [];
            if(Validator::checkProperties($array, $this, $faltan)){             
                $this->Codigo = $array[0]['Codigo'];
                $this->Nombre = $array[0]['Nombre'];
                $this->$Presentacion = $array[0]['Presentacion'];
                $this->Foto = $array[0]['Foto'];
                $this->MarcaI = $array[0]['MarcaId'];
                $this->FamiliaId = $array[0]['FamiliaId'];
                $this->ProveedorId = $array[0]['ProveedorId'];
                $this->Rating = $array[0]['Rating'];
                $this->Estado = $array[0]['Estado'];
            } else {
                //Lanzo una excepción si no es correcta la validación
                throw new Exception("Faltan datos: ".implode(", ", $faltan));
            }
     }

     public function GuardarProducto(){
         return $this->Codigo;
     }

     //Getter a la propiedad unused
     public function getUnused(){
         return $this->unused;
     }
}

And here is the entry point with which you start your code:

if($_SERVER['REQUEST_METHOD']=='POST'){
     $postBody = file_get_contents("php://input");
     $jsonToArray = json_decode($postBody,true);
     try{
         $producto = new productos($jsonToArray);
         print_r($producto->getCodigo());
         http_response_code(200);
    } catch(Exception $e){
        http_response_code(400);
    }
}
    
answered by 03.04.2018 в 18:52
2

I give you two forms of implementation, in the second way you can create more validation rules once you know you have the complete array

1 how to check if an array is "complete" (all keys defined in a template)

      #!/usr/bin/env php
      <?php
      $postBodyBueno = '[
        {
          "Codigo":"1",
          "Nombre":"NESTEA",
          "Presentacion":"1.5 L",
          "Foto":"http://localhost/api/public/img/pepsi.jpg",
          "MarcaId":"1",
          "FamiliaId":"1",
          "ProveedorId":"2",
          "Rating":"5",
          "Estado":"0"
        }
      ]';

      $postBodyMalo = '[
        {
          "Codigo":"1",
          "Nombre":"NESTEA",
          "Presentacion":"1.5 L",
          "Foto":"http://localhost/api/public/img/pepsi.jpg",
          "MarcaId":"1",
          "FamiliaId":"1",
          "ProveedorId":"2",
          "campoextranodefinido":"undefined"
        }
      ]';

      function ValidateArrayDesdeJson($jsonString='')
      {
          $lasKeys = array(
          'Codigo',  'Nombre', 'Presentacion', 'Foto', 'MarcaId',
          'FamiliaId', 'ProveedorId', 'Rating', 'Estado'
          );
          $jsonToArray = json_decode($jsonString, true)[0];
          $testKeys = array_diff($lasKeys, array_keys($jsonToArray));
          if ($testKeys) :
              $err = '400 - Faltan campos ['.
                implode(', ', $testKeys).']'.PHP_EOL;
            else :
                  $err = '200 - Parece que esta bien'.PHP_EOL;
            endif;
              return $err;
      }

      echo "postBodyBueno:".ValidateArrayDesdeJson($postBodyBueno);
      echo "postBodyMalo:".ValidateArrayDesdeJson($postBodyMalo);

Result:

$ ./checkarraykeys.php
postBodyBueno:200 - Parece que esta bien
postBodyMalo:400 - Faltan campos [Rating, Estado]

2 included as check in the constructor

  #!/usr/bin/env php
  <?php
  class Producto
  {
      //atributos
      private $Codigo;
      private $Nombre;
      private $Presentacion;
      private $Foto;
      private $MarcaId;
      private $FamiliaId;
      private $ProveedorId;
      private $Rating;
      private $Estado;

      //constructor
      public function __construct( array $attr = []
      ) {
          $this->ValidarAtributosProducto($attr);
          $this->Codigo = intval($attr['Codigo']);
          $this->Nombre = $attr['Nombre'];
          $this->Presentacion = $attr['Presentacion'];
          $this->Foto = $attr['Foto'];
          $this->MarcaI = $attr['MarcaId'];
          $this->FamiliaId = $attr['FamiliaId'];
          $this->ProveedorId = $attr['ProveedorId'];
          $this->Rating = $attr['Rating'];
          $this->Estado = $attr['Estado'];
      }

      private function ValidarAtributosProducto(array $attr = [])
      {
          $lasKeys = array(
          'Codigo',  'Nombre', 'Presentacion', 'Foto', 'MarcaId',
          'FamiliaId', 'ProveedorId', 'Rating', 'Estado'
          );
          $testKeys = array_diff($lasKeys, array_keys($attr));
          if ($testKeys) :
              throw new Exception(
                  'Faltan campos ['.
                  implode(', ', $testKeys).
                  ']'.PHP_EOL
              );
          endif;

          if (0==$attr['Codigo']) :
              throw new Exception(
                  'El Código no es válido ['.$attr['Codigo'].']'.PHP_EOL
              );
          endif;

      }
  }

  $arrayProducto = array( 'Nombre'=>'un producto');
  try {
      $producto = new Producto($arrayProducto);
  } catch(Exception $e) {
      echo '400 - Mensaje: ' .$e->getMessage();
  }

Result:

$ ./classvalidation.php
400 - Mensaje: Faltan campos [Codigo, Presentacion, Foto, MarcaId, FamiliaId, ProveedorId, Rating, Estado]
    
answered by 03.04.2018 в 18:23
2

You could create a private method in the class responsible for checking that all the fields are in the array that you pass to the constructor, this method could be called from the same constructor.

The code would look something like this:

public function Productos( $array ) {
        if($this->validar($array)){
            $this->Codigo       = $array[0]['Codigo'];
            $this->Nombre       = $array[0]['Nombre'];
            $this->Presentacion = $array[0]['Presentacion'];
            $this->Foto         = $array[0]['Foto'];
            $this->MarcaI       = $array[0]['MarcaId'];
            $this->FamiliaId    = $array[0]['FamiliaId'];
            $this->ProveedorId  = $array[0]['ProveedorId'];
            $this->Rating       = $array[0]['Rating'];
            $this->Estado       = $array[0]['Estado'];
        }else{
            //aquí manejas el error
        }


    }
private function validar($array){
        $campos=['Codigo','Nombre','Presentacion','Foto','MarcaId','FamiliaId','ProveedorId','Rating','Estado'];
        $valido=true;
        foreach($campos as $campo){

            if(!array_key_exists($campo,$array[0])){

                $valido =false;
            }
        }
        return $valido;
}

}
    
answered by 03.04.2018 в 18:26