Truy vấn dữ liệu trong CakePHP

Khi làm việc trong CakePHP, chúng ta phải thao tác với cơ sở dữ liệu (CSDL) như lấy dữ liệu ra để xử lý, so sánh hoặc thao tác, CakePHP hỗ trợ chúng ta 2 cách để truy vấn dữ liệu chính, đó là:

  •  Truy vấn dữ liệu tự động thông qua các hàm viết sẵn của CakePHP
  •  Truy vấn dữ liệu từ câu lệnh query trực tiếp

Chúng ta có thể sử dụng câu truy vấn (query) để truy vấn dữ liệu trực tiếp hoặc có thể sử dụng cách truy vấn dữ liệu tự động trong CakePHP. Mỗi cách có ưu điểm và nhược điểm khác nhau, đòi hỏi chúng ta phải có kiến thức về CakePHP và hiểu rõ chúng ta đang muốn làm gì để có kết quả tốt nhất.

1. Truy vấn dữ liệu tự động:

Khi truy vấn dữ liệu tự động ta thường dùng hàm find

a. Cú pháp:

$this->Model->find($type,$params);

Trong đó:

$type là phương thức truy vấn dữ liệu, có thể là 'all', 'first', 'count', 'list', 'neighbors' hoặc 'threaded', giá trị mặc định là 'first' 

$params là mảng tập hợp các điều kiện lấy dữ liệu, gom cụm dữ liệu, sắp xếp hoặc thể hiện dữ liệu. 

array(
    'conditions' => array('Model.field' => $thisValue), //mang cac dieu kien tim kiem
    'recursive' => 1, //la mot gia tri so nguyen, co the la -1, 0, 1 hoac 2
    'fields' => array('Model.field1', 'DISTINCT Model.field2'), //mang cac cot du lieu muon lay ra
    'order' => array('Model.created', 'Model.field3 DESC'), //menh de ORDER BY sap xep thu tu ket qua sau khi tim kiem la tang dan (ASC) hoac giam dan (DESC)
    'group' => array('Model.field'), //menh de GROUP BY
    'limit' => n, //so nguyen
    'page' => n, //so nguyen
    'offset'=>n, //so nguyen   
    'callbacks' => true //cac gia tri bao gom true, false, 'before', 'after'
)

b. Ví dụ:

Để rõ hơn CakePHPViet đưa ra ví dụ sau sử dụng bảng users với dữ liệu đơn giản để truy vấn gồm có 3 dòng. 

csdl truy van

  • find('all',$param): trả về tất cả các kết quả được tìm thấy.

Ví dụ: Trong bảng users ta truy vấn

$this->User->find('all') ;

Kết quả sẽ truy vấn tất cả dữ liệu của bảng users:

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [id] => 1
                    [username] => admin
                    [password] => e14afb8d0980244a48da3695d1341bd0a0c1c02c
                    [created] => 2010-08-11 13:51:30
                )

        )

    [1] => Array
        (
            [User] => Array
                (
                    [id] => 2
                    [username] => user1
                    [password] => 2900239a2c8dd5ca4c44fd40816539c5c8fb24dd
                    [created] => 2011-06-27 00:00:00
                )

        )

    [2] => Array
        (
            [User] => Array
                (
                    [id] => 3
                    [username] => user2
                    [password] => de5748fd07e65faa9eb2151a1cba5446f1ef21af
                    [created] => 2011-06-27 20:25:15
                )

        )

)

Có thể giới hạn tìm kiếm bằng các mảng điều kiện của biến $param, ví dụ như sử dụng trường fields để giới hạn số cột dữ liệu truy vấn, hoặc chọn điều kiện để truy vấn bằng trường conditions

Ví dụ: 

$this->User->find('all',array('fields'=>array('User.id','User.username'),
                              'conditions'=>array('User.id'=>2)
		));

 Kết quả:

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [id] => 2
                    [username] => user1
                )
        )
)

 

  • find('first',$param): chỉ trả về một kết quả duy nhất

Ví dụ: Cũng với bảng users, nếu phương thức ta sử dụng là first 

