[CakePHP] Model : Truy xuất, lưu và xóa dữ liệu.
Khi tôi tìm hiểu về CakePHP và viết một ứng dụng web đơn giản để làm quen, tôi vẫn dùng cách truyền thống là dùng query trực tiếp để thao tác với cơ sở dữ liệu. Do lúc đó có ít thời gian để đọc tài liệu tôi vẫn chưa hiểu lắm và áp dụng được những hàm dựng sẵn trong Model mà CakePHP cung cấp. Sau ...
Khi tôi tìm hiểu về CakePHP và viết một ứng dụng web đơn giản để làm quen, tôi vẫn dùng cách truyền thống là dùng query trực tiếp để thao tác với cơ sở dữ liệu. Do lúc đó có ít thời gian để đọc tài liệu tôi vẫn chưa hiểu lắm và áp dụng được những hàm dựng sẵn trong Model mà CakePHP cung cấp. Sau khi tìm hiểu tôi thấy chúng thật đa dạng và thực sự sẽ rất hữu ích trong việc phát triển web, chúng giúp ta tiết kiệm thời gian hơn, code nhìn trong sáng dễ đọc hơn và đồng thời cũng là tận dụng được tối đa sức mạnh mà CakePHP đã dựng sẵn cho chúng ta trong đa số các trường hợp thường gặp. Ví dụ như khi bạn tuân thủ quy ước đặt tên thì CakePHP sẽ ngầm hiểu mà không cần bạn phải quan tâm xem sẽ xử lý như nào.
Trong bài này, tôi xin trình bày về việc truy xuất, lưu và xóa dữ liệu trong CakePHP, phần cuối sẽ là bổ sung một chút về Behavior.
1) Truy xuất dữ liệu trong CakePHP
Trong phiên bản CakePHP 2.x, việc truy xuất dữ liệu từ DB ra về cơ bản sẽ được thực hiện bởi hàm find() với format như sau :
find(string $type = type_name, array $params = array())
Trong đó biến type_ có thể nhận các giá trị built - in của CakePHP _là ‘all’, ‘first’, ‘count’, ‘list’, ‘neighbors’, và ‘threaded’, _mặc định sẽ là_ ‘first’_. Ngoài ra, CakePHP còn hỗ trợ để có thể custom _type theo ý của mình. Nhưng có một chú ý là tất cả các từ khóa này đều phân biệt hoa thường, nên nếu viết ‘All’ thay vì ‘all” thì sẽ sai và không chạy được. Chúng ta sẽ cùng tìm hiểu chi tiết hơn các từ khóa này :
- all : nếu từ khóa này được dùng thì kết quả lấy dữ liệu sẽ trả về tất cả các bản ghi dưới dạng mảng.
- first : ngay cái tên của nó đã nói lên rằng nó match được bản ghi đầu tiên thì sẽ trả về và không lấy ra bản ghi tiếp theo.
- count : đơn giản kết quả trả về sẽ là tổng số bản ghi mà hàm find() đếm được.
- list : sẽ trả về một mảng danh sách các bản ghi được index và nó thường hữu ích khi dùng với các chỗ dạng list, như dropdown list chẳng hạn. - neighbors : tương tự như 'first' nhưng kết quả sẽ trả về bản ghi trước và sau.
- threaded : khi dùng từ khóa này thì kết quả sẽ trả về là một mảng nested, và thường được dùng để dựng một kết quả nested với parent_id.
Kế đến là biến _params_, biến này có thể có hoặc không và nó nhận vào một mảng, với tất cả key cũng là optional. Biến params mặc định sẽ có các key sau : ‘conditions’ : sẽ chứa một mảng các điều kiện sẽ dùng để find, mặc định là null. ‘recursive’ : con số đệ quy trong CakePHP, mặc định là 1. ‘fields’ : sẽ là một mảng các trường trả về, mặc định là null. ‘order’ : giống như order trong query thường và nó nhận một mảng giá trị, mặc định là null. ‘group’ : một mảng các trường sẽ group by, mặc định là null. ‘limit’ : giới hạn bản ghi trả về, mặc định là null. ‘page’ : số page, được dùng với những data phân trang, mặc định là null. ‘offset’ : offset giống như trong query thường, mặc định là null. ‘callbacks’ : có thể nhận các giá trị true, false, ‘before’ và ‘after’. Mặc định là true, tức là callback method sẽ được gọi khi dùng find(). Nếu set thành false sẽ hủy việc gọi này. Trường hợp dùng ‘before’ sẽ gọi before callback, ngược lại sẽ gọi after callback khi dùng ‘after’.
Có thể xem ví dụ bên dưới với giải thích đi kèm để rõ hơn :
array(
'conditions' => array('Model.field' => $thisValue), //mảng các điều kiện
'recursive' => 1, //int
//mảng tên các trường
'fields' => array('Model.field1', 'DISTINCT Model.field2'),
//chuỗi hoặc mảng định nghĩa thứ tự order
'order' => array('Model.created', 'Model.field3 DESC'),
'group' => array('Model.field'), //mảng các trường sẽ GROUP BY
'limit' => n, //int
'page' => n, //int
'offset' => n, //int
'callbacks' => true //có thể nhận giá trị khác là false, 'before', 'after'
)
Đối với type_ thì chúng ta có thể tạo ra theo ý của mình để phục vụ mục đích riêng. Bước đầu tiên là cần phải khai báo _type_ cho biến Model_::findMethods, ví dụ :
class Article extends AppModel {
public $findMethods = array('available' =>true);
}
Lúc này $type = ‘avaiable’ đã có nên việc tiếp theo là thực thi fucntion có tên findAvaiable, trong function này người dùng sẽ định nghĩa riêng các nhu cầu của họ để find, dưới đây là một hàm ví dụ đơn giản :
protected function _findAvailable($state, $query, $results = array()) {
if ($state === 'before') {
$query['conditions']['Article.published'] = true;
return $query;
}
return $results;
}
Đến đây, khi dùng find() ta sử dụng ‘avaiable’ giống như các từ khóa khác :
public function index() {
$articles = $this->Article->find('available', array(
'order' => array('created' => 'desc')
));
}
Chúng ta giờ đã hiểu cơ bản về cách dùng hai biến type_ và _params, và điều mà ta sẽ quan tâm kế tiếp là nếu đã lấy được dữ liệu vậy chúng sẽ có dạng như thế nào? Kết quả mà hàm find() trả về đơn thuần là một mảng có format cơ bản như bên dưới, và sẽ bảo gồm dữ liệu của model chính và cả dữ liệu của model có quan hệ với nó.
Array
(
[ModelName] => Array
(
[id] => 83
[field1] => value1
[field2] => value2
)
[AssociatedModelName] => Array
(
[id] => 1
[field1] => value1
[field2] => value2
)
)
Ngoài sử dụng hàm find() như trên, CakePHP còn cung cấp cho chúng ta hai lựa chọn truy xuất dữ liệu khác, khá là mềm dẻo trong việc thao tác với DB, đó là :
**a) **Hàm findAllBy()_ _: Hàm này sẽ trả về kết quả giống như find(‘all’) nhưng nó sẽ find dữ liệu theo tên trường được chỉ định.
findAllBy<fieldName>(string $value, array $fields, array $order, int $limit, int $page, int $recursive)
**b) **Hàm findBy() : Hàm này trả về duy nhất một bản ghi giống find(‘first’) nhưng khác ở chỗ nó chỉ định find theo tên một trường nào đó.
findBy(string $value[, mixed $fields[, mixed $order]]);
Cuối cùng, để truy xuất dữ liệu theo cách truyền thống mà không dùng hàm dựng sẵn của CakePHP. Bạn hoàn toàn có thể dùng hàm query(string $query) để thực thi câu lệnh SQL thuần, hay hàm field() để lấy giá trị của một trường dữ liệu :
query(string $query)
field(string $fieldName, array $conditions = null, string $order = null)
2) Lưu, cập nhật dữ liệu với CakePHP
Ở phần trước chúng ta đã tìm hiểu cách để truy xuất dữ liệu với CakePHP, nên trong phần này chúng ta sẽ tiếp tục cùng tìm hiểu khái quát cách lưu trữ dữ liệu vào DB trước khi chúng được lấy ra để sử dụng. CakePHP cung cấp hàm save() cho công việc này. Dữ liệu mà đưa vào lưu trữ khi dùng hàm save() sẽ có dạng đơn giản như bên dưới :
Array
(
[ModelName] => Array
(
[fieldname1] => 'value'
[fieldname2] => 'value'
)
)
Bình thường nếu bạn dùng FormHelper của CakePHP thì bạn không cần phải lo lắng khi dùng _save(), _bởi vì khi đó dữ liệu cần lưu sẽ được gán vào biến _$this->request->data _theo format trên. Do đó chỉ bằng một dòng lệnh như dưới dữ liệu của bạn đã được lưu vào DB :
$this->Recipe->save($this->request->data)
Khi hàm save() được gọi nó sẽ validate những dữ liệu có trong tham số đầu tiên, và bạn có thể dùng biến Model::$validationError để debug nếu validate có lỗi.
if ($this->Recipe->save($this->request->data)) {
// code xử lý ki validate thành công, không có lỗi
}
debug($this->Recipe->validationErrors);
Kế đến chúng ta sẽ tìm hiểu một số hàm hữu ích có liên quan khi lưu dữ liệu vào DB. **a) **Hàm set(one,one, one,two = null) Hàm này có thể dùng để set một hay nhiều trường dữ liệu vào mảng dữ liệu bên trong model. Và bạn cũng có thể dùng nó để gán những giá trị mới tới nhiều trường. Chúng ta có thể xem ví dụ bên dưới để rõ hơn cách dùng của set()
$this->Post->read(null, 1); // đọc
//dùng set() để set giá trị cho các trường
$this->Post->set(array(
'title' => 'New title',
'published' => false
));
$this->Post->save(); // lưu vào DB
**b) **Hàm clear() Đây là hàm rất hữu ích khi chúng ta muốn reset trạng thái của model, nó sẽ xóa tất cả những dữ liệu chưa được save hay các lỗi khi validate.
**c) **Hàm save(array data=null,booleandata = null, boolean data=null,booleanvalidate = true, array fieldList = array())_ Hàm này sẽ lưu trữ dữ liệu được format theo dạng mảng. Nếu bạn không muốn validate dữ liệu thì có thể set _validate = false, và để hạn chế trường được lưu bằng mảng fieldList._ Ngoài ra, bạn có thể sử dụng hàm này theo cú pháp _save__(__array__ __data = null,_ array __params__ __=__ __array__()), trong đó biến params có thể có các key sau :
- validate : set nó bằng true/false để có/không validate dữ liệu.
- fieldList : mảng các trường sẽ là đối tượng lưu
- _callback _: nếu set false sẽ bỏ callback, còn dùng before hay after sẽ gọi các hàm callbacks trước hoặc sau.
- counterCache : là giá trị boolean điều khiển việc update bộ đếm cache
☆ Nếu bạn không muốn cập nhật trường modified một cách tự động khi dùng hàm này thì có thể set modfied => false trong mảng _data_. Khi lưu mới dữ liệu thành công thì ID cho bản ghi được lưu vào DB sẽ tự động lưu giữ trong thuộc tính id, ví dụ : _this__->__Post__->id_ . Do vậy mà việc tạo mới hay cập nhật sẽ phụ thuộc vào id có được set hay không? Nếu nó được set thì là cập nhật dữ liệu đã có, còn nếu không sẽ tạ ra một bản ghi mới trong DB.
**d) **Hàm_ create(array data = array())_ Hàm này gần giống như _clear()_, nó sẽ reset trạng thái của model để lưu mới dữ liệu. Nó không giống như cái tên create, nó sẽ không thực sự tạo bản ghi trong DB mà đơn thuần là clear trạng thái của model và set _data_ để lưu chúng vào DB.
☆ Nếu $data được đưa vào thì nó sẽ merge với các trường trong DB và sẵn sàng để lưu với dữ liệu được đưa vào đó. Chú ý là nếu false hay null được đưa vào thì dữ liệu sẽ là 1 mảng trắng.
**e) **Hàm saveField(string fieldName,stringfieldName, string fieldName,stringfieldValue, $validate = false Sẽ được dùng để lưu một trường đơn lẻ. Nhưng bạn cần chú ý là cần cho biết ID của bản ghi trước khi gọi hàm này, ví dụ :
$this->Post->id = $id
Giống với save() ở trên thì saveField() cũng có một cú pháp khác _saveField(string fieldName,stringfieldName, string fieldName,stringfieldValue, array params = array()), _trong đó mảng_ params có thể nhận các key sau :
- validate : set nó bằng true/false để có/không validate dữ liệu.
- _callback _: nếu set false sẽ bỏ callback, còn dùng before hay after sẽ gọi các hàm callbacks trước hoặc sau.
- counterCache : là giá trị boolean điều khiển việc update bộ đếm cache
**f) **Hàm updateAll(array fields,mixedfields, mixed fields,mixedconditions) Hàm này sẽ cập nhật tất cả các trường có trong mảng fields_ theo điều kiện được chỉ ra trong mảng _conditions. Số record được cập nhật có thể là một hoặc nhiều tùy thuộc vào tham số thứ 2, nếu nó không được đưa vào hoặc set là true thì tất cả bản ghi sẽ được update. Chúng ta có thể xem ví dụ bên dưới, khi thực thi thì nó sẽ đóng tất cả ticket của khách hàng có ID là 453 :
$this->Ticket->updateAll(
array('Ticket.status' => "'closed'"),
array('Ticket.customer_id' => 453)
);
**g) **Hàm saveMany(array data=null,arraydata = null, array data=null,arrayoptions = array()) Đây là hàm dùng để lưu nhiều dòng dữ liệu của cùng model một lúc, dưới đây là các lựa chọn có thể dùng :
- validate : nếu bạn set là false thì sẽ vô hiệu hóa validation, nếu true thì sẽ tiến hành validate từng dữ liệu trước khi lưu, còn set là first thì validate tất cả dữ liệu trước khi lưu và đây là giá trị mặc định.
- _atomic _: mặc định là true, và việc lưu sẽ được thực hiên trên 1 transaction. Nếu DB hay bảng không hỗ trợ transaction thì sẽ set false.
- _fieldList _: tương tự như hàm save()
- _deep _: đây là option nếu set là true thì sẽ lưu cả các model liên kết
- _callbacks _: giống hàm save()
- counterCache : giống hàm save()
Ngoài những hàm ở trên thì cakePHP còn một số hàm khác liên quan đến việc lưu của các model liên kết, cũng như việc lưu dữ liệu khi chúng có các quan hệ như_ hasOne, hasMany, belongsTo. Nhưng chúng ta sẽ bàn đến sau khi kết thúc tìm hiểu về mối liên kết các model trong phần Linking Models Together.
3) Xóa dữ liệu
Cuối cùng trong phần tìm hiểu về thao tác dữ liệu trong CakePHP, chúng ta sẽ cùng tìm hiểu cách xóa dữ liệu. Phần này CakePHP cung cấp hai hàm để thực hiện xóa dữ liệu.
Thứ nhất, hàm delete(integer id=null,booleanid = null, boolean id=null,booleancascade = true).
Hàm này sẽ xóa bản ghi dựa theo ID được đưa vào, và mặc định nó sẽ xóa cả bản ghi có quan hệ cascade với bản ghi chỉ định. Ví dụ khi ta xóa một Post thì những Comment của post đó cũng sẽ bị xóa theo.
Thứ hai và cũng là cuối cùng, hàm deleteAll(mixed conditions,conditions, conditions,cascade = true, $callbacks = false).
Về cơ bản là nó giống hàm delete() nhưng điểm khác là nó sẽ xóa tất cả bản ghi mà thỏa điều kiện được đưa vào. Trong đó :
- conditions : là những điều kiện
- cascade : nếu true thì sẽ xóa cả những bản ghi có quan hệ cascade với bản ghi bị xóa.
- callbacks : nếu là true sẽ gọi callback, còn false sẽ vô hiệu hóa callbacks.
To be continued ...