Adding validation to Models. Needs docs.

This commit is contained in:
Lonnie Ezell 2016-09-14 00:15:59 -05:00
parent 59a3fe2a1f
commit a5f4f8bc5c
No known key found for this signature in database
GPG Key ID: 88F86F2034554774
4 changed files with 225 additions and 3 deletions

View File

@ -513,6 +513,25 @@ class Services
//--------------------------------------------------------------------
/**
* The Validation class provides tools for validating input data.
*/
public static function validation(Validation $config = null, $getShared = true)
{
if ($getShared)
{
return self::getSharedInstance('validation', $config);
}
if (is_null($config))
{
$config = new Validation();
}
return new \CodeIgniter\Validation\Validation($config);
}
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// Utility Methods - DO NOT EDIT

View File

@ -37,9 +37,9 @@
*/
use CodeIgniter\Pager\Pager;
use CodeIgniter\Validation\ValidationInterface;
use Config\App;
use Config\Database;
//use Config\Services;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Database\BaseBuilder;
use CodeIgniter\Database\BaseConnection;
@ -191,6 +191,38 @@ class Model
*/
protected $builder;
/**
* Rules used to validate data in insert, update, and save methods.
* The array must match the format of data passed to the Validation
* library.
*
* @var array
*/
protected $validationRules = [];
/**
* Contains any custom error messages to be
* used during data validation.
*
* @var array|null
*/
protected $validationMessages = null;
/**
* Skip the model's validation. Used in conjunction with skipValidation()
* to skip data validation for any future calls.
*
* @var bool
*/
protected $skipValidation = false;
/**
* Our validator instance.
*
* @var \CodeIgniter\Validation\ValidationInterface
*/
protected $validation;
//--------------------------------------------------------------------
/**
@ -199,7 +231,7 @@ class Model
* @param ConnectionInterface $db
* @param BaseConfig $config Config/App()
*/
public function __construct(ConnectionInterface &$db = null, BaseConfig $config = null)
public function __construct(ConnectionInterface &$db = null, BaseConfig $config = null, ValidationInterface $validation = null)
{
if ($db instanceof ConnectionInterface)
{
@ -220,6 +252,12 @@ class Model
$this->tempReturnType = $this->returnType;
$this->tempUseSoftDeletes = $this->useSoftDeletes;
if (is_null($validation))
{
$validation = \Config\Services::validation();
}
$this->validation = $validation;
}
//--------------------------------------------------------------------
@ -532,6 +570,15 @@ class Model
*/
public function insert($data)
{
// Validate data before saving.
if ($this->skipValidation === false)
{
if ($this->validate($data) === false)
{
return false;
}
}
// Must be called first so we don't
// strip out created_at values.
$data = $this->doProtectFields($data);
@ -569,6 +616,15 @@ class Model
*/
public function update($id, $data)
{
// Validate data before saving.
if ($this->skipValidation === false)
{
if ($this->validate($data) === false)
{
return false;
}
}
// Must be called first so we don't
// strip out updated_at values.
$data = $this->doProtectFields($data);
@ -953,6 +1009,95 @@ class Model
//--------------------------------------------------------------------
/**
* Grabs the last error(s) that occurred. If data was validated,
* it will first check for errors there, otherwise will try to
* grab the last error from the Database connection.
*
* @param bool $forceDB Always grab the db error, not validation
*
* @return array|null
*/
public function errors(bool $forceDB = false)
{
// Do we have validation errors?
if ($forceDB === false && $this->skipValidation === false)
{
$errors = $this->validation->getErrors();
if (! empty($errors))
{
return $errors;
}
}
// Still here? Grab the database-specific error, if any.
$error = $this->db->getError();
return $error['message'] ?? null;
}
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// Validation
//--------------------------------------------------------------------
/**
* Set the value of the skipValidation flag.
*
* @param bool $skip
*
* @return $this
*/
public function skipValidation(bool $skip = true)
{
$this->skipValidation = $skip;
return $this;
}
//--------------------------------------------------------------------
/**
* Validate the data against the validation rules (or the validation group)
* specified in the class property, $validationRules.
*
* @param array $data
*
* @return bool
*/
public function validate($data): bool
{
if ($this->skipValidation === true || empty($this->validationRules))
{
return true;
}
// Query Builder works with objects as well as arrays,
// but validation requires array, so cast away.
if (is_object($data))
{
$data = (array)$data;
}
// ValidationRules can be either a string, which is the group name,
// or an array of rules.
if (is_string($this->validationRules))
{
$valid = $this->validation->run($data, $this->validationRules);
}
else
{
$this->validation->setRules($this->validationRules, $this->validationMessages);
$valid = $this->validation->run($data);
}
return (bool)$valid;
}
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// Magic

View File

@ -0,0 +1,27 @@
<?php namespace Tests\Support\Models;
use CodeIgniter\Model;
class ValidModel extends Model
{
protected $table = 'job';
protected $returnType = 'object';
protected $useSoftDeletes = false;
protected $dateFormat = 'integer';
protected $allowedFields = ['name', 'description'];
protected $validationRules = [
'name' => 'required|min_length[3]'
];
protected $validationMessages = [
'name' => [
'required' => 'You forgot to name the baby.',
'min_length' => 'Too short, man!'
]
];
}

View File

@ -3,6 +3,7 @@
use CodeIgniter\Model;
use Tests\Support\Models\JobModel;
use Tests\Support\Models\UserModel;
use Tests\Support\Models\ValidModel;
/**
* @group DatabaseLive
@ -260,7 +261,7 @@ class ModelTest extends \CIDatabaseTestCase
$data = new \stdClass();
$data->name = 'Magician';
$data->description = 'Makes peoples things dissappear.';
$model->protect(false)->save($data);
$this->seeInDatabase('job', ['name' => 'Magician']);
@ -443,5 +444,35 @@ class ModelTest extends \CIDatabaseTestCase
//--------------------------------------------------------------------
public function testValidationBasics()
{
$model = new ValidModel($this->db);
$data = [
'description' => 'some great marketing stuff'
];
$this->assertFalse($model->insert($data));
$errors = $model->errors();
$this->assertEquals('You forgot to name the baby.', $errors['name']);
}
//--------------------------------------------------------------------
public function testSkipValidation()
{
$model = new ValidModel($this->db);
$data = [
'name' => '2',
'description' => 'some great marketing stuff'
];
$this->assertTrue(is_numeric($model->skipValidation(true)->insert($data)));
}
//--------------------------------------------------------------------
}