$this->User->find('first');

Thì kết quả là: 

Array
(
    [User] => Array
        (
            [id] => 1
            [username] => admin
            [password] => e14afb8d0980244a48da3695d1341bd0a0c1c02c
            [created] => 2010-08-11 13:51:30
        )

)

Cũng giống như find('all'), các bạn sử dụng các điều kiện để tìm theo ý muốn.

  • find('count',$param): kết quả trả về là một số nguyên 

Ví dụ: 

$this->User->find('count'); 

 Phương thức này sẽ đếm số dòng kết quả tìm thấy, do đó kết quả của câu lệnh trên là 3

  • find('list',$param): kết quả trả về là một mảng danh sách, thường được dùng như input của dropdown list, hay select box.

Ví dụ: 

$this->User->find('list');

Kết quả như sau:

Array
(
    [1] => 1
    [2] => 2
    [3] => 3
)

Thay vì hiển thị kết quả là list các id, ta cũng có thể hiển thị list các username để dễ sử dụng.

Ta sẽ làm như sau:

$this->User->find('list',array('fields'=>array('User.username')));

  Kết quả sẽ là danh sách các username, thay vì id như lúc đầu:

Array
(
    [1] => admin
    [2] => user1
    [3] => user2
)

 

  • find('neighbors',$param): tương tự như find('list'), nhưng trả về là một kết quả đứng trước và một kết quả đứng sau cái đã tìm được

Ví dụ:

$this->User->find('neighbors',array('field'=>'id','value'=>2));

Kết quả

Array
(
    [prev] => Array
        (
            [User] => Array
                (
                    [id] => 1
                    [username] => admin
                    [password] => e14afb8d0980244a48da3695d1341bd0a0c1c02c
                    [created] => 2010-08-11 13:51:30
                )
        )
    [next] => Array
        (
            [User] => Array
                (
                    [id] => 3
                    [username] => user2
                    [password] => de5748fd07e65faa9eb2151a1cba5446f1ef21af
                    [created] => 2011-06-27 20:25:15
                )
        )
)

 

  • find('threaded',$param): trả về kết quả là một mảng lồng nhau, sử dụng để tìm kiếm dữ liệu có cấu trúc hình cây, hoặc cấu trúc đệ quy , trong CakePHP find('threaded') thường được sử dụng với behavior tree.
  • Với truy vấn kiểu này, thì cơ sở dữ liệu phải có thêm trường parent_id

Ví dụ:

$user=$this->User->find('threaded');

Kết quả:

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [id] => 1
                    [username] => admin
                    [password] => e14afb8d0980244a48da3695d1341bd0a0c1c02c
                    [created] => 2010-08-11 13:51:30
                )

            [children] => Array
                (
                )

        )

    [1] => Array
        (
            [User] => Array
                (
                    [id] => 2
                    [username] => user1
                    [password] => 2900239a2c8dd5ca4c44fd40816539c5c8fb24dd
                    [created] => 2011-06-27 00:00:00
                )

            [children] => Array
                (
                )

        )

    [2] => Array
        (
            [User] => Array
                (
                    [id] => 3
                    [username] => user2
                    [password] => de5748fd07e65faa9eb2151a1cba5446f1ef21af
                    [created] => 2011-06-27 20:25:15
                )

            [children] => Array
                (
                )

        )

)

c. Ưu điểm:

- Truy vấn dữ liệu từ các bảng sẽ được thực hiện tự động thông qua khai báo quan hệ ở model.

- Việc thêm quan hệ hoặc thay đổi quan hệ giữa các bảng sẽ được thực hiện ở model, vì vậy khi quan hệ với bảng khác hoặc thay đổi dữ liệu chỉ cần thay đổi quan hệ ở model.

- Dễ dàng hơn trong việc phân trang, phân trang tìm kiếm.

- Dữ liệu lấy ra đã được đưa theo cấu trúc mảng hình cây hợp lý, làm cho việc hiển thị dữ liệu trở lên đơn giản và dễ dàng hơn.

- Và điều quan trọng hơn cả là tránh được lỗi SQL injection khi sử dụng các câu truy vấn để truy vấn dữ liệu. 

