Trang

Thứ Năm, 18 tháng 7, 2013

Tương tác cở sở dữ liệu với Zend_Db_Table [Zend Framework]

Tương tác cơ sở dữ liệu là một việc không thể thiếu đối với 1 trang web động. Zend Framework hỗ trợ nhiều lớp để tương tác với cơ sở dữ liệu thông qua Model. Và một trong những lớp thông dụng nhất đó là Zend_Db_Table.

Trước hết ta tìm hiểu Model là gì? Và tại sao phải sử dụng Model ?
Model là tầng xử lý những tác vụ liên quan đến tương tác cơ sở dữ liệu từ những yêu cầu của Controller. Model xử lý và trả về kết quả dưới dạng một mảng dữ liệu, khi đó thông qua View ta sẽ đẩy nội dung của mảng dữ liệu ấy ra bên ngoài. Việc tách biệt tầng Model có rất nhiều thuận lợi, trước là dễ quản lý sau là dễ nâng cấp và phát triển trong tương lai của mã nguồn.

I. CÁC BƯỚC THỰC HIỆN:

1. Bước 1: Để tương tác được với Model thì trước tiên ta phải kết nối được với cơ sở dữ liệu, ta thực hiện cấu hình kết nối cơ sở dữ liệu qua bài: Cấu hình kết nối cở sở dữ liệu trong Zend Framework.
Sau khi thực hiện kết nối, ta tiếp tục bước 2.

2. Bước 2: Tới đây, để các lớp trong Model sử dụng được trong Controller, chúng ta phải báo cho Controller biết đường dẫn tới các lớp trong Model. Vì thế ta cần phải định nghĩa như sau trong file bootstrap.php:
protected function _initAutoload()
     {
        $autoloader = new Zend_Application_Module_Autoloader(array(
            'namespace' => '',
            'basePath'  => dirname(__FILE__),
        ));
        return $autoloader;
     }
Nếu không có bước này chúng ta sẽ bị lỗi giống như sau:
Fatal error: Class 'Model_User' not found in C:\xampp\htdocs\zfexam\application\controllers\UserController.php on line 4.
Sau khi khai báo cho Controller biết ta tiến hành bước 3 là tương tác với Model

3. Bước 3: tương tác với Model:

 a. Trên mô hình không module:
Ta vào trong thư mục models, tạo file chitiettin.php với nội dung sau:
<?php
  class Model_Chitiettin extends Zend_Db_Table_Abstract
    {
          protected $_name = "chitiettin";
          protected $_primary = "idTin";
    }
?>
Qua đoạn lệnh trên ta hiểu phần nào về quy tắc định nghĩa một lớp Model trong Zend Framework. Vì tất cả các file nằm trong thư mục models nên áp dụng theo cơ chế lazy loading, ta có quy tắc định nghĩa: Model_Tênfile ( Tênfile phải viết hoa chữ cái đầu tiên ).

Lưu ý:
 + Thư mục models của chúng ta có s, nhưng khi định nghĩa thì chúng ta bỏ qua s và viết bình thường là Model.
 + Tên file ta có thể đặt tên là Chitiettin.php hay chitiettin.php cũng đều được. Nhưng trong định nghĩa tên lớp ta phải viết hoa chữ cái đầu tiên.

Trong lớp Model_Chitiettin ở trên, ta kế thừa lớp Zend_Db_Table_Abstract. Và khai báo tên bảng, tên khóa chính thông qua hai thuộc tính $_name$_primary. Việc khai báo tên bảng và khóa chính là để Zend biết chúng ta thao tác trên bảng nào.

 b. Trên mô hình multimodules:
Giả sử chúng ta có hai module là DefaultAdmin. Và dĩ nhiên trong mỗi module chúng ta cũng có models. Cấu trúc file model như sau:
<?php
  class Admin_Model_Chitiettin extends Zend_Db_Table_Abstract
    {
          protected $_name = "chitiettin";
          protected $_primary = "idTin";
    }
