How to continue model validation process when standard PresenceOf/ConstraintViolation validation failed?

I am testing about Phalcon's model validation (1.3.2).

Some behavior is different from my image.

Questions

  • Is there any option like 'allowEmpty' of 'InclusionIn' for foreignKey validation? (test01)
  • How to continue validation process other than foreignKey validation? (test01)
  • How to continue foreignKey validation process for other field(s)? (test02)
  • Why 'LastYear' is treated as '' or null for 'year'? (test05)
  • How to continue validation process other than NotNull validation? (test05)

tables

[country] -- 1:n -- [robots] -- n:1 -- [robot_types]

  • country
id name
1 Japan
2 USA
  • robot_types
id name
1 cyborg
2 humanoid
  • robots
id name year country_id robottypesid
1 ASIMO 2000 1 1
2 RoboCop 2010 2 2

project skeletons and scaffolds

phalcon project robots
phalcon all-models --relations
phalcon scaffold --table-name=robots
phalcon scaffold --table-name=country
phalcon scaffold --table-name=robot_types

Sample code

  • app/models/Robots.php
<?php
use Phalcon\Mvc\Model\Validator\Numericality;
use Phalcon\Mvc\Model\Validator\Uniqueness;

class Robots extends \Phalcon\Mvc\Model
{

    /**
     *
     * @var integer
     */
    public $id;

    /**
     *
     * @var string
     */
    public $name;

    /**
     *
     * @var integer
     */
    public $year;

    /**
     *
     * @var integer
     */
    public $country_id;

    /**
     *
     * @var integer
     */
    public $robot_types_id;

    /**
     * Initialize method for model.
     */
    public function initialize()
    {
        $this->belongsTo('country_id', 'Country', 'id', array('foreignKey' => true));
        $this->belongsTo('robot_types_id', 'RobotTypes', 'id', array('foreignKey' => true));
    }

    public function validation()
    {
        $this->validate(new Uniqueness(
            array(
                "field"   => "name"
            )
        ));
        $this->validate(new Numericality(
            array(
                "field"   => "year"
            )
        ));
        return $this->validationHasFailed() != true;
    }
}
  • app/controllers/RobotsController.php
<?php
... 
class RobotsController extends ControllerBase
{
    ...
    public function testAction()
    {

        $this->db->begin();
        echo "<p>test01</p>";
        $robot = new Robots();
        if (!$robot->save()) {
            foreach ($robot->getMessages() as $message) {
                $this->flash->error($message);
            }
        } else {
            echo "Great, a new robot was saved successfully!";
        }
        /*
        result
            Value of field "country_id" does not exist on referenced table
        I hope
            country_id is required
            robot_types_id is required
            name is required
            year is required
        */
        $this->db->rollback();


        $this->db->begin();
        echo "<p>test02</p>";
        $robot = new Robots();
        $robot->country_id = '3';
        $robot->robot_types_id = '5';
        if (!$robot->save()) {
            foreach ($robot->getMessages() as $message) {
                $this->flash->error($message);
            }
        } else {
            echo "Great, a new robot was saved successfully!";
        }
        /*
        result
            Value of field "country_id" does not exist on referenced table
        I hope
            Value of field "country_id" does not exist on referenced table
            Value of field "robot_types_id" does not exist on referenced table
            name is required
            year is required
        */
        $this->db->rollback();


        $this->db->begin();
        echo "<p>test03</p>";
        $robot = new Robots();
        $robot->country_id = '1';
        $robot->robot_types_id = '5';
        if (!$robot->save()) {
            foreach ($robot->getMessages() as $message) {
                $this->flash->error($message);
            }
        } else {
            echo "Great, a new robot was saved successfully!";
        }
        /*
        result
            Value of field "robot_types_id" does not exist on referenced table
        I hope
            Value of field "robot_types_id" does not exist on referenced table
            name is required
            year is required
        */
        $this->db->rollback();


        $this->db->begin();
        echo "<p>test04</p>";
        $robot = new Robots();
        $robot->country_id = '1';
        $robot->robot_types_id = '2';
        if (!$robot->save()) {
            foreach ($robot->getMessages() as $message) {
                $this->flash->error($message);
            }
        } else {
            echo "Great, a new robot was saved successfully!";
        }
        /*
        result
            name is required
            year is required
        I hope
            name is required
            year is required
        */
        $this->db->rollback();


        $this->db->begin();
        echo "<p>test05</p>";
        $robot = new Robots();
        $robot->name = 'ASIMO';
        $robot->year = 'LastYear';
        $robot->country_id = '1';
        $robot->robot_types_id = '2';
        if (!$robot->save()) {
            foreach ($robot->getMessages() as $message) {
                $this->flash->error($message);
            }
        } else {
            echo "Great, a new robot was saved successfully!";
        }
        /*
        result
            year is required
        I hope
            Value of field: 'name' is already present in another record
            Value of field: 'year' must be numeric
        */
        $this->db->rollback();


        $this->db->begin();
        echo "<p>test06</p>";
        $robot = new Robots();
        $robot->name = 'ASIMO';
        $robot->year = '2014';
        $robot->country_id = '1';
        $robot->robot_types_id = '2';
        if (!$robot->save()) {
            foreach ($robot->getMessages() as $message) {
                $this->flash->error($message);
            }
        } else {
            echo "Great, a new robot was saved successfully!";
        }
        /*
        result
            Value of field: 'name' is already present in another record
        I hope
            Value of field: 'name' is already present in another record
        */
        $this->db->rollback();


        $this->db->begin();
        echo "<p>test07</p>";
        $robot = new Robots();
        $robot->name = 'ASIMO2';
        $robot->year = '2014';
        $robot->country_id = '1';
        $robot->robot_types_id = '2';
        if (!$robot->save()) {
            foreach ($robot->getMessages() as $message) {
                $this->flash->error($message);
            }
        } else {
            echo "Great, a new robot was saved successfully!";
        }
        /*
        result
            Great, a new robot was saved successfully!
        I hope
            Great, a new robot was saved successfully!
        */
        $this->db->rollback();


        $this->db->begin();
        echo "<p>test08</p>";
        $robot = new Robots();
        $robot->name = 'ASIMO';
        $robot->year = 'LastYear';
        $robot->country_id = '3';
        $robot->robot_types_id = '5';
        if (!$robot->save()) {
            foreach ($robot->getMessages() as $message) {
                $this->flash->error($message);
            }
        } else {
            echo "Great, a new robot was saved successfully!";
        }
        /*
        result
            Value of field "country_id" does not exist on referenced table
        I hope
            Value of field "country_id" does not exist on referenced table
            Value of field "robot_types_id" does not exist on referenced table
            Value of field: 'name' is already present in another record
            Value of field: 'year' must be numeric
        */
        $this->db->rollback();

    }
    ...
}