d. Nhược điểm:

- Việc truy vấn dữ liệu sẽ trở lên vô cùng nhiều và chậm do truy vấn từ tẩt cả các bảng quan hệ nếu không sử dụng hợp lý recursive và bindModel.

- Dữ liệu lấy ra sẽ được hiển thị theo nhiều định dạng, tùy theo từng mối quan hệ là một – một, một nhiều hay nhiều nhiều. 

e. Cách sử dụng recursive:

Recursive trong CakePHP thể hiện mức tham chiếu quan hệ nông, sâu giữa các bảng với nhau, recursive là một giá trị số nguyên, trong cakephp có 4 mức thể hiện tham chiếu: -1, 0, 1 và 2, giá trị recursive càng lớn thì dữ liệu lấy ra càng nhiều và có mối liên hệ phức tạp với nhau.

- Giá trị -1: chỉ lấy dữ liệu trong bảng hiện tại, không truy vấn thêm bất cứ bảng nào khác liên quan.

- Giá trị 0: sẽ lấy dữ liệu bảng hiện tại, đồng thời truy vấn luôn dữ liệu của các bảng có quan hệ trực tiếp với bảng đó.

- Giá trị 1 và 2 sẽ truy vấn dữ liệu nhiều hơn, dữ liệu sẽ bao gồm tất cả các bảng có quan hệ trực tiếp và cả quan hệ gián tiếp với bảng hiện tại.

Sử dụng giá trị recursive giúp cho việc truy vấn dữ liệu đơn giản và bớt đi số lượng dữ liệu không cần thiết, giúp việc truy vấn nhanh hơn và gọn nhẹ hơn. Thông thường, với một bảng có quá nhiều quan hệ với nhiều bảng khác, ta sẽ sử dụng giá trị recursive bằng -1 để có thể truy vấn chỉ trong bảng đó, điều đó giúp website của chúng ta ổn định, và hoạt động nhanh hơn

2. Truy vấn dữ liệu từ câu lệnh query:

a. Cú pháp:

$this->Model->query('SQL query'); 

b. Ví dụ: 

Trong users_controller.php ta truy vấn dữ liệu ở bảng users, lấy ra user có id=1 như sau: 

$this->User->query('select * from users where id=1');

Kết quả sẽ là: 

Array
(
    [0] => Array
        (
            [users] => Array
                (
                    [id] => 1
                    [username] => admin
                    [password] => e14afb8d0980244a48da3695d1341bd0a0c1c02c
                    [created] => 2010-08-11 13:51:30
                )

        )

)

c. Ưu điểm

- Việc truy vấn dữ liệu sẽ không còn phụ thuộc vào quan hệ ở model, mà chỉ phụ thuộc vào quan hệ khi khai báo ở câu query

- Việc truy vấn dữ liệu sẽ thực hiện nhanh hơn, liên kết giữa các bảng sẽ rõ ràng hơn dựa vào câu lệnh liên kết ở query.

d. Nhược điểm

- Khi thay đổi quan hệ giữa các bảng, hoặc thêm quan hệ với một bảng mới, phải sửa lại câu truy vấn. Do đó chỉ nên sử dụng với số lượng ít các câu lệnh truy vấn kiểu thế này, để làm giảm bớt khó khăn trong việc chỉnh sửa code. 

- Phân trang sẽ trở nên khó khăn phức tạp hơn, phải sử dụng các phương thức ghi đè cho việc phân trang.

- Dữ liệu lấy ra dưới dạng mảng, nhưng chưa được sắp xếp lại theo cấu trúc.

- Phải hiểu rất rõ quan hệ và sử dụng thành thạo các lệnh truy vấn.

Với 2 kiểu truy vấn dữ liệu như trên với ưu điểm và nhược điểm riêng, tùy vào trường hợp sử dụng mà ta có thể dùng một cách phù hợp. Hi vọng sau bài viết này, các bạn có thêm kiến thức về CakePHP để xây dựng cho bản thân một project hoàn hảo :)


TAGS: cakephp find



Comments