?>
Khi có nhiều modules thì cấu trúc thư mục sẽ thay đổi thành: admin/models/chitiettin.php. Do vậy ta đặt tên lớp theo cơ chế lazy loading là: Tênmodule_Model_Chitiettin .
Tới đây ta có thể viết các câu lệnh để thực hiện truy vấn cơ sở dữ liệu.

II. THỰC HIỆN TRUY VẤN CƠ SỞ DỮ LIỆU:

1. Active record:
Khi làm việc với các Framework, chúng ta sẽ làm quen với khái niệm active record. Active record là các hàm dựng sẵn, giúp chúng ta dễ dàng thực hiện các câu truy vấn bằng việc truyền vào các dữ liệu cần thiết mà không cần viết các câu truy vấn.
Ví dụ: Với câu truy vấn thường:
" SELECT * FROM chitiettin WHERE idTin=2 ".
Ta dùng active record:
$se=$this->select();
$se->from('chitiettin');
$se->where(' idTin = ? ', 2); 

2. Câu lệnh select:
Tạo một select:
$se=$this->select();
Lệnh này tương đương với câu SQL " SELECT * ". Và chúng ta sẽ sử dụng $se để thêm các thông số cho truy vấn.

 a. From:
 Ta không cần định chỉ rõ là bảng nào trong active record vì chúng ta đã khai báo tên bảng ở thuộc tính $_name. Tuy nhiên chúng ta sẽ dùng from để tối ưu hóa câu truy vấn, cụ thể chúng ta sẽ giới hạn lại các cột, chỉ lấy ra dữ liệu của các cột cần thiết.
Cú pháp:
$se=$this->select();
$se->from(' tên_bảng ', array( 'cột 1', ' cột 2 ', ' ... ' ));
Ví dụ:
SQL: " SELECT idTin, TieuDe, TomTat, NoiDung FROM chitiettin  "
Active record:
$se=$this->select();
$se->from(' chitiettin ',array(' idTin ', ' TieuDe ', ' TomTat ', ' NoiDung '));

b. Where:
Cú pháp:
$se=$this->select();
$se->where( ' tên_cột biểu_thức ? ', ' giá_trị ' );
Cú pháp ở trên cho ta liệt kê dữ liệu với điều kiện giá trị của cột sẽ bằng, lớn hơn hay nhỏ hơn giá trị nào đó. Quy tắc trong zend framework đối với mệnh đề where là tên cột, rồi đến biểu thức, rồi đến ký hiệu "?". Và sau cùng là mới là giá trị.
Biểu thức có thể là:  ">", "<", "=", ">=", "<=".
Ví dụ 1:
SQL: " SELECT * FROM chitiettin WHERE idTin=2 "
Active record:
$se=$this->select();
$se->where(' idTin = ? ', 2);
Ví dụ 2:
Giả sử ta có $idTin=5
SQL: " SELECT * FROM chitiettin WHERE idTin= $idTin "
Active record:
$se=$this->select();
$se->where(' idTin = ? ', $idTin);
Ví dụ 3:
Giả sử ta có $TieuDe="CR7-se-den-Manchester-United"
SQL: " SELECT * FROM chitiettin WHERE TieuDe = '$TieuDe' "
Active record:
$se=$this->select();
$se->where(' TieuDe = ? ', $TieuDe);
Ví dụ 4:
Ta muốn lấy các tin có idTin lớn hơn 5.
SQL: " SELECT * FROM chitiettin WHERE idTin > 5 "
Active record:
$se=$this->select();
$se->where(' idTin > ? ', 5);
Ví dụ 5:
Ta muốn lấy tin ở thể loại bóng đá và có idTin=5.
SQL: " SELECT * FROM chitiettin WHERE TheLoai='bong-da' AND idTin=5  "
Active record:
$se=$this->select();
$se->where('TheLoai = ?', 'bong-da');
$se->where(' idTin = ? ', 5);
Lưu ý: Nếu câu truy vấn có nhiều hơn 1 điều kiện thì ứng với bao nhiêu điều kiện ta có bấy nhiêu dòng $se->where, không có $se->and