How do you think?

Thanks.



34.0k
edited Sep '14

If it helps you somehow, here are my 2 cents:

Q1: Is there any option like 'allowEmpty' of 'InclusionIn' for foreignKey validation? (test01)

A1: Alter your column and allow null vales. In the Models, remove "foreignKey => true"

Q2: How to continue validation process other than foreignKey validation? (test01)

A2: I never thought about it. But the error is thrown by the database server

Q3: How to continue foreignKey validation process for other field(s)? (test02)

A3: Maybe you can use a select in beforeValidation() method to check if the key exists. (implemen a custom validator for the model)

Q4: Why 'LastYear' is treated as '' or null for 'year'? (test05)

A4: The field type is integer. (int) 'LastYear' = 0; And since it's a string that you are passing, phalcon (i think) converts it to a string this is the result of '' or NULL

Q5: How to continue validation process other than NotNull validation? (test05)

A5: Implement custom validators (A3)

Hi, Calin Rada. Thank you for your comment.

I've understood that auto validations are simplified. If I hope validation result like above, I have to write custom validator and custom validation method.

I will comment my own solution.

Thanks.

Hi.

This is my solution.

Test action came to an operation that I've hope.

app/models/Robots.php

<?php

use Phalcon\Mvc\Model\Validator\Numericality;
use Phalcon\Mvc\Model\Validator\Uniqueness;
use Phalcon\Mvc\Model\Validator\PresenceOf;
use Phalcon\Mvc\Model\Message;

class Robots extends \Phalcon\Mvc\Model
{

    /**
     *
     * @var integer
     */
    public $id;

    /**
     *
     * @var string
     */
    public $name;

    /**
     *
     * @var integer
     */
    public $year;

    /**
     *
     * @var integer
     */
    public $country_id;

    /**
     *
     * @var integer
     */
    public $robot_types_id;

    /**
     * Initialize method for model.
     */
    public function initialize()
    {

        $this->belongsTo('country_id', 'Country', 'id', array('foreignKey' => true));
        $this->belongsTo('robot_types_id', 'RobotTypes', 'id', array('foreignKey' => true));

    }

    public function beforeValidation()
    {

        $this->setup(array(
            "notNullValidations" => false,
            "virtualForeignKeys" => false,
        ));

    }

    public function validation()
    {

        $this->validate(new PresenceOf(
            array(
                'field'   => 'country_id',
            )
        ));
        if (! ('' === $this->country_id || is_null($this->country_id))) {
            $conditions = 'id = :id:';
            $parameters = array('id' => $this->country_id);
            $result = Country::count(array($conditions, 'bind' => $parameters));
            if (1 != Country::count(array($conditions, 'bind' => $parameters))) {
                $message = new Message(
                    'Value of field "country_id" does not exist on referenced table',
                    'country_id',
                    'ConstraintViolation'
                );
                $this->appendMessage($message);
            }
        }

        $this->validate(new PresenceOf(
            array(
                'field'   => 'robot_types_id',
            )
        ));
        if (! ('' === $this->robot_types_id || is_null($this->robot_types_id))) {
            $conditions = 'id = :id:';
            $parameters = array('id' => $this->robot_types_id);
            if (1 != RobotTypes::count(array($conditions, 'bind' => $parameters))) {
                $message = new Message(
                    'Value of field "robot_types_id" does not exist on referenced table',
                    'robot_types_id',
                    'ConstraintViolation'
                );
                $this->appendMessage($message);
            }
        }

        $this->validate(new PresenceOf(
            array(
                'field'   => 'name',
            )
        ));
        $this->validate(new Uniqueness(
            array(
                'field'   => 'name',
                'allowEmpty' => true,
            )
        ));

        $this->validate(new PresenceOf(
            array(
                'field'   => 'year',
            )
        ));
        $this->validate(new Numericality(
            array(
                'field'   => 'year',
                'allowEmpty' => true,
            )
        ));

        return $this->validationHasFailed() != true;
    }

}

Thanks.