If you're asking, "what's Yii?" check out my earlier tutorial: Introduction to the Yii Framework, which reviews the benefits of Yii and includes an overview of what's new in Yii 2.0, released in October 2014.
In this Programming With Yii2 series, I'm guiding readers in use of the newly upgraded Yii2 Framework for PHP. In this tutorial, I'm going to introduce you to Yii2's validators. Validators simplify the code needed to validate input, i.e. verify conformance or non-conformance of data input, typically from users via web forms.
For these examples, we'll continue to leverage the Hello application codebase we've used in past tutorials. Use the GitHub links on this page to get the code.
Just a reminder, I do participate in the comment threads below. I'm especially interested if you have additional ideas or want to suggest topics for future tutorials. You can also reach me @reifman on Twitter.
What Is a Validator?
If you're a web developer, you likely know that user input can't be trusted. For example, users can use SQL injection techniques to try to run queries that change or expose passwords. Someone once leveraged SQL injection against my open source PHPList installation and managed to discover one of my passwords (PHPList stored these in plain text). More commonly, you just want to ensure that the data that users provide conforms to the types, forms and ranges of your application.
Building validators in PHP by hand takes time. The Yii Framework provides a ton of baseline validation features so there's no need to build them from scratch. But, if you need some custom extensions, that's straightforward as well.
Validations are yet another reason why I think it always makes sense to build applications on a web framework such as Yii rather than vanilla PHP.
In earlier episodes, we've also talked a lot about Yii's code generator, Gii. One of the benefits of Gii is that it will write the appropriate validation rules for your models based on the SQL type definitions in the schema. This is a big time saver.
What Validations Does Yii Support?
Here's a list of the built in Yii validators and links to documentation:
- BooleanValidator. Ensures a value is true or false.
- CaptchaValidator. Validates a CAPTCHA form verification field.
- CompareValidator. Compares two values from the form or a constant, e.g. x must be less than 99.
- DateValidator. Ensures value is a date.
- DefaultValueValidator. Not a true validator. Sets default values for empty fields.
- NumberValidator. Ensures a value is numeric e.g. integer or float.
- EmailValidator. Ensures a value is a valid email address.
- ExistValidator. Ensures that a value exists in another table.
- FileValidator. Ensures existence of an uploaded file.
- FilterValidator. Not a true validator. Performs a transformation on provided value.
- ImageValidator. Validates image and image properties.
- RangeValidator. Ensures a value is within a list of values.
- RegularExpressionValidator. Performs validation against a condition defined by a regular expression.
- RequiredValidator. Ensures a value is present.
- SafeValidator. Not a true validator. Allows massive assignment of a posted web form to include an attribute. e.g. $model->attributes = $_POST['Comment'];
- StringValidator. Ensures value is a string.
- UniqueValidator. Ensures value is unique within a table, such as an email address.
- UrlValidator. Ensures value is in URL format e.g. http://yourdomain.com
How Yii Validation Works
Here's how Yii describes the flow of validation. Typically, you can use the default scenario and don't need to build your own. You'll generally need to rely on Gii to generate rules or write your own.
When the validate()
method is called, it goes through the following steps to perform validation:
- Determine which attributes should be validated by getting the attribute list from yii\base\Model::scenarios() using the current scenario. These attributes are called active attributes.
- Determine which validation rules should be used by getting the rule list from yii\base\Model::rules() using the current scenario. These rules are called active rules.
- Use each active rule to validate each active attribute which is associated with the rule. The validation rules are evaluated in the order they are listed.
According to the above validation steps, an attribute will be validated if and only if it is an active attribute declared in scenarios()
and is associated with one or multiple active rules declared in rules()
.
Example of Model Validation Rules
Here's what a set of model validation rules may look like. I've taken these from the Meeting Planner application Place model:
public function rules() { return [ [['name','slug'], 'required'], [['place_type', 'status', 'created_by', 'created_at', 'updated_at'], 'integer'], [['name', 'google_place_id', 'slug', 'website', 'full_address', 'vicinity'], 'string', 'max' => 255], [['website'], 'url'], [['slug'], 'unique'], [['searchbox'], 'unique','targetAttribute' => 'google_place_id'], [['name', 'full_address'], 'unique', 'targetAttribute' => ['name', 'full_address']], ]; }
As we implement our own validation examples further below, you'll learn what each of the definitions above represent.
Example of Displaying Errors
There are a couple of ways to access the errors returned by validation.
Here's an example of getting the array of errors in the controller:
$model = new \app\models\ContactForm; // populate model attributes with user inputs $model->attributes = \Yii::$app->request->post('ContactForm'); if ($model->validate()) { // all inputs are valid } else { // validation failed: $errors is an array containing error messages $errors = $model->errors; }
And here's an example of leveraging Yii's errorSummary function within ActiveForms:
<div class="meeting-place-form"> <?php $form = ActiveForm::begin(); ?> <?= $form->errorSummary($model); ?>
Here's what it looks like:
Advanced Validation
In later episodes, I'll also give examples of making use of advanced validation features:
- Defining scenarios to selectively apply rules for certain situations
- Defining custom error messages
- Validation events to override validation or perform specific functionality before and/or after validation
- Conditional validation to perform a validation rule only if a specific event is true
-
Ad Hoc validation to use validation rules independently of form submission
- Custom validators to create essential validations beyond what Yii offers out of the box
- Client side validation to make use of Yii's built-in ActiveForm JavaScript validation without requiring a page refresh
- AJAX validation for implementing server side AJAX validations to extend Yii's client-side JavaScript page validation capabilities
For now, let's begin walking through examples of the various kinds of built-in validators.
Basic Field Validators
Let's look at some of the basic field validators which are helpful to everyday form implementation.
Preparing a Model Using Migrations and Gii
As we've done in early episodes of this series, I'm going to create a migration:
./yii migrate/create create_sample_table
I'll create a Sample model to create some example schema and validations using Gii. Here's the migration code:
<?php use yii\db\Schema; use yii\db\Migration; class m150219_235923_create_sample_table extends Migration { public function up() { $tableOptions = null; if ($this->db->driverName === 'mysql') { $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; } $this->createTable('{{%sample}}', [ 'id' => Schema::TYPE_PK, 'thought' => Schema::TYPE_STRING.' NOT NULL DEFAULT ""', 'goodness' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'rank' => Schema::TYPE_INTEGER . ' NOT NULL', 'censorship' => Schema::TYPE_STRING . ' NOT NULL', 'occurred' => Schema::TYPE_DATE . ' NOT NULL', ], $tableOptions); } public function down() { $this->dropTable('{{%sample}}'); } }
Then, we'll run the migration:
./yii migrate/up Yii Migration Tool (based on Yii v2.0.2) Total 1 new migration to be applied: m150219_235923_create_sample_table Apply the above migration? (yes|no) [no]:yes *** applying m150219_235923_create_sample_table > create table {{%sample}} ... done (time: 0.009s) *** applied m150219_235923_create_sample_table (time: 0.015s) Migrated up successfully.
Then, we'll use Yii's code generator to build a model:
And then CRUD files:
Gii generates these sample validation rules:
class Sample extends \yii\db\ActiveRecord { public function rules() { return [ [['goodness', 'rank'], 'integer'], [['rank', 'censorship', 'occurred'], 'required'], [['occurred'], 'safe'], [['thought', 'censorship'], 'string', 'max' => 255] ]; }
Now, let's use these to work with and walk through some of the basic validators.
Required Validator
The RequiredValidator ensures a value is present. You can see it in place above for rank, censorship and occurred.
Visit the Sample Create form generated by Gii, e.g. http://localhost:8888/hello/sample/create. Yii's ActiveForm JavaScript client-validation will present an error message even when you tab away from one of these fields.
Safe Validator
The SafeValidator is not a true validator. It allows massive assignment of a posted web form to include an attribute. e.g. $model->attributes = $_POST['Comment']. Or, in the Gii created SampleController, you can see this code:
public function actionCreate() { $model = new Sample(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('create', [ 'model' => $model, ]); } }
Without the safe rule in the Sample model (or another rule), the occurred value would not be assigned to the model attributes. This reduces the likelihood of an additional attack vector without deliberate code.
public function rules() { return [ [['occurred'], 'safe'],
Default Value Validator
The DefaultValueValidator is not a true validator. It sets default values for empty fields.
Let's change the rule for occurred
to set a default date value using the current date. We'll also remove the required validator to allow the default validator to fill the value.
public function rules() { return [ [['goodness', 'rank'], 'integer'], [['rank', 'censorship'], 'required'], // Gii created this // [['occurred'], 'safe'], ['occurred', 'default', 'value' => date("Y-m-d")],
When we create a new Sample and leave the occurred
field blank, you can see the resulting view includes the current date filled in by the default value validator.
Filters
The FilterValidator is also not a true validator. It performs a transformation on a provided value. Most commonly, you might use this to trim whitespace off the ends of a string.
FilterValidators are defined with inline function callbacks such as this custom validation function:
// an inline validator defined as an anonymous function ['token', function ($attribute, $params) { if (!ctype_alnum($this->$attribute)) { $this->addError($attribute, 'The token must contain letters or digits.'); } }],
Since trim is a native PHP function, we can just declare our validation rule inline:
[['thought'], 'trim'],
If you submit a form with pre-pending or trailing spaces on the thought field, the FilterValidator will remove them.
Now, let's look at some of the built-in type validators.
The Type Validators
Type validators ensure that user data conforms to specific types, often those specified in your database schema. Gii will generate these automatically.
String and Number Validator
The StringValidator ensures a value is a string. The NumberValidator ensures a value is numeric, e.g. integer or float.
Here are sample rule definitions:
public function rules() { return [ [['goodness', 'rank'], 'integer'], [['thought', 'censorship'], 'string', 'max' => 255] // [['rank', 'censorship'], 'required'],
I'm also temporarily removing the required validation to see how string and number validations work independently.
Here's what the validation error messages will look like:
Goodness as high fails because it's not a number, whereas rank as 27 passes. Censorship is blank (NULL) and fails the string validation.
Boolean Validator
The BooleanValidator ensures a value is true or false. You can define the values for true and false. The defaults are integer 0 or 1. This validator may be more useful when the field is used with a drop-down selector, e.g. Yes / No.
Here's how I defined my rule for Boolean:
public function rules() { return [ [['goodness'], 'boolean'], [['rank'], 'integer'],
Here's the boolean validator error message:
Date Validator
The DateValidator ensures the value is a properly formatted date which can be customized with a format attribute. With Yii ActiveForm, this is currently a server side validation. Therefore, I also added back a required rule for the Occurred field.
Here are my rule definitions with the Date validator for the Occurred field:
public function rules() { return [ [['goodness'], 'boolean'], [['rank'], 'integer'], [['thought', 'censorship'], 'string', 'max' => 255], [['rank', 'censorship','occurred'], 'required'], ['occurred', 'date', 'format' => 'yyyy-M-d'], //['occurred', 'default', 'value' => date("Y-m-d")], [['thought'], 'trim'], ]; }
Here's what it looks like when we submit the form:
What's Next?
Watch for upcoming tutorials in my Programming With Yii2 series as I continue diving into different aspects of the framework. In the next two episodes, I'll guide you through the remaining validators and show you how to build advanced extensions to Yii's validation framework.
You may also want to check out my Building Your Startup With PHP series, which is using Yii2's advanced template as I build a real world application.
I welcome feature and topic requests. You can post them in the comments below, ping me @reifman on Twitter, or email me at my Lookahead Consulting website.
If you'd like to know when the next Yii2 tutorial arrives, check my Tuts+ instructor page. It always includes all my articles immediately after they are published.
Related Links
- Yii2 Guide to Validating User Input
- Yii2 Validators (Documentation)
- Yii2 Developer Exchange, my Yii2 resource site
Comments