cphalcon: [NFR] \Phalcon\Mvc\Model doesn't save an empty string

$note = new Notes();
$note->note = '';
$note->save();
var_dump($note->getMessages());
array (size=1)
    0 => object(Phalcon\Mvc\Model\Message)[49]
        protected '_type' => string 'PresenceOf' (length=10)
        protected '_message' => string 'note is required' (length=16)
        protected '_field' => string 'note' (length=4)
        protected '_model' => null

But empty string is valid value

<bountysource-plugin>

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource. </bountysource-plugin>

About this issue

  • Original URL
  • State: closed
  • Created 11 years ago
  • Comments: 28 (9 by maintainers)

Commits related to this issue

Most upvoted comments

This is implemented in 2.0.3

<?php

namespace Some;

use Phalcon\DI,
    Phalcon\Db\Column,
    Phalcon\Mvc\Model,
    Phalcon\Events\Manager as EventsManager,
    Phalcon\Db\Adapter\Pdo\MySQL as Connection,
    Phalcon\Mvc\Model\Manager as ModelsManager,
    Phalcon\Mvc\Model\Metadata\Memory as ModelsMetaData;

$eventsManager = new EventsManager();

$di = new DI();

$connection = new Connection(array(
    "host"     => "localhost",
    "username" => "root",
    "password" => "",
    "dbname"   => "phalcon_test",
));

$connection->setEventsManager($eventsManager);

$eventsManager->attach('db',
    function ($event, $connection) {
        switch ($event->getType()) {
            case 'beforeQuery':
                echo $connection->getSqlStatement(), " [", join(', ', $connection->getSqlVariables()), "]\n";  
                break;
        }
    }
);

$modelsManager = new ModelsManager();
$modelsManager->setDi($di);

$metaData = new ModelsMetadata();

$di['db'] = $connection;
$di['modelsManager'] = $modelsManager;
$di['modelsMetadata'] = $metaData;

class Robots extends Model
{
    public function initialize()
    {
        $this->allowEmptyStringValues(['name', 'text', 'datetime']);
    }
}

$robot = new Robots();
$robot->name = "";
$robot->text = "";
$robot->datetime = "";
$robot->save();
print_r($robot->getMessages());

Data is stored as they come in the same format, the framework doesn’t make any automatic transformation.

Data is usually filtered/sanitized before be stored in the database. As you can see most validations/filters/sanitizers return empty strings. If an application doesn’t require filter/sanitize/validate data before store them that is simply wonderful! Users are doing a perfect job, and hackers are not allowed 😃

If you’re receiving an array you must be aware of that. Applying a string filter over an array doesn’t make sense. I don’t know if it’s necessary point out that.

I understand this behavior is not perfect, but it has a good intention and removing it is a greater problem than an improvement. Why not just disable the automatic validation and implement a validation system that fits your needs?

More options:

1. Re-implement the automatic validation system You could add a base model to your system, this gives you the freedom to implement any validation system, you can even translate the messages produced instead of using the hardcoded ones:

use Phalcon\Mvc\Model\Message;

class BaseModel extends Phalcon\Mvc\Model
{
    public function validation()
    {
        $notNullAttributes = $this->getModelsMetaData()->getNotNullAttributes($this);
        foreach ($notNullAttributes as $field) {
            if (!isset($this->$field) || $this->$field === null) {
                $this->appendMessage(throw new Message($field . ' is required'));
                return false;
            }
        }
        return true;
    }    
}

Use this class as base in your models:

class Notes extends BaseModel 
{
}

2. Convert empty strings into raw values Alternatively, you can convert the empty strings to raw values skipping the automatic “not null” validator:

class Notes extends Phalcon\Mvc\Model
{
    public function beforeValidation()
    {
        if ($this->note === '') {
            $this->note = new Phalcon\Db\RawValue('');
        }
    }
}

3. Change the field to allow null and move this validation to the application layer If it’s acceptable, marking the not null field in the table to allows nulls, implementing this validation in your application:

use Phalcon\Mvc\Model\Message;

class Notes extends Phalcon\Mvc\Model
{
    public function validation()
    {
        if ($this->note === null) {
            $this->appendMessage(throw new Message('Note cannot be null'));
            return false;
        }
    }
}