Inside Yii 2-Part 2
This is the part 2 of the series, Inside Yii 2. Here we will be looking at Model in the MVC pattern, and how we can make use of it in Yii 2.
This post assumes that you read the part 1 of the series.
Models
The extract below from wikipedia tells us how we can reason about the M in the MVC pattern.
A model stores data that is retrieved according to commands from the controller and displayed in the view.
The code for this post can be found here. Just checkout branch models-part2
.
Simple right?
To break it down a bit, the following highlights how we can use it:
I use Model to represent and accept user inputs e.g User details.
I can define some validation rules on the Model to ascertain a valid and expected user input.
I use the Model to store the valid user input data either in memory or persist the data to a storage e.g. MySQL, Redis e.t.c
When the user later asks for the saved information, we will retrieve the data from where it was stored via the model and present it to the user.
We can simply visualize it this way:
Storing: User -> View -> Controller -> Model -> Storage
Retrieving: User <- View <- Controller <- Model <- Storage
The data flows in the direction of the arrows.
Enough of the bla bla bla… let’s do some fun stuff.
Creating Models
There are two options, creating the file manually or using Yii 2 in-built gii
command.
The model generator
takes two required options --tableName
and --modelClass
. We can get this by running php yii gii/model --help
from the root of our application. The command also gives us details of other options and their default values.
--modelClass
- This is the name of the ActiveRecord class that would be generated. AR provides an object-oriented solution, which helps us to interact with our DB tables. More details about AR implementation in Yii 2 is here - AR in Yii.--tableName
- Name of the database table that the ActiveRecord class is associated with.
Setup App DB
Before moving forward, we need to first create a DB and table. I expect you already have a MySQL server
setup in your local environment. If not, you can get MySQL
up and running from here.
I will assume you now have MySQL running, just create a database. I will call mine inside_yii2_db
.
Create tables
We will go ahead and create our db tables. We will do that manually and you can use the sql query below to create yours, though Yii comes with a very nice way of creating our db structure using db migration feature, just like in most frameworks. But we wouldn’t do that now, may be in future posts.
CREATE TABLE `semesters` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `courses` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL DEFAULT '',
`semester_id` int(11) unsigned NOT NULL,
`total_registered` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `fk_courses_semester_id` (`semester_id`),
CONSTRAINT `fk_courses_semester_id` FOREIGN KEY (`semester_id`) REFERENCES `semesters` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Update db credentials on the db config
Open the db config file - config/db.php
<?php return [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=127.0.0.1;dbname=inside_yii2_db', 'username' => 'devuser', 'password' => 'secret', 'charset' => 'utf8', ];
You can update the credentials - dbname, username, password
for your own environment.
Create Model with command
The commands below will create the corresponding models for our tables
php yii gii/model --tableName=semesters --modelClass=Semester
php yii gii/model --tableName=courses --modelClass=Course
One of benefits of model generator is that it reads the constraints, columns data types as defined in the table and generates rules for validation, relations based on foreign key constraints.
You can open the models classes
models/Semester.php
models/Course.php
Note:
The generator will generate the relations using the database names.
For example, check Course.php
/**
* @return \yii\db\ActiveQuery
*/
public function getSemester()
{
return $this->hasOne(Semesters::className(), ['id' => 'semester_id']);
}
We can see in the method above, the relation class name is Semesters
(plural) instead of Semester
(singular) - this is the ideal naming convention for a model
since the ActiveRecord
class represents a single record in the semesters table
.
The generator uses the table name
of the related table via the foreign key
to create the relations method. To avoid modifying the generated class, some preferred naming their db tables in singular noun.
Model rules
/**
* @inheritdoc
*/
public function rules()
{
return [
[['semester_id'], 'required'],
[['semester_id', 'total_registered'], 'integer'],
[['name'], 'string', 'max' => 50],
[['semester_id'], 'exist', 'skipOnError' => true, 'targetClass' => Semester::className(), 'targetAttribute' => ['semester_id' => 'id']],
];
}
Let’s look at the rules one by one.
- required
: As we can see, the generated rules defined semester_id
as a required
field, when we call method validate
on course model and the semester_id
is missing, it will fail with error message Semester ID cannot be blank.
.
integer
: If you try to save any other value other thaninteger
in any of the fields defined asinteger
, the validation will fail with messageTotal Registered must be an integer.
max
: Here we defined that the maximun length for fieldname
is 50.exist
: This is another interesting validator, this validator checks if the value insemester_id
can be found in the target column of the table represented by the target class, i.e. is there is record insemesters
table whoseid
is what we have in thesemester_id
.
There are many other useful validators that the framework provides.
Load, Validate and Save
Now let’s look at some basic methods that we can call on a model to successfully save it.
$data = [
'Semester' => [
'name' => 'Spring',
]
];
// create the instance of the model
$semester = new Semester();
// load the data (e.g. user input from the post data from the http request)
$semester->load($data);
// if validate is successful, save it
if ($semester->validate()) {
$semester->save();
} else {
var_dump($semester->getErrors());
}
If validation fails, we can get the errors by calling getErrors()
on the model.
In the next post of the series Inside-Yii2 part 3, we will look at form models
and how we can use it to take user inputs, clean it up, validate, load the cleaned input to the actual model and save.
WATCH OUT! for more.
You can get the application code on github - inside-yii2 repo, just checkout the models-part2 branch.