smooth 2011-06-27 22:24:23

Bài viết rất hay,
Website về CakePHP ở Việt Nam mình đang rất ít, hy vọng admin sẽ làm rạng danh CakePHP tại Việt Nam.

võ văn tài 2011-07-08 08:24:14

a cho e hỏi nếu như truy vấn cơ sở dữ liêu đến 2 bảng thi e pai lam sao. ví du e co bang categories(id, title) và news(id, name,category_id) e muốn kết 2 bảng lại bằng sql nhưng ko biêt pai lam như thế nào. công việc xuất ra view sẽ dùng lệnh gì.
a hướng dẫn giúp e với thank anh

HT 2011-07-08 09:18:02

@võ văn tài: bạn đặt giá trị recursive =0 rồi dùng hàm find như bài hướng dẫn để lấy giá trị. Ví dụ $this->Model->find('all');.
Để xuất giá trị trong view thì trước tiên trong controller bạn gán giá trị vào một biến rồi set giá trị cho biến đó, dùng lệnh $this->set('tên biến',$biến); ví dụ: $this->set('categories',$categories);. Sau đó trong view, bạn dùng vòng lặp foreach để in giá trị của biến đó ra. :)
P/s: có cái này ngoài lề tí, bạn đừng đặt tên table là news hay các từ đặc biệt khác như images chẳng hạn, vì khi sử dụng CakePHP sẽ nhầm lẫn giữa tên model của table đó với từ khóa trong CakePHP làm phát sinh lỗi. Bạn đổi tên bảng lại nhé. ^^

haind 2011-11-21 02:05:09

chao các bạn.

Hiện tại, các ví dụ ở trong cakephpviet.com chưa bao giờ sử dụng nhiều model trong 1 controller. Các bạn cho mình hỏi có thẻ sử dụng nhiều model trong 1 controller nào đó được không ạ.

HT 2011-11-21 04:13:10

Được chứ bạn, có 2 cách để dùng như sau:
+ Khai báo sử dụng các model bằng biến var $users=array(các model sử dụng); lưu ý là phải liệt kê luôn tên của model hiện tại của controller đó, và model đó đứng đầu mảng.
+ Sử dụng loadModel: $this->loadModel('tên model'); sau khi khai báo thì có thể gọi model đó để sử dụng bình thường.

Bùi Công Thành 2012-04-16 22:16:47

Cho mình hỏi tí: giả sử mình có 5 bảng như sau:
- hoadon(id, user_id)
- lichthi(id, hoadon, thoigian, diadiem_id, monthi_id)
- diadiem(id, tendiadiem)
- monthi(id, tenmonthi)
Giờ mình muốn truy vấn với user_id bằng 1 giá trị cụ thể nào đó, muốn lấy thông tin tenmonthi, diadiem, thoigian của user đó thì truy vấn sao cho hợp lý? Nếu kết tất cả các bảng thì có vẻ sẽ làm chậm hệ thống nhiều!
Cảm ơn HT và diễn đàn rất nhiều!

HT 2012-05-10 10:21:00

@Bùi Công Thành: Chào bạn, theo mình nghĩ thì có 2 cách
- Cách 1 là tận dụng recursive của CakePHP, bạn chọn giá trị recursive thích hợp rồi truy vấn dữ liệu, thì khi đó tự động bảng đó sẽ lấy thêm dữ liệu có liên quan tương ứng với id đó, sau đó chỉ việc in ra trên view. Cách này sẽ không kết hết tất cả các bảng lại, chỉ giới hạn một số dữ liệu cần thiết mình muốn tìm.
- Cách 2 là truy vấn từng bước: select hoadon_id có user_id bằng giá trị cụ thể từ bảng hoadon, xong select lịch thi từ bảng lichthi với hoadon_id đã tìm ra, bạn sẽ có được diadiem_id và monthi_id, rồi dùng thêm 2 câu truy vấn find đơn giản khác để tìm ra tên và địa điểm, sau đó in lên trên view. Cách này thì hơi nhiều bước nhưng mà chỉ truy vấn dữ liệu mà bạn muốn tìm, không gắn kết các bảng lại với nhau, lượng dữ liệu sẽ ít hơn.
Tùy vào bạn muốn sử dụng cách nào mà chọn cách phù hợp nhé, mình nghĩ ra được 2 cách, nếu bạn có cách nào khác nữa thì chia sẻ nhé ^^
Thân!