c. Order:
Câu lệnh order có tác dụng sắp xếp các record rồi trả về theo một thứ tự nào đó.(record là các dòng dữ liệu trong 1 bảng)
Cú pháp:
$se=$this->select();
.
.
.
$se->order(' tên_cột  DESC '); //sắp xếp theo thứ tự giảm dần.
or
$se->order(' tên_cột  ASC '); //sắp xếp theo thứ tự tăng dần.
Ghi chú:
DESC: thứ tự giảm dần. ví du: 10>9>8>7>6>...
ASC: thứ tự tăng dần. ví dụ: 1<2<3<4<5<..
Nếu câu lệnh order không có tham số thì mặc định là tăng dần.
Ví dụ:
Lấy tất cả các tin từ bảng chitiettin rồi sắp xếp các tin từ mới đến cũ.
SQL: " SELECT * FROM chitiettin ORDER BY idTin DESC "
Active record:
$se=$this->select();
...
$se->order(' idTin DESC ');

d. Limit: Giới hạn số record lấy ra.
Cú pháp:
$se=$this->select();
.
.
.
$se->limit("$count","$offset");
Ghi chú:
$count: Số dòng (record) cần lấy ra.
$offset: Lấy từ vị trí $offset.
Cú pháp này hơi ngược so với SQL, trong SQL chúng ta sẽ limit vị_trí, số_dòng.
Ví dụ:
Lấy 5 tin mới nhất từ bảng chitiettin.
SQL: " SELECT * FROM chitiettin ORDER BY idTin DESC LIMIT 0,5  "
Active record:
$se=$this->select();
...
$se->order(' idTin DESC ');
$se->limit(5,0);

e. join:
Đôi khi việc truy vấn dữ liệu đòi hỏi chúng ta phải kết bảng để có thể lấy dữ liệu theo ý muốn. Zend_Db_Table cho phép chúng ta có thể kết nhiều bảng với nhau bằng active record là join.
Cú pháp:
$se=$this->select(SELECT_WITH_FROM_PART);
$se->setIntegrityCheck(false);
$se->join(' bảng_thứ_2 ', ' điều_kiện_kết_bảng ', array( các_field_cần_lấy_ở_bảng_thứ_2 ));

Ví dụ 1:
Lấy các tin có cùng loại tin là bóng đá.
Ta sẽ join bảng chitiettin với bảng loaitin với điều kiện kết bảng: chitiettin.idLT = loaitin.idLT
$se=$this->select(SELECT_WITH_FROM_PART);
$se->setIntegrityCheck(false);
$se->join(' loaitin ', ' chitiettin.idLT = loaitin.idLT ',array(' idLT ', ' TenLT ', ' TenLT_KhongDau '));
$se->where(' loaitin.TenLT_KhongDau = ?', 'bong-da');

Ví dụ 2:
Chúng ta sẽ kết 3 bảng để lấy các tin thuộc thể loại là thể thao và loại tin là bóng đá.
Điều kiện kết bảng:
+ theloai.idTL = loaitin.idTL
+ chitiettin.idLT = loaitin.idLT
Ở đây ta có:
+ Cứ mỗi loại tin thì nó sẽ thuộc một thể loại nào đó. Nên mỗi loại tin sẽ có một idTL.
Ví dụ: loại tin bóng đá, bóng rổ, điền kinh,... thuộc về thể loại thể thao.
+ Cứ mỗi tin sẽ thuộc một loại tin nào đó. Nên mỗi tin sẽ có một idLT.
Ví dụ: "CR7 sẽ đến MU"," Sir Alex Ferguson về hưu"... thuộc về loại tin bóng đá.
$se=$this->select(SELECT_WITH_FROM_PART);
$se->setIntegrityCheck(false);
$se->join(' theloai ', ' theloai.idTL = loaitin.idTL ', array(' idTL ', ' TenTL ', ' TenTL_KhongDau '));
$se->join(' loaitin ', ' loaitin.idLT = chitiettin.idLT ', array(' idLT ', ' TenLT ', ' TenLT_KhongDau '));
$se->where(' theloai.TenTL_KhongDau = ? ', ' the-thao ');
$se->where(' loaitin.TenLT_KhongDau = ? ', ' bong-da ');

