[CakePHP] Model : Validation & Behavior.
Tiếp tục phần trước, tôi xin trình bày về Validation và Behavior trong CakePHP. 4) Validate dữ liệu Như phần trước đã đề cập đến validate_ thì trước khi dữ liệu được lưu xuống DB thì chúng sẽ được kiểm tra xem có gì bất thường hay không, và đảm nhiệm an toàn cho DB thì CakePHP trang bị cho ...
Tiếp tục phần trước, tôi xin trình bày về Validation và Behavior trong CakePHP.
4) Validate dữ liệu
Như phần trước đã đề cập đến validate_ thì trước khi dữ liệu được lưu xuống DB thì chúng sẽ được kiểm tra xem có gì bất thường hay không, và đảm nhiệm an toàn cho DB thì CakePHP trang bị cho chúng ta một loạt các options dựng sẵn để thực hiện việc này. Biến _validate này sẽ được định nghĩa trong Model.
Việc đầu tiên chúng ta nghĩ tới đó là đặt các luật lệ cho việc validate. Chúng ta cùng xem xét một ví dụ :
class User extends AppModel {
public $validate = array(
'login' => 'alphaNumeric', // có thể là kí tự alphabe hay số
'email' => 'email', // cần phải có format của email
'born' => 'date' // dữ liệu ngày tháng
);
}
Ở ví dụ trên chúng ta đã dùng Simple Rules, tức là luật đơn giản theo format ‘tên trường dữ liệu’ => ‘tên rule được định nghĩa sẵn bởi CakePHP’. Ngoài ra, CakePHP còn có một số cách định nghĩa luật lệ cho các trường dữ liệu.
** - Một luật một trường ** : nếu dùng cách này thì chúng ta sẽ điều khiển tốt hơn việc các luật lệ sẽ làm việc như nào. Hãy xem ví dụ sau :
public $validate = array(
'fieldName1' => array(
// hoặc dạng array('ruleName', 'param1', 'param2' ...)
'rule' => 'ruleName',
'required' => true,
'allowEmpty' => false,
// hoặc giá trị 'update'
'on' => 'create',
'message' => ‘Message lỗi ở đây'
)
);
** Chúng ta sẽ đi tìm hiểu lần lượt các key bên trên. **
- rule : là key định nghĩa phương thức validate và có thể là một giá trị hay một mảng. Như với luật lệ chỉ được nhập kí tự alphabe và số thì sẽ là một giá trị đơn ('rule' => 'alphaNumeric'), còn nếu giá trị cần nhiều tham số thì sẽ là một mảng ('rule' => array('minLength', 8)).
- required : key này có thể nhận vào giá trị boolean, chuỗi create hay update. Nếu giá trị set là true thì sẽ luôn bắt buộc, nếu set create thì chỉ bắt buộc khi tạo mới, cuối cùng update sẽ có hiệu lực khi cập nhật dữ liệu.
- _allowEmpty _:nếu set key với giá trị _false _thì trường được áp luật lệ này không được empty ( thực chất nó được định nghĩa bằng logic _!empty(value)__ __||__ __is_numeric(value)). _Có vẻ giống với required nhưng có điểm khác biệt là required dùng hàm isset để check.
- _on _: key này có thể set là create hay update để điều khiển việc luật lệ sẽ được thực hiện khi tạo mới hay chỉ khi cập nhật dữ liệu.
- _message _: đây là key cho phép set câu thông báo lỗi khi validation bị failed.
- Nhiều luật một trường : về cơ chế hoạt động thì kỹ thuật này cũng tương tự như “một luật một trường” nhưng nó sẽ mềm dẻo hơn ở chỗ cho phép áp dụng nhiều luật lệ validation trên một trường dữ liệu. Điểm khác biệt nữa thể hiện ở chỗ có thêm key là last :
- last : key này cho phép xác định xem luật lệ nào được chạy cuối cùng, ta cùng xem code ví dụ sau :
public $validate = array(
'login' => array(
'rule1' => array(
'rule' => 'alphaNumeric',
'message' => 'Only alphabets and numbers allowed',
'last' => false
),
'rule2' => array(
'rule' => array('minLength', 8),
'message' => 'Minimum length of 8 characters'
)
)
);
Đoạn code này sẽ chạy validate từ rule1 đến rule2, vì do ở rule1 last được set false nên dù rule1 có validate thất bại thì rule2 vẫn được tiến hành. Chính vì thế để dừng validate khi rule1 thất bại thì cần set _last => true, _CakePHP sẽ hiểu rằng đến đó là validate cuối cùng rồi và dừng xử lý.
Và đến đây, nếu bạn chưa thỏa mãn với các luật sẵn có mà CakePHP cũng cấp thì bạn hoàn toàn có thể định nghĩa riên các rule cho ứng dụng của mình, sẽ có hai cách thực hiện điều này.
- Dùng Regular Expression : Chúng ta hãy cùng xem xét ví dụ dưới để hiểu hơn khi áp dụng Regular Expression vào việc validate dữ liệu.
public $validate = array(
'login' => array(
'rule' => '/^[a-z0-9]{3,}$/i',
'message' => 'Only letters and integers, min 3 characters'
)
);
- Tạo riêng hàm validate : ví dụ khi bạn muốn validate việc mã khuyến mại đã được dùng quá 25 lần thì báo lỗi, CakePHP không thể làm được cho bạn với những cái có sẵn, nên bạn cần custom riêng cho mình một hàm để làm điều đó. Chúng ta cùng xem xét code bên dưới :
class User extends AppModel {
public $validate = array(
'promotion_code' => array(
'rule' => array('limitDuplicates', 25),
'message' => 'Mã này đã được sử dụng quá nhiều lần.'
)
);
public function limitDuplicates($check, $limit) {
// $check sẽ có giá trị : array('promotion_code' =>’ giá trị cụ thể’)
// $limit sẽ có giá trị: 25
$existingPromoCount = $this->find('count', array(
'conditions' => $check,
'recursive' => -1
));
return $existingPromoCount < $limit;
}
Ở ví dụ trên bạn sẽ thấy rằng phải tạo một hàm public để xử lý việc validate và cách gọi thì không khác với các key sẵn có của CakePHP. Thêm nữa thì bạn có thể để hàm validate của mình ở Model hoặc Behavior, vì các hàm ở Model/behavior sẽ được kiểm tra trước khi tìm đến lớp _validation. _Do vậy, bạn có thể override những hàm validate sẵn có của CakePHP. Điều cuối cùng bạn cần quan tâm là kết quả trả về của hàm custom của bạn : Nếu giá trị đúng thì trả về true, ngược lại hãy trả về false. Và nếu là một chuỗi thì đó được coi như một thông báo lỗi khi validate thất bại.
Như vậy, đến đây bạn đã có cái nhìn sơ lược về cách sử dụng $validate cũng như cách tạo riêng validate cho mình. Câu hỏi đặt ra là bạn có muốn các luật lệ mình dùng có thể thêm vào hay loại bỏ và chỉnh sửa luật lệ không? Và để làm điều này CakePHP cung cấp đối tượng ModelValidator. Chúng ta sẽ cùng tìm hiểu cách nó hoạt động :
- Thêm vào luật lệ mới : bạn có thể dùng hàm add() như sau, code bên dưới có nghĩa bạn sẽ thêm vào luật notEmpty đối với trường password :
$this->validator()->add('password', 'required', array(
'rule' => 'notEmpty',
'required' => 'create'
));
Đặc biệt hơn là bạn có thể dùng nhiều hàm add() trong đoạn code bên trên để thêm nhiều luật lệ cho trường password.
- Chỉnh sửa luật hiện có : CakePHP cung cấp cho bạn một số hàm để có thể thay đổi, mở rộng hay bỏ luật lệ nào đó. Như với trường hợp trên, bạn muốn thay đổi luật lệ từ notEmpty sang required và thay vì chỉ lúc create sẽ là validate mọi lúc.
$this->validator()->getField('password')->setRule('required', array(
'rule' => 'required', // thay đổi từ notEmpty sang required
'required' => true // thay đổi từ create sang true
));
Còn nếu bạn chỉ muốn thay đổi một thuộc tính của luật lệ thì bạn có thể dùng tên thuộc tính một cách trực tiếp như ví dụ sau :
$this->validator()->getField('password')
->getRule('required')->message = 'Trường này không được trống';
- Xóa bỏ luật lệ : CakePHP cung cấp cho chúng ta hàm remove() để làm điều này, bạn có thể xóa tất cả hoặc chỉ một số luật chỉ định :
// Xóa bỏ tất cả luật lệ cho trường username
$this->validator()->remove('username');
// Chỉ bỏ đi luật 'required' khỏi trường password
$this->validator()->remove('password', 'required');
Phần cuối cùng tôi muốn đề cập đến trong chủ đề validation là các hàm mà CakePHP đã trang bị sẵn cho chúng ta sử dụng, vì phần này khá dài và cũng dễ hiểu với tên gọi của chúng nên tôi chỉ list ra chức năng cơ bản của chúng :
- alphaNumeric(mixed $check) : chỉ cho phép chữ và số
- between_(_string check_, _integer min, integer $max) : thỏa mãn trong khoảng
- boolean_(_string $check) : chỉ được là giá trị boolean
- cc_(_mixed check_, _mixed type = 'fast', boolean deep = false_, _string regex = null) : check credit card
- comparison_(_mixed check1_, _string operator = null, integer $check2 = null) : so sánh 2 giá trị số
- custom_(_mixed check_, _string regex = null) : được dùng khi cần đến một Regular Expression
- date(
- string check_, _mixed format = 'ymd', string $regex = null) : kiểm tra ngày tháng năm
- datetime(array check_, _mixed dateFormat = 'ymd', string $regex = null) : kiểm tra ngày giờ
- decimal_(_string check_, _integer places = null, string $regex = null) : check xem có thỏa mãn giá trị thập phân không
- email_(_string check_, _boolean deep = false, string $regex = null) : check xem có phải là email không
- equalTo_(_mixed check_, _mixed compareTo) : so sánh hai chuỗi
- extension_(_mixed check_, _array extensions = array('gif', 'jpeg', 'png', 'jpg')) : kiểm tra đuôi mở rộng
- fileSize(check_, _operator = null, $size = null) : check kích cỡ của file
- inList_(_string check_, _array list, boolean $caseInsensitive = false): check có nằm trong list hay không
- ip_(_string check_, _string type = 'both'): check xem có phải IP không
- isUnique() : kiểm tra giá trọ có phải đơn nhất
- maxLength_(_string check_, _integer max) : check số kí tự tối đa
- mimeType_(_mixed check_, _array|string mimeTypes) : kiểm tra format của mime
- minLength_(_string check_, _integer min):check độ số kí tự tối thiểu
- multiple_(_mixed check_, _mixed options = array(), boolean $caseInsensitive = false): check với trường hợp có thể chọn nhiều item
- notEmpty_(_mixed $check): đảm bảo trường dữ liệu không là 0 hoặc chuỗi trống
- numeric_(_string $check) : đảm bảo giá trị là số
- naturalNumber_(_mixed check_, _boolean allowZero = false) : check số tự nhiên
- range_(_string check_, _integer lower = null, integer $upper = null): kiểm tra giá trị có trong khoảng giá trị
- url_(_string check_, _boolean strict = false): có đúng format của URL không
- .v..v.
5) Behavior
Trong CakePHP, khái niệm Behavior mang ý nghĩa là một cách tổ chức những hàm chức năng được định nghĩa trong Model. Nó cho phép chúng ta tách và tái sử dụng logic mà tạo nên loại behavior, thêm nữa nó không bắt buộc sự kế thừa. Một ví dụ là bạn xử lý những dữ liệu được lưu dạng cây thì bạn nên dùng TreeBehavior, bởi nó đã bao gồm các hàm thông dụng nhất dùng để thao tác với tree. Do đó bạn không cần phải tốn công sức viết ra các hàm để xử lý nữa. CakePHP cung cấp cho chúng ta bốn loại behavior là ACL, Containable, Translate, Tree, nhưng trong bài này tôi sẽ chỉ đề cập đến cách sử dụng chung và cách tạo ra behavior cũng như phương thức trong nó. Phần chi tiết của từng loại tôi sẽ đề cập đến ở các phần sau.
** 5.1) Sử dụng Behaviors **
Thông qua biến actAs thì các behavior sẽ được attach vào Model của bạn. Bạn có thể khai báo đơn giản như _public actsAs array('Tree')_; câu lệnh này sẽ báo cho CakePHP là sẽ sử dụng tới TreeBehavior. Một số behaviors có thể bắt buộc hoặc cho phép có những settings khi attach vào Model. Ví dụ :
public $actsAs = array('Tree' => array(
'left' => 'left_node',
'right' => 'right_node'
),
‘Translate’
);
Như ví dụ trên thì đã có setting cho TreeBehavior biết tên của trường ‘left’ và ‘right’ lần lượt là ‘left_node’ và 'right_node'. Trong khai báo biến actAs_ thì bạn cũng hoàn toàn có thể khai báo thêm behavior như đoạn lệnh trên. Một khi behavior đã được attach vào Model thì nó sẽ có hiệu lực trong suốt lifetime của Model, nhưng nếu bạn lúc nào đó bỏ nó đi thì hoàn toàn có thể, ví dụ : _this->Category->Behaviors->unload('Translate'); Nhưng trong thực tế chúng ta thường sẽ vô hiệu hóa việc behavior handle những callbacks của Model :
$this->Category->Behaviors->disable('Translate');
Và khi bạn cần đến thì hãy enable lên :
$this->Category->Behaviors->enable('Translate');
Trong một số trường hợp bạn sẽ cần attach mới một behavior nào đó bằng hàm load() :
$this->Category->Behaviors->load(‘SomeBehavior’);
Một khả năng nữa của hàm load() là override setting của một behavior nào đó mà đã được attach vào Model, ta sẽ thử ghi đè lên tên trường ‘left’ đã khai báo ở trên :
`$this->Category->Behaviors->load('Tree', array('left' => 'new_left_node'));
Cuối cùng, CakePHP cho phép xem danh sách những behavior nào đang được attach vào Model rồi bằng hàm loaded()
$behaviors = $this->Category->Behaviors->loaded();
** 5.2) Tạo ra Behavior **
Khi behavior được attach vào Model thì callbacks sẽ được gọi một cách tự động. Những callbacks tương tự như trong Models : beforeFind, afterFind, beforeValidate, afterValidate, beforeSave, afterSave, beforeDelete, afterDelete và onError. Behavior nên được đặt trong thư mục app/Model/Behavior. Và tên thì sẽ đặt dạng CamelCase với hậu tố là Behavior, ví dụ như NameBehavior.php
Khi mà behaviors đã được share trong tất cả instances của Model mà đang dùng đến chúng, thì nên lưu trữ settings trên alias/model mà đang sử dụng behavior. Và khi tạo behaviors thì hàm setup() sẽ được gọi.
public function setup(Model $Model, $settings = array()) {
if (!isset($this->settings[$Model->alias])) {
$this->settings[$Model->alias] = array(
'option1_key' => 'option1_default_value',
'option2_key' => 'option2_default_value',
'option3_key' => 'option3_default_value',
);
}
$this->settings[$Model->alias] = array_merge(
$this->settings[$Model->alias], (array)$settings);
}
** 5.3) Tạo phương thức của behavior **
Phương thức của behavior được available tới bất kì một model nào làm việc với behavior. Ví dụ chúng ta có :
class Duck extends AppModel {
public $actsAs = array('Flying');
}
Và bạn sẽ có thể gọi FlyingBehavior hệt như chúng là phương thức trong Model của bạn : $this->Duck->fly('hanoi','tphcm'); Như bạn thấy phương thức này có 2 tham số truyền vào nhưng thực tế là có 3, dạng như đoạn bên dưới :
public function fly(Model $Model, $from, $to) {
// code thực hiện điều gì đó ở đây.
}
Cuối cùng trong phần này chúng ta sẽ đi qua một lượt những callbacks trong behavior. Chúng sẽ được trigger trước khi mà những callbacks của Model được gọi:
- setup_(_Model Model_, _array settings = array()):được gọi kh một behavior được attach vào model
- cleanup_(_Model $Model): dùng khi detach behavior khỏi model
- beforeFind_(_Model Model_, _array query): nếu mà trả về false thì sẽ không thực hiện hàm find()
- afterFind_(_Model Model_, _mixed results, boolean $primary = false):có thể dùng để bổ sung kết quả find()
- beforeValidate_(_Model Model_, _array options = array()):có ích khi muốn modify validate của model hay handle những logic nào đó trước khi validate.
- afterValidate(Model $Model):có thể dùng để thực hiên cleanup hay chuẩn bị gì đó khi cần
- beforeSave(Model Model_, _array options = array()):có thể trả về true để tiếp tục việc lưu dữ liệu hay trả về false để bỏ việc lưu trữ
- afterSave(Model Model_, _boolean created, array $options = array()):có thể đùng để thực hiện cleanup sau khi lưu trữ thành công
- beforeDelete(Model Model_, _boolean cascade = true): có thể trả về true để tiếp tục việc lưu dữ liệu hay trả về false để bỏ việc lưu trữ
- afterDelete(Model $Model): có thể đùng để thực hiện cleanup sau khi xóa thành công
Đến đây, chúng ta đã cùng xem qua một lượt những tính năng mà CakePHP cung cấp, từ việc trích xuất dữ liệu ra, rồi validate dữ liệu trước khi chúng được lưu vào DB và cuối cùng là khái niệm cũng như cách sử dụng của Behavior . Trong lần này, chỉ là những kiến thức sơ lược, trong những phần kế tiếp chúng ta sẽ tìm hiểu sâu hơn cũng như tìm hiểu cả những cái mới như về Linking Model, FormHelper …
To be continued ...