Vu Dang Bao Yen 2012-05-29 01:28:42

mình tạo ra 1 button đóng lại mình muốn đóng lại ma ko biết làm sao.Giúp mình với

HT 2012-05-29 08:30:15

@Bao Yen: bạn dùng javascript hoặc jquery để làm cái này, tạo một button gắn id rồi code javascript, google để tìm hiểu thêm bạn nhé.
P/S: bạn post comment không đúng chủ đề rồi :)

Lã Văn Tuyên 2012-06-06 09:34:42

A admin. Cho em hỏi chút ạ. Em có 2 table tour(id,TenTour,...) và Log(id,id_tour....)
E muốn ở bên Log lúc insert mà nó tự động insert id bên table Tour vào cột id_tour bên table Log thì thế nào hả anh. A cho e xin yahoo để tiện liên lạc. cảm ơn anh nhiều

Tấn Hùng 2012-06-13 09:08:41

Admin ơi cho mình hỏi, mình tạo một action la edit mà khi edit nó không hiện lên dữ liệu có sẵn mà không có gì hết, khi thêm dữ liệu xong mình submit thì chương trình thêm vào 1 row mới giống add vậy.
Code của mình đây :
đây là code trong CareersController
public function edit($id = null) {
if (empty($this->request->data)) {
$this->request->data = $this->Career->read();
} else {
$this->Career->id_careers = $id;
if ($this->Career->save($this->request->data)){
$this->Session->setFlash('Nghành nghề đã được cập nhật');
$this->redirect(array('action'=>'index'));
} else {
$this->Session->setFlash('có lỗi xẩy ra');
$this->redirect(array('action'=>'index'));
}
}
Đây là code edit.ctp
<?php echo $this->Form->create('Career', array('action'=>'edit')); ?>
<fieldset>
<legend>Edit Career</legend>
<?php
echo $this->Form->input('id_careers',array('type'=>'hidden'));
echo $this->Form->input('name_careers');
echo $this->Form->input('description_careers');
echo $this->Form->input('id_industries');
?>
</fieldset>
<?php echo $this->Form->end('Submit');?>
đây là code edit trong trang index.php
<?php echo $this->Html->link('Edit', array('controller'=>'careers','action' => 'edit', $career['Career']['id_careers'])); ?>

Mong bạn giúp mình với không biết sai chỗ nào, tại mình đang làm bài tập nên .....
Thanks bạn nhiều

HT 2012-06-14 00:37:01

@Tấn Hùng : bạn send source với csdl qua email gaht@cakephpviet để mình check cho nhanh nhé :), cho mình biết bạn dùng cakephp bản mấy luôn nhé.
Thân!

HT 2012-06-14 08:39:34

@Lã Văn Tuyên: để insert vào thì bạn phải tự viết hàm xử lý, CakePHP nó không tự động add được. Vậy lúc ở hàm insert ở Logs controller bạn viết thêm phần xử lý insert tour luôn:
- Tìm id của tour theo tour_id bên log
- Nếu có rồi thì không insert, còn chưa có thì insert vào. Bạn tham khảo cách lưu dữ liệu tại link: http://www.cakephpviet.com/posts/view/47/save-du-lieu-trong-cakephp.html
P/S: anh admin không thường dùng yahoo, nếu thắc mắc của bạn chưa có bài viết nào nói đến nó thì bạn gởi mail về gaht@cakephpviet.com nhé.
Thân! :)

Trọng Nghĩa 2012-07-24 04:09:36

Như vậy là Controller User thì cũng phải đặt tên Model là User, và sẽ truy vấn tới bảng Users, chẳng hạn trong trang web của mình cần truy vấn nhiều bảng không có quan hệ khóa ngoại với nhau thì phải làm sao ?

HT 2012-07-25 02:17:29

