Model là phần trong mô hình MVC. Là đối tượng đại diện cho phần dữ liệu, phương thức xử lý và nghiệp vụ logic.
Bạn có thể tạo mới các lớp model bằng việc kế thừa từ lớp yii\base\Model hoặc các lớp con của nó. Lớp cơ sở yii\base\Model hỗ trợ nhiều tính năng như:
Lớp Model
thường dựa trên lớp để thực hiện chức năng nâng cao, chẳng hạn Active Record.
Vui lòng tham khảo thêm tài liệu để biết thêm thông tin.
Lưu ý: Model của bạn không phải bắt buộc kế thừa từ lớp yii\base\Model. Tuy nhiên, vì Yii chứa nhiều thành phần dựng lên và hỗ trợ cho yii\base\Model, vì thế nó là lớp cơ sở cho các lớp Model.
Model đại diện cho tầng xử lý nghiệp vụ và chứa các thuộc tính. Mỗi thuộc tính được truy cập toàn cục như phần tử của model. Phương thức yii\base\Model::attributes() sẽ mô tả các thuộc tính trong lớp model hiện có.
Bạn có thể truy cập vào thuộc tính như các phần tử của các đối tượng:
$model = new \app\models\ContactForm;
// "name" là tên thuộc tính của ContactForm
$model->name = 'example';
echo $model->name;
Bạn có thể truy cập các thuộc tính như truy cập mảng các phần tử, nhờ sự hỗ trợ từ lớp ArrayAccess và ArrayIterator bởi yii\base\Model:
$model = new \app\models\ContactForm;
// truy cập các thuộc tính như mảng các phần tử
$model['name'] = 'example';
echo $model['name'];
// iterate attributes
foreach ($model as $name => $value) {
echo "$name: $value\n";
}
Mặc định, nếu Model của bạn được kế thừa từ lớp yii\base\Model, và tất cả các biến có phạm vi toàn cục trong lớp
. Ví dụ, Model ContactForm
sau có bốn thuộc tính là: name
, email
,
subject
và body
. Model ContactForm
dùng để nhận dữ liệu từ form HTML.
namespace app\models;
use yii\base\Model;
class ContactForm extends Model
{
public $name;
public $email;
public $subject;
public $body;
}
Bạn có thể ghi đè phương thức yii\base\Model::attributes() để định nghĩa các thuộc tính theo các cách khác. Phương thức nên được trả
tên của thuộc tính trong Model. Ví dụ, lớp yii\db\ActiveRecord trả về
danh sách tên của các cột liên quan tới các bảng trong CSDL như tên các thuộc tính. Bạn có thể ghi đè các phương thức như
__get()
, __set()
để có thể truy cập các thuộc tính như các đối tượng thông thường.
Mỗi khi cần hiển thị giá trị hoặc nhận dữ liệu cho thuộc tính, bạn cần hiển thị nhãn tương ứng với các thuộc tính
. Ví dụ, với thuộc tính firstName
, bạn cần hiển thị nhãn First Name
nhãn này sẽ thân thiện hơn khi hiển thị tới người dùng với việc nhập dữ liệu và hiện thông báo.
Bạn có thể lấy tên nhãn các thuộc tính quan việc gọi phương thức yii\base\Model::getAttributeLabel(). Ví dụ,
$model = new \app\models\ContactForm;
// hiển thị "Name"
echo $model->getAttributeLabel('name');
Mặc định, nhãn thuộc tính sẽ tự động tạo từ tên của thuộc tính.
Phương thức yii\base\Model::generateAttributeLabel() sẽ tạo mới các nhãn cho các thuộc tính. Nó sẽ chuyển tên các biến thành các từ mới
qua việc chuyển ký tự đầu tiên thành ký tự in hoa. Ví dụ, username
thành Username
,
và firstName
thành First Name
.
Nếu bạn không muốn việc tạo các nhản bằng cách tự động, bạn cần ghi đè phương thức yii\base\Model::attributeLabels() để mô tả các thuộc tính. Chẳng hạn,
namespace app\models;
use yii\base\Model;
class ContactForm extends Model
{
public $name;
public $email;
public $subject;
public $body;
public function attributeLabels()
{
return [
'name' => 'Tên liên hệ',
'email' => 'Địa chỉ email',
'subject' => 'Tiêu đề',
'body' => 'Nội dung',
];
}
}
Với ứng dụng cần hỗ trợ đa ngôn ngữ, bạn cần dịch lại nhãn của các thuộc tính. Xem trong phương thức attributeLabels() , như sau:
public function attributeLabels()
{
return [
'name' => \Yii::t('app', 'Tên liên hệ'),
'email' => \Yii::t('app', 'Địa chỉ email'),
'subject' => \Yii::t('app', 'Tiêu đề'),
'body' => \Yii::t('app', 'Nội dung'),
];
}
Bạn có thể gán nhãn cho các thuộc tính. Chẳng hạn, dựa vào scenariocủa Model đã được sử dụng , bạn có thể trả về các nhãn khác nhau cho các thuộc tính khác nhau.
Lưu ý: Chính xác rằng, nhãn của thuộc tính là một phần của views. Tuy nhiên việc khai báo các nhãn vào Model thường rất tiện lợi, code dễ nhìn và tái sử dụng.
Model thường được sử dụng ở các kịch bản khác nhau . Ví dụ, Model User
dùng để xử lý việc đăng nhập,
nhưng cũng có thể được dùng ở mục đăng ký. Ở các kịch bản khác nhau, Model có thể được dùng trong các nghiệp vụ
và xử lý logic khác nhau. Ví dụ,thuộc tính email
có thể được yêu cầu trong mục đăng ký tài khoản mới,
nhưng không được yêu cầu khi xử lý đăng nhập.
Mỗi Model sử dụng thuộc tính yii\base\Model::$scenario để xử lý tuỳ theo kịch bản cần đợc dùng.
Mặc định, Model sẽ hỗ trợ kịch bản là default
. Xem đoạn mã sau để hiểu 2 cách thiết lập kịch bản cho Model.
setting the scenario of a model:
// kịch bản được thiết lập qua thuộc tính
$model = new User;
$model->scenario = User::SCENARIO_LOGIN;
// kịch bản được thiết lập qua việc cấu hình khởi tạo
$model = new User(['scenario' => User::SCENARIO_LOGIN]);
Mặc định, các kịch bản được hỗ trợ bởi model được xác định qua các nguyên tắc xác minh được mô tả ở Model. Tuy nhiên, bạn có thê tuỳ biến bằng cách ghi đè phương thức yii\base\Model::scenarios(), như sau:
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
const SCENARIO_LOGIN = 'login';
const SCENARIO_REGISTER = 'register';
public function scenarios()
{
return [
self::SCENARIO_LOGIN => ['username', 'password'],
self::SCENARIO_REGISTER => ['username', 'email', 'password'],
];
}
}
Lưu ý: Như phần trên và ví dụ vừa rồi, lớp Model được kế thừa từ lớp yii\db\ActiveRecord bởi vì lớp Active Record thường được sử dụng nhiều kịch bản.
Phương thức scenarios()
trả về một mảng có chứa các khóa là tên các kịch bản và các giá trị tương ứng là các
danh sách thuộc tính được chọn. An active attribute can be massively assigned và là đối tượng sẽ được
dùng để xác thực (validation). Chẳng hạn ở ví dụ trên, thuộc tính username
và password
sẽ được chọn
ở kịch bản login
; còn ở kịch bản register
, sẽ có thêm thuộc tính email
ngoài 2 thuộc tính username
và password
.
Việc triển khai phương thức scenarios()
mặc định sẻ trả về các kịch bản tìm thấy trong phương thức
yii\base\Model::rules(). Khi khi đè phương thức scenarios()
, nếu bạn muốn khai báo các kịch bản mới, ngoài các kịch bản mặc định
in addition to the default ones, bạn có thể viết mã như sau:
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
const SCENARIO_LOGIN = 'login';
const SCENARIO_REGISTER = 'register';
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios[self::SCENARIO_LOGIN] = ['username', 'password'];
$scenarios[self::SCENARIO_REGISTER] = ['username', 'email', 'password'];
return $scenarios;
}
}
Xây dựng các kịch bản được dùng vào việc xác thực và massive attribute assignment. Tuy nhiên, bạn có thể dùng vào mục đích khác. Chẳng hạn, bạn có thể khai báo các nhãn thuộc tính khác nhau được dựa trên kịch bản hiện tại.
Khi dữ liệu cho model được chuyển lên từ người dùng cuối, dữ liệu này cần được xác thực để chắc chắn rằng dữ liệu này là hợp lệ
(được gọi là quy tắc xác nhận, có thể gọi business rules). Ví dụ, cho model ContactForm
,
bạn muốn tất cả các thuộc tính không được để trống và thuộc tính email
phải là địa chỉ email hợp lệ.
Nếu các giá trị cho các thuộc tính không được thỏa mãn với các quy tắc xác nhận, các thông báo lỗi sẽ được
được hiển thị để giúp người dùng sửa lỗi.
Bạn có thể gọi phương thức yii\base\Model::validate() để xác thực các dữ liệu đã nhận. Phương thức sẽ dùng các quy tắc xác nhận
được khai báo ở phương thức yii\base\Model::rules() để xác thực mọi thuộc tính liên quan. Nếu không có lỗi nào tìm thấy
, sẽ trả về giá trị true
. Nếu không thì, phương thức sẽ giữ các thông báo lỗi tại thuộc tính yii\base\Model::$errors
và trả kết quảfalse
. Ví dụ,
$model = new \app\models\ContactForm;
// gán các thuộc tính của model từ dữ liệu người dùng
$model->attributes = \Yii::$app->request->post('ContactForm');
if ($model->validate()) {
// tất cả các dữ liệu nhập vào hợp lệ
} else {
// xác nhận lỗi: biến $errors chứa mảng các nội dung thông báo lỗi
$errors = $model->errors;
}
Các quy tắc xác nhận được gắn vào model, việc ghi đè phương thức yii\base\Model::rules() cùng với việc trả về
có chứa các thuộc tính an toàn cần được xác thực. Ví dụ sau đây sẽ cho thấy các quy tắc xác nhận được khai báo cho model
ContactForm
:
public function rules()
{
return [
// the name, email, subject and body attributes are required
[['name', 'email', 'subject', 'body'], 'required'],
// the email attribute should be a valid email address
['email', 'email'],
];
}
Mỗi quy tắc được dùng để xác nhận một hoặc nhiều các thuộc tính, và một thuộc tính có thể được xác nhận một hoặc nhiều quy tắc. Vui lòng tham khảo mục Xác nhận đầu vào để biết thêm chi tiết về cách khai báo các quy tắc xác nhận.
Đôi khi, bạn muốn các quy tắc chỉ được áp dụng chỉ trong một số kịch bản. Để làm như vậy, bạn có thể
thêm thông tin thuộc tính on
ở mỗi quy tắc, giống như sau:
public function rules()
{
return [
// thuộc tính username, email và password cần được nhập ở kịch bản "register"
[['username', 'email', 'password'], 'required', 'on' => self::SCENARIO_REGISTER],
// username và password cần được nhập ở kịch bản "login"
[['username', 'password'], 'required', 'on' => self::SCENARIO_LOGIN],
];
}
Nếu bạn không chỉ định thuộc tính on
, quy tắc sẽ áp dụng trong tất cả các kịch bản. Một quy tắc được gọi
một quy tắc hoạt động nếu nó được áp dụng với kịch bản hiện tại scenario.
Một thuộc tính được xác nhận nếu và chỉ nếu nó là thuộc tính được kích hoạt với khai báo tại phương thức scenarios()
và
được liên kết với một hoặc nhiều quy tắc được khai báo ở phương thức rules()
.
Gán nhanh là cách tiện lợi cho việc nhập dữ liệu vào model từ người dùng với một dòng mã.
Nó nhập vào các thuộc tính của model bằng việc gán dữ liệu nhập vào qua thuộc tính yii\base\Model::$attributes
. 2 đoạn mã sau hoạt động giống nhau , cả 2 đều lấy dữ liệu trong form gửi lên từ người dùng
vào các thuộc tính của model ContactForm
. Nhanh gọn, cách trên, sẽ dùng gán nhanh, mã của bạn trông sạch và ít lỗi hơn cách sau đó:
$model = new \app\models\ContactForm;
$model->attributes = \Yii::$app->request->post('ContactForm');
$model = new \app\models\ContactForm;
$data = \Yii::$app->request->post('ContactForm', []);
$model->name = isset($data['name']) ? $data['name'] : null;
$model->email = isset($data['email']) ? $data['email'] : null;
$model->subject = isset($data['subject']) ? $data['subject'] : null;
$model->body = isset($data['body']) ? $data['body'] : null;
Gán nhanh chỉ gán dữ liệu cho những thuộc tính gọi là thuộc tính an toàn (safe attributes) đó là các thuộc tính được liệt kê trong phương thức
yii\base\Model::scenarios() cho thuộc tính scenario của model.
Chẳng hạn, nếu model User
có các kịch bản mô tả như sau, tiếp đến kịch bản
login
đang được chọn, thì chỉ thuộc tính username
và password
có thể được gán nhanh. Bất kỳ các thuộc tính khác
sẽ được giữ nguyên.
public function scenarios()
{
return [
self::SCENARIO_LOGIN => ['username', 'password'],
self::SCENARIO_REGISTER => ['username', 'email', 'password'],
];
}
Thông tin: Lý do việc gán nhanh chỉ gán dữ liệu cho các thuộc tính an toàn là bởi vì bạn muốn kiểm soát những thuộc tính có thể được thay đổi bởi người dùng. Chẳng hạn, nếu model
User
có thuộc tínhpermission
nhằm xác định các quyền hạn của người dùng, bạn chỉ muốn thuộc tính này chỉ được thay đổi bởi quản trị viên thông qua giao diện phụ trợ.
Bởi vì mặc định phương thức yii\base\Model::scenarios() sẽ trả về tất cả các kịch bản và thuộc tính nằm trong phương thức yii\base\Model::rules(), nếu bạn không ghi đè phương thức này, có nghĩa là một thuộc tính là an toàn miễn là có khai báo ở một trong các quy tắc xác nhận.
Vì lý do này, bí danh safe
được đưa ra bạn có thể khai báo các thuộc tính an toàn
mà không thực sự xác nhận nó. Chẳng hạn, các quy tắc sau đây khai báo thuộc tính title
và description
là thuộc tính an toàn.
public function rules()
{
return [
[['title', 'description'], 'safe'],
];
}
Như mô tả trên, khai báo phương thức yii\base\Model::scenarios() có 2 mục đích: liệt kê thuộc tính cần được xác nhận
, và xác định các thuộc tính là an toàn. Trong một số trường hợp khác, bạn muốn xác nhận thuộc tính nhưng
không muốn đánh dấu là an toàn. bạn có thể thực hiện bằng việc đặt dấu chấm than !
vào tên thuộc tính
khi khai báo tại phương thức scenarios()
, giốn như thuộc tính secret
như sau:
public function scenarios()
{
return [
self::SCENARIO_LOGIN => ['username', 'password', '!secret'],
];
}
Khi model đang ở kịch bản login
, cả 3 thuộc tính sẽ được xác nhận. Tuy nhiên, chỉ có thuộc tính username
và password
được gán nhanh. Để gán giá trị cho thuộc tính secret
, bạn
cần được gán trực tiếp như sau,
$model->secret = $secret;
Điều tương tự có thể được thực hiện trong phương thức rules()
:
public function rules()
{
return [
[['username', 'password', '!secret'], 'required', 'on' => 'login']
];
}
Trong trường hợp này các thuộc tính username
, password
và secret
là yêu cầu nhập, nhưng thuộc tính secret
phải cần được gán trực tiếp.
Các model thường được cần trích xuất ra các định dạng khác nhau. Chẳng hạn, bạn cần chuyển dữ liệu sang của models sang định dạng JSON hoặc Excel. Quá trình xuất có thể được chia nhỏ thành hai bước độc lập:
Bạn chỉ cần tập trung vào bước đầu tiên, bởi vì bước thứ 2 có thể được thực hiện bởi các trình định dạng dữ liệu , chẳng hạn như yii\web\JsonResponseFormatter.
Các đơn giản nhất để chuyển đổi model sang dạng mảng là sử dụng thuộc tính yii\base\Model::$attributes. For example,
$post = \app\models\Post::findOne(100);
$array = $post->attributes;
Bởi mặc định, thuộc tính yii\base\Model::$attributes sẽ trả về các giá trị của tất cả các thuộc tính được khai báo trong phương thức yii\base\Model::attributes().
Còn một cách linh hoạt và tiện lợi hơn trong việc chuyển đổi model sang định dạng mảng là sử dụng phương thức yii\base\Model::toArray() . Cách chuyển đổi cũng tương tự như trong cách của thuộc tính yii\base\Model::$attributes. Tuy nhiên, nó cho phép bạn chọn các dữ liệu , được gọi là fields, được đặt trong mảng kết quả và chúng được định dạng thế nào. Trong thực tế, đó là cách trích xuất mặc định của các model ở việc phát triển các dịch vụ RESTful Web, như được mô tả trong mục Response Formatting.
Một trường đơn giản là tên của thành phần thu được nằm trong mảng khi gọi phương thức yii\base\Model::toArray() của model.
Mặc định, tên trường sẽ tương đương với tên thuộc tính. Tuy nhiên, bạn có thể thay đổi bằng việc ghi đè
qua phương thức fields() và/hoặc phương thức extraFields(). Cả 2 phương thức
trả về danh sách các khai báo trường. Các trường được định nghĩa bởi phương thức fields()
là các trường mặc định, nghĩa là phương thức
toArray()
sẽ trả về những trường mặc định. Phương thức extraFields()
sẽ khai báo thêm các trường bổ sung có thể được trả về
bởi phương thức toArray()
miễn là bạn chỉ định chugns qua tham số $expand
. Chẳng hạn,
đoạn mã sau sẽ trả về các trường được định nghĩa trong phương thức fields()
và 2 trường prettyName
và fullAddress
nếu chúng được định nghĩa trong phương thức extraFields()
.
$array = $model->toArray([], ['prettyName', 'fullAddress']);
Bạn có thể ghi đè phương thức fields()
để thêm, xóa, cập nhật hoặc định nghĩa lại các trường. Phương thức fields()
sẽ trả về dữ liệu dạng mảng. Mảng này có các khóa là tên các trường, và các giá trị của mảng tương ứng
với các trường đã định nghĩa giá trị có thể là tên các thuộc tính/biến hoặc một hàm trả về các giá trị trường tương ứng
. Trong trường hợp đặc biệt khi tên trường giống với tên thuộc tính xác định của nó, bạn có thể bỏ qua khóa mảng. Ví dụ,
// liệt kê rõ ràng các trường, sử dụng tốt nhất khi bạn nắm được các thay đổi
// trong bản CSDL hoặc các thuộc tính của model, không gây ra sự thay đổi của trường (để giữ tương thích với API).
public function fields()
{
return [
// tên trường giống với tên thuộc tính
'id',
// tên trường là "email", tương ứng với tên thuộc tính là "email_address"
'email' => 'email_address',
// tên trường là "name", giá trị được định nghĩa bởi hàm
'name' => function () {
return $this->first_name . ' ' . $this->last_name;
},
];
}
// lọc ra một số trường, nên sử dụng khi bạn muốn kế thừa các trường
// thêm vào blacklist một số trường không cần thiết.
public function fields()
{
$fields = parent::fields();
// remove fields that contain sensitive information
unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);
return $fields;
}
Cảnh báo: Bởi vì theo mặc định tất cả các thuộc tính của model sẽ liệt kê trong mảng trích xuất, bạn nên kiểm tra dữ liệu của bạn để chắc chắn rằng chúng không chứa các thông tin không cần thiết. Nếu có thông tin như vậy, bạn nên ghi đè phương thức
fields()
để lọc chúng ra. Tại ví dụ trên, chúng ta chọn các trường để lọc ra làauth_key
,password_hash
vàpassword_reset_token
.
Các model là phần trung tâm đại diện cho tầng dữ liệu, chứa các quy tắc và logic. Model thường được tái sử dụng tại một số nơi khác nhau. Với một ứng dụng được thiết kế tốt, thông thường các model được chú trọng hơn controllers.
Tổng hợp mục, models
Bạn cần có sự xem xét các đề nghị trên mỗi khi bạn triển khai hệ thống lớn và phức tạp. Trong các hệ thống này, cácmodel cần được chú trọng bởi vì chúng được sử dụng ở nhiều nơi và có thể chứa nhiều các quy tắc và các xử lý nghiệp vụ. Điều này có sự ảnh hưởng tại tiến trình bảo trì mỗi thay đổi mã của bạn có thể ảnh hưởng tới nhiều vị trí khác nhau. Để mã code của bạn dễ được bảo trì hơn, bạn có thể được thực hiện các chiến lược sau:
Ví dụ, với Mẫu dự án Advanced, bạn có thể định nghĩa lớp cơ sở model
là common\models\Post
. Tiếp đến tại ứng dụng front end, bạn định nghĩa lớp khung là
frontend\models\Post
lớp này kế thừa từ lớp common\models\Post
. Và tương tự cho ứng dụng back end,
bạn định nghĩa model backend\models\Post
. Với cách giải quyết này, bạn sẽ chắc chắn rằng mã của bạn tại model frontend\models\Post
chỉ dùng cho ứng dụng front end, và nếu bạn thực hiện với bất kỳ thay đổi nào, bạn không cần lo lắng về
việc thay đổi này có ảnh hưởng tới ứng dụng back end.
Found a typo or you think this page needs improvement?
Edit it on github !
Signup or Login in order to comment.