Yii2 framework backup
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

453 lines
14 KiB

11 years ago
Model
=====
In keeping with the MVC approach, a model in Yii is intended for storing or temporarily representing application data.
Yii models have the following basic features:
11 years ago
- Attribute declaration: a model defines what is considered an attribute.
- Attribute labels: each attribute may be associated with a label for display purpose.
- Massive attribute assignment: the ability to populate multiple model attributes in one step.
- Scenario-based data validation.
11 years ago
Models in Yii extend from the [[yii\base\Model]] class. Models are typically used to both hold data and define
the validation rules for that data (aka, the business logic). The business logic greatly simplifies the generation
of models from complex web forms by providing validation and error reporting.
The Model class is also the base class for more advanced models with additional functionality, such
as [Active Record](active-record.md).
11 years ago
Attributes
----------
The actual data represented by a model is stored in the model's *attributes*. Model attributes can
be accessed like the member variables of any object. For example, a `Post` model
may contain a `title` attribute and a `content` attribute, accessible as follows:
11 years ago
```php
$post = new Post();
11 years ago
$post->title = 'Hello, world';
$post->content = 'Something interesting is happening.';
11 years ago
echo $post->title;
echo $post->content;
```
Since [[yii\base\Model|Model]] implements the [ArrayAccess](http://php.net/manual/en/class.arrayaccess.php) interface,
you can also access the attributes as if they were array elements:
11 years ago
```php
$post = new Post();
11 years ago
$post['title'] = 'Hello, world';
$post['content'] = 'Something interesting is happening';
echo $post['title'];
echo $post['content'];
```
By default, [[yii\base\Model|Model]] requires that attributes be declared as *public* and *non-static*
11 years ago
class member variables. In the following example, the `LoginForm` model class declares two attributes:
`username` and `password`.
11 years ago
```php
// LoginForm has two attributes: username and password
class LoginForm extends \yii\base\Model
{
public $username;
public $password;
}
```
Derived model classes may declare attributes in different ways, by overriding the [[yii\base\Model::attributes()|attributes()]]
method. For example, [[yii\db\ActiveRecord]] defines attributes using the column names of the database table
11 years ago
that is associated with the class.
11 years ago
11 years ago
Attribute Labels
11 years ago
----------------
11 years ago
Attribute labels are mainly used for display purpose. For example, given an attribute `firstName`, we can declare
a label `First Name` that is more user-friendly when displayed to end users in places such as form labels and
error messages. Given an attribute name, you can obtain its label by calling [[yii\base\Model::getAttributeLabel()]].
11 years ago
To declare attribute labels, override the [[yii\base\Model::attributeLabels()]] method. The overridden method returns
a mapping of attribute names to attribute labels, as shown in the example below. If an attribute is not found
in this mapping, its label will be generated using the [[yii\base\Model::generateAttributeLabel()]] method.
In many cases, [[yii\base\Model::generateAttributeLabel()]] will generate reasonable labels (e.g. `username` to `Username`,
`orderNumber` to `Order Number`).
11 years ago
```php
// LoginForm has two attributes: username and password
class LoginForm extends \yii\base\Model
{
public $username;
public $password;
public function attributeLabels()
{
return [
11 years ago
'username' => 'Your name',
11 years ago
'password' => 'Your password',
];
11 years ago
}
}
```
Scenarios
---------
A model may be used in different *scenarios*. For example, a `User` model may be used to collect user login inputs,
but it may also be used for user registration purposes. In the one scenario, every piece of data is required;
in the other, only the username and password would be.
To easily implement the business logic for different scenarios, each model has a property named `scenario`
that stores the name of the scenario that the model is currently being used in. As will be explained in the next
few sections, the concept of scenarios is mainly used for data validation and massive attribute assignment.
11 years ago
Associated with each scenario is a list of attributes that are *active* in that particular scenario. For example,
in the `login` scenario, only the `username` and `password` attributes are active; while in the `register` scenario,
additional attributes such as `email` are *active*. When an attribute is *active* this means that it is subject to validation.
11 years ago
Possible scenarios should be listed in the `scenarios()` method. This method returns an array whose keys are the scenario
names and whose values are lists of attributes that should be active in that scenario:
11 years ago
```php
class User extends \yii\db\ActiveRecord
{
public function scenarios()
{
return [
'login' => ['username', 'password'],
'register' => ['username', 'email', 'password'],
];
11 years ago
}
}
```
If `scenarios` method is not defined, default scenario is applied. That means attributes with validation rules are
considered *active*.
If you want to keep the default scenario available besides your own scenarios, use inheritance to include it:
```php
class User extends \yii\db\ActiveRecord
{
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios['login'] = ['username', 'password'];
$scenarios['register'] = ['username', 'email', 'password'];
return $scenarios;
}
}
```
Sometimes, we want to mark an attribute as not safe for massive assignment (but we still want the attribute to be validated).
We may do so by prefixing an exclamation character to the attribute name when declaring it in `scenarios()`. For example:
11 years ago
```php
['username', 'password', '!secret']
11 years ago
```
In this example `username`, `password` and `secret` are *active* attributes but only `username` and `password` are
considered safe for massive assignment.
Identifying the active model scenario can be done using one of the following approaches:
```php
class EmployeeController extends \yii\web\Controller
{
public function actionCreate($id = null)
{
// first way
$employee = new Employee(['scenario' => 'managementPanel']);
// second way
$employee = new Employee();
$employee->scenario = 'managementPanel';
// third way
$employee = Employee::find()->where('id = :id', [':id' => $id])->one();
if ($employee !== null) {
$employee->scenario = 'managementPanel';
}
}
}
```
The example above presumes that the model is based upon [Active Record](active-record.md). For basic form models,
scenarios are rarely needed, as the basic form model is normally tied directly to a single form and, as noted above,
the default implementation of the `scenarios()` returns every property with active validation rule making it always
available for mass assignment and validation.
11 years ago
Validation
----------
When a model is used to collect user input data via its attributes, it usually needs to validate the affected attributes
to make sure they satisfy certain requirements, such as an attribute cannot be empty, an attribute must contain letters
only, etc. If errors are found in validation, they may be presented to the user to help him fix the errors.
The following example shows how the validation is performed:
```php
$model = new LoginForm();
11 years ago
$model->username = $_POST['username'];
$model->password = $_POST['password'];
if ($model->validate()) {
11 years ago
// ... login the user ...
11 years ago
} else {
$errors = $model->getErrors();
11 years ago
// ... display the errors to the end user ...
11 years ago
}
```
The possible validation rules for a model should be listed in its `rules()` method. Each validation rule applies to one
or several attributes and is effective in one or several scenarios. A rule can be specified using a validator object - an
instance of a [[yii\validators\Validator]] child class, or an array with the following format:
11 years ago
```php
[
['attribute1', 'attribute2', ...],
11 years ago
'validator class or alias',
// specifies in which scenario(s) this rule is active.
// if not given, it means it is active in all scenarios
'on' => ['scenario1', 'scenario2', ...],
11 years ago
// the following name-value pairs will be used
11 years ago
// to initialize the validator properties
'property1' => 'value1',
'property2' => 'value2',
// ...
]
11 years ago
```
When `validate()` is called, the actual validation rules executed are determined using both of the following criteria:
11 years ago
- the rule must be associated with at least one active attribute;
- the rule must be active for the current scenario.
11 years ago
### Creating your own validators (Inline validators)
If none of the built in validators fit your needs, you can create your own validator by creating a method in you model class.
This method will be wrapped by an [[InlineValidator|yii\validators\InlineValidator]] an be called upon validation.
You will do the validation of the attribute and [[add errors|yii\base\Model::addError()]] to the model when validation fails.
The method has the following signature `public function myValidator($attribute, $params)` while you are free to choose the name.
Here is an example implementation of a validator validating the age of a user:
```php
public function validateAge($attribute, $params)
{
$value = $this->$attribute;
if (strtotime($value) > strtotime('now - ' . $params['min'] . ' years')) {
$this->addError($attribute, 'You must be at least ' . $params['min'] . ' years old to register for this service.');
}
}
public function rules()
{
return [
// ...
[['birthdate'], 'validateAge', 'params' => ['min' => '12']],
];
}
```
You may also set other properties of the [[InlineValidator|yii\validators\InlineValidator]] in the rules definition,
for example the [[skipOnEmpty|yii\validators\InlineValidator::skipOnEmpty]] property:
```php
[['birthdate'], 'validateAge', 'params' => ['min' => '12'], 'skipOnEmpty' => false],
```
11 years ago
Massive Attribute Retrieval and Assignment
------------------------------------------
Attributes can be massively retrieved via the `attributes` property.
The following code will return *all* attributes in the `$post` model
as an array of name-value pairs.
```php
$post = Post::find(42);
if ($post) {
$attributes = $post->attributes;
var_dump($attributes);
}
11 years ago
```
Using the same `attributes` property you can massively assign data from associative array to model attributes:
```php
$post = new Post();
$attributes = [
'title' => 'Massive assignment example',
'content' => 'Never allow assigning attributes that are not meant to be assigned.',
];
$post->attributes = $attributes;
var_dump($attributes);
11 years ago
```
11 years ago
In the code above we're assigning corresponding data to model attributes named as array keys. The key difference from mass
11 years ago
retrieval that always works for all attributes is that in order to be assigned an attribute should be **safe** else
it will be ignored.
11 years ago
11 years ago
Validation rules and mass assignment
------------------------------------
In Yii2 unlike Yii 1.x validation rules are separated from mass assignment. Validation
rules are described in `rules()` method of the model while what's safe for mass
assignment is described in `scenarios` method:
```php
class User extends ActiveRecord
11 years ago
{
public function rules()
{
return [
// rule applied when corresponding field is "safe"
['username', 'string', 'length' => [4, 32]],
['first_name', 'string', 'max' => 128],
['password', 'required'],
// rule applied when scenario is "signup" no matter if field is "safe" or not
['hashcode', 'check', 'on' => 'signup'],
];
}
public function scenarios()
{
return [
// on signup allow mass assignment of username
'signup' => ['username', 'password'],
'update' => ['username', 'first_name'],
];
}
11 years ago
}
```
For the code above mass assignment will be allowed stsrictly according to `scenarios()`:
```php
$user = User::find(42);
$data = ['password' => '123'];
$user->attributes = $data;
print_r($user->attributes);
```
Will give you empty array because there's no default scenario defined in our `scenarios()`.
```php
$user = User::find(42);
$user->scenario = 'signup';
$data = [
'username' => 'samdark',
'password' => '123',
'hashcode' => 'test',
];
$user->attributes = $data;
print_r($user->attributes);
```
Will give you the following:
```php
array(
'username' => 'samdark',
'first_name' => null,
'password' => '123',
'hashcode' => null, // it's not defined in scenarios method
)
```
In case of not defined `scenarios` method like the following:
11 years ago
```php
class User extends ActiveRecord
11 years ago
{
public function rules()
{
return [
['username', 'string', 'length' => [4, 32]],
['first_name', 'string', 'max' => 128],
['password', 'required'],
];
}
11 years ago
}
```
The code above assumes default scenario so mass assignment will be available for all fields with `rules` defined:
```php
$user = User::find(42);
$data = [
'username' => 'samdark',
'first_name' => 'Alexander',
'last_name' => 'Makarov',
'password' => '123',
];
$user->attributes = $data;
print_r($user->attributes);
```
Will give you the following:
11 years ago
```php
array(
'username' => 'samdark',
'first_name' => 'Alexander',
'password' => '123',
)
```
11 years ago
11 years ago
If you want some fields to be unsafe for default scenario:
```php
class User extends ActiveRecord
{
function rules()
{
return [
['username', 'string', 'length' => [4, 32]],
['first_name', 'string', 'max' => 128],
['password', 'required'],
];
}
public function scenarios()
{
return [
11 years ago
self::SCENARIO_DEFAULT => ['username', 'first_name', '!password']
];
}
}
```
Mass assignment is still available by default:
```php
$user = User::find(42);
$data = [
'username' => 'samdark',
'first_name' => 'Alexander',
'password' => '123',
];
$user->attributes = $data;
11 years ago
print_r($user->attributes);
```
The code above gives you:
```php
array(
'username' => 'samdark',
'first_name' => 'Alexander',
'password' => null, // because of ! before field name in scenarios
)
```
11 years ago
See also
--------
- [Model validation reference](validation.md)
- [[yii\base\Model]]