@Trọng Nghĩa: để truy vấn nhiều bảng không có quan hệ khóa ngoại với nhau thì trước khi truy vấn bạn khai báo $this->loadModel('tên model cần truy vấn'); như ví dụ:
$this->loadModel('Demo');//gọi model cần truy vấn lên trước
$this->Demo->find('all');//sau đó mới truy vấn
Nếu trong một controller mà bạn muốn truy vấn nhiều model thì bạn khai báo var $uses = array('các model')';
Lưu ý là đặt tên của model chính trước tiên nhé.
VD: bạn đang trong controller Posts, và bạn muốn truy vấn các model khác nữa thì khai báo:
var $uses = array('Post','Demo','....');
Thân mến!

Trọng Nghĩa 2012-07-25 05:35:31

Thanks Admin, từ trước tới giờ mình toàn code bằng PHP cơ bản thôi chứ chưa xài framework bao giờ, sao thấy framework khó xài quá.

HT 2012-07-27 08:03:25

@Trọng Nghĩa: ban đầu mình cũng thấy khó, nhưng quen rồi thì sẽ dễ xài bạn ạ. ^^!

Phúc 2013-01-11 03:03:07

Xin chào cakephphviet
Mình cũng mới vọc sang cakephp nên còn gà quá, có một vướng mắc mong cakephpviet giải đáp giùm, xin cảm ơn
Chả là mình có tạo một menu đa cấp ngoài site, mình select được menu từ bảng Category ra rồi nhưng khi mình thêm sản phẩm vào danh mục thì lại nảy sinh một vấn đề như sau:
+ Thư mục mình có cấu trúc ví dụ: Tin tức/ Tin trong nước/ An ninh
+ Khi mình thêm tin vào mục An ninh thì chỉ khi click vào An ninh mới ra list bài viết, còn khi mình click vào danh mục cha của An ninh là Tin trong nước thì lại không list ra được bài viết nằm trong An ninh, mình dùng cakephp 1.3. Mong cakephpviet giúp đỡ, xin chân thành cảm ơn!

Trương Đức Toản 2013-01-24 21:27:52

Mình có một vấn đề muốn hỏi mọi người, không rõ có đúng chủ đề không.
Mình có 1 web , vừa có tin tức và vừa có sản phẩm. Phần tìm kiếm chỉ có tìm kiếm text thôi. Vậy có cách nào để tìm kiếm trên cả 2 bảng rồi show dữ liệu ra không?
Mình muốn dùng luôn phân trang trong cake, nên nếu tách 2 truy vấn thì không dùng được cách này. Có ai biết thì giúp mình với.
Cảm ơn mọi người!

HT 2013-02-18 04:55:16

@Phúc: Bạn có sử dụng Tree behavior để tạo menu đa cấp không, vì tree behavior có thể giải quyết được vấn đề này.
Bạn tham khảo cách sử dụng tại link:
http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html
Thân.

HT 2013-02-19 04:46:33

@Trương Đức Toản: nếu mà 2 bảng có quan hệ với nhau thì bạn joins 2 bảng lại rồi query và phân trang, bạn tham khảo bài viết này nhé:
http://www.cakephpviet.com/posts/view/64/phan-trang-co-ban-trong-cakephp-p2-phan-trang-tung-phan.html#comments
Còn nếu 2 bảng không liên quan nhau thì bạn phải phân trang 2 cái đó riêng ra thôi.
http://www.cakephpviet.com/posts/view/64/phan-trang-co-ban-trong-cakephp-p2-phan-trang-tung-phan.html

Loi 2013-08-25 08:56:59

Vậy mình muốn sửa dữ liệu hoặc xóa dữ liệu thì dùng câu truy vấn dư nào hả mọi người?

HT 2013-08-26 10:36:58

@Loi: dùng hàm delete($id) để xóa, và dùng updateAll() để update dữ liệu, ngoài ra còn có thể dùng save(), saveAll(), saveField() để lưu hoặc update dữ liệu.
Cú pháp chung sẽ là $this->Model->tên-hàm(); nhé bạn

Comment