3. Lấy dữ liệu từ câu truy vấn select:
Sau khi viết các câu lệnh truy vấn, chúng ta sẽ tiến hành lấy kết quả truy vấn. Nhưng đầu tiên ta nên xem kết quả trả về sẽ là gì đã?
Trong Controller:
public function indexAction()
  {
        $ctt = new Model_Chitiettin();
        $getAll = $ctt->getTin();
        echo "<pre>";
        print_r($getAll);
        echo "</pre>";
  }

Trong Model:
<?php
class Model_Chitiettin extends Zend_Db_Table_Abstract{
        protected $_name = "chitiettin";
        protected $_primary = "idTin";
        
     public function getTin()
       {
            $se=$this->select();
            ...
            ...
            return $se;
       }
}
?>
Kết quả trả về sẽ như thế này:
Zend_Db_Table_Select Object
(
    [_info:protected] => Array
        (
            [schema] => 
            [name] => chitiettin
            [cols] => Array
                     ...
                     ...
) 
Ta nhận thấy ngay đây là một đối tượng của Zend_Db_Table_Select. Nhưng chúng ta không nên quan tâm đến điều này vì mục đích của chúng ta là làm sao để lấy được kết quả mà mình muốn lấy.
Ta thay đổi hàm getTin một chút:
$se=$this->select();
...
...
$result=$this->fetchAll($se);
return $result;
Và kết quả như sau:
Zend_Db_Table_Rowset Object
(
    [_data:protected] => Array
        (
            [0] => Array
             ...
             ...
 )
Ta thấy kết quả trả về là một đối tượng của lớp Zend_Db_Table_Rowset, trong đối tượng này chứa rất nhiều thông tin. Ngoài những thông tin cần thiết, một điều không mấy thú vị là username, password và tên cơ sở dữ liệu cũng được lấy ra. Tuy nhiên không mấy khi chúng ta dùng đến thuộc tính này.
Ta tiếp tục thay đổi hàm getTin
$se=$this->select();
...
...
$result=$this->fetchAll($se)->toArray();
return $result;
Kết quả trả về:
Array
(
    [0] => Array
        (
            [idTin] => 141
            [idCD] => 1
            [idCM] => 2
            [TieuDe] => [Css] CSS là gì?
        )

    [1] => Array
        (
            [idTin] => 140
            [idCD] => 1
            [idCM] => 2
            [TieuDe] => [HTML] Ngôn ngữ HTML (Phần 2).
        )
     ...
     ...
)
Đến đây ta đã có kết quả mà ta mong đợi. Ta sử dụng lệnh foreach để duyệt từng dòng dữ liệu.
foreach( $getAll as $row )
 {
       echo $row['idTin']."<br/>";
       echo $row['TieuDe']."<br/>";
 }

4. Đếm số dòng dữ liệu (record) trả về.
$result=$this->fetchAll($se)->toArray();
$num=count($result);

5. Lấy dòng đầu tiên của truy vấn.
$result=$this->fetchRow($se)->toArray();

6. Câu lệnh insert:
$arr = array(' tên_field_1 '=>' giá_trị_1 ', ' tên_field_2 '=>' giá_trị_2 ', ...);
Cú pháp:
$this->insert($arr);

7. Câu lệnh update:
$arr = array(' tên_field_1 '=>' giá_trị_1 ', ' tên_field_2 '=>' giá_trị_2 ', ...);
Cú pháp:
$this->update($arr, điều_kiện);
Câu lệnh trên tương đương với:
UPDATE tenbang
SET tên_field_1 = 'giá_trị_1' , tên_field_2 = 'giá_trị_2', ...
WHERE điều_kiên. 
Ví dụ:
Update tên hãng HP thành HP2 với id là hp
public function updateHangsx()
  {
     $arr = array('tenhang'=>'HP2');
     $this->update($arr, " id = 'hp' ");
  }

8. Câu lệnh delete:
$se=$this->delete(" điều_kiện ")
Câu lệnh trên tương đương với:
DELETE FROM tenbang
WHERE điều_kiện
Nguồn: http://www.qhonline.info

Không có nhận xét nào:

Đăng nhận xét