Phần I: Giới thiệu về PDO 




Một trong những đồ chơi hoành tráng nhất trong phiên bản PHP "5 chấm" là sự ra đời của thư viện đối tượng PDO mở rộng, cho phép chúng ta thao tác với các CSDL khác nhau một cách nhẹ nhàng, đơn giản như là đi chơi Tết (Nói vậy chớ CMXQ thấy đi chơi tết cũng khó bỏ xừ, ít nhất là cái công đoạn rúc ra khỏi cái chăn ấm áp ).

Để chính thức khởi động PHPVN cho năm mới, CMXQ sẽ trùm chăn làm một serial về PDO. Điều kiện cần và đủ là:
- Biết qua một tí về ADO, BDO, ODBC hay cái quái gì có liên quan đến Data Object cũng được (để dễ so sánh với cái PDO này)
- Một cốc cafe G7 (cái này thì chắc ai cũng có sẵn).
- Nếu ở ngoài Bắc thì cần một cái chăn ấm nữa

Nào, bây giờ chúng ta bắt đầu:

I. Giới thiệu về PDO




PDO là cụm từ viết tắt của PHP Data Objects, có nghĩa là Các đối tượng dữ liệu trong PHP (đại loại thế). Đây là một thành phần mở rộng của PHP nhằm cung cấp cho chúng ta một cách thức đơn giản đề truy cập vào các CSDL khác nhau trong PHP.

Nếu như các bạn đã từng lập trình trên .Net, các bạn sẽ thấy một cái gọi là ADO (bản cũ) và ADO.Net, còn nếu các bạn đã từng code bằng Delphi, nó cũng có một món đồ chơi tương tự gọi là BDO, còn trên Java là JDBC... Về cơ bản thì mấy cái này nó giông giống nhau, mà bản chất là cung cấp một mô hình trừu tượng để truy cập tới CSDL. (Có thể coi nó như là một lớp (layer) trong lập trình đa lớp).

Cơ chế làm việc của những cái này có thể được mô hình hoá như sau:

Ứng dụng <----> PDO <---> Trình điều khiển các CSDL <---> Các truy vấn tới CSDL và kết quả trả về từ CSDL <---> CSDL (Access, MySQL, SQL Server, Oracle....).

Chúng ta có thể hình dung rằng mỗi một Hệ quản trị CSDL có một cách quản lý dữ liệu khác nhau, chẳng ai giống ai. Vì vậy, để truy xuất vào một loại CSDL nào đó, chúng ta cần phải khai báo trình điều khiển CSDL tương ứng với loại CSDL đó. Sau khi đã khai báo trình điều khiển, chúng ta có thể kết nối tới CSDL đó, tiến hành các truy vấn (có thể dựa trên ngôn ngữ truy vấn SQL, qua các Stored Procedure... mà CSDL đó hỗ trợ), lấy kết quả về rồi dùng các lệnh khác của PHP xử lý kết quả và hiển thị cho NSD.

II. Cài đặt PDO




PDO chỉ xuất hiện trong PHP từ ver 5 chấm trở đi, vì vậy nếu như server của các bạn chỉ support dưới 5 chấm, hãy chịu khó update lên 5 chấm.

Sau khi cài đặt xong, chúng ta phải mở cái php.ini và enable thư viện này (nếu chưa có) cùng các trình điều khiển tới các CSDL liên quan.

VD dưới đây là dòng lệnh cấu hình trình điều khiển CSDL chạy trên Windows:

extension=php_pdo.dll
extension=php_pdo_firebird.dll
extension=php_pdo_informix.dll
extension=php_pdo_mssql.dll
extension=php_pdo_mysql.dll
extension=php_pdo_oci.dll
extension=php_pdo_oci8.dll
extension=php_pdo_odbc.dll
extension=php_pdo_pgsql.dll
extension=php_pdo_sqlite.dll

Chi tiết hơn trên các HĐH khác nhau thì các bạn chịu khó xem trên PHP Manual nhé. Giờ tớ phải đi pha cafe đã.

I. Thiết lập kết nối tới CSDL




Đầu tiên, chúng ta cần phải biết rằng PDO thuần tuý là một lớp (class) được dùng để thao tác với CSDL. Vì vậy, nó cũng có các phương thức và thuộc tính như bất kỳ một lớp nào được xây dựng trên PHP.

PDO sử dụng một phương thức mặc định là __construct(). Phương thức này kiêm luôn nhiệm vụ tạo kết nối tới CSDL. Phương thức này có các tham số như sau:
PDO {
PDO __construct(string dsn,
string username,
string password,
array driver_options);
}
Trong đó:
- dsn (Data Source Name) là một chuỗi văn bản chứa các thông tin cần thiết để kết nối tới một CSDL xác định.
- username và password: chứa tên truy cập và mật khẩu của một tài khoản có quyền thao tác trên CSDL đó.
- driver_options là các tham số tuỳ chọn bổ sung nếu cần.

Cái DSN hơi khó nhớ một chút, và nó tuỳ thuộc vào loại CSDL. Nếu cần thiết thì các bạn chịu khó vào Google search. Ví dụ dưới đây là một chuỗi DSN kết nối tới CSDL MySQL:
'mysql:dbname=testdb;host=127.0.0.1';

Ví dụ:
PHP Code:

= new PDO('mysql:host=localhost;dbname=thumotti''thumotti''thuhaiti');?>



Đoạn mã trên tạo ra một biến $dbh (thể hiện của đối tượng PDO). Vì __construct là phương thức mặc định của PDO nên trong đoạn mã trên chúng ta không cần phải gọi tên phương thức này ra.

II. Huỷ kết nối tới CSDL

Để huỷ kết nối, đơn giản chúng ta chỉ việc gán instance của đối tượng đó với giá trị null.

VD:
PHP Code:

= new PDO('mysql:host=localhost;dbname=thumotti''thumotti''thuhaiti');$dbh=null?>




III. Sử dụng PDO để truy vấn SQL 



Ở trên, các bạn đã biết cách kết nối và huỷ kết nối tới CSDL. Bây giờ chúng ta sẽ tiến hành thực hiện một truy vấn SQL thông qua PDO.


Để thực hiện một truy vấn SQL, chúng ta sử dụng một phương thức của PDO là exec().

PHP Code:


PDO {int exec(string statement);



Trong đó, statement là một câu lệnh SQL. Chú ý rằng câu lệnh SQL này phải chạy được trên CSDL mà ta đang sử dụng, (do mỗi một hệ quản trị CSDL lại có một vài câu lệnh SQL đặc chủng nên ta cần phải chú ý điều này). Phương thức trên trả về kết quả là số "râu" (row - record hay bản ghi) bị ảnh hưởng bởi câu lệnh SQL


Ví dụ dưới đây sử dụng để xoá một vài bản ghi nào đó:

PHP Code:

$dbh = new PDO('mysql:host=localhost;dbname=thumotti''thumotti''thuhaiti');$count $dbh->exec("DELETE FROM users WHERE user_id<100"); // Xoá bỏ những "tên" có "số" nhỏ hơn 100.print("Đã xoá bỏ $count người khỏi CSDL.\n");?>


Comment: Hic, các ông thầy bói kể ra cũng đúng, mỗi người ai cũng có một cái số cả. Nhỡ bị exec delete thì toi

Vậy là chúng ta đã biết cách thực hiện một truy vấn. Tuy nhiên, đời vẫn còn dài, ít nhất chúng ta cần phải lấy thông tin từ CSDL ra rồi in nó nữa chứ. Dù sao thì hồi 2 đã hết, xin xem tiếp hồi 3 ...
Nếu như các bạn làm theo những bước ở trên thì chưa đủ, ta phải tìm cách lấy dữ liệu trả về từ các query (VD như truy vấn Select chẳng hạn).

Để làm điều này, trước tiên chúng ta phải sử dụng một phương thức khác của PDO là PDO->query(). Phương thức này có nhiệm vụ thực thi một truy vấn SQL và trả về tập kết quả. Thường thì tập kết quả này sẽ được một lớp đối tượng khác là PDO Statement xử lý. Đây là tập các phương thức chuyên dụng để xử lý kết quả trả về, nó cũng gần giống như cái recordset vậy.

PHP Code:

PDO {PDOStatement query(string sql);


Các bạn cần chú ý phân biệt giữa hai phương thức exec() và query(). Exec() thực thi một truy vấn và trả về số các dòng bị ảnh hưởng bởi truy vấn, còn query() trả về một tập kết quả (thường là một recordset hoặc giá trị false). Người ta thường dùng exec() để truy vấn các câu lệnh như Insert, delete, update..., và dùng query() để truy vấn các câu lệnh Select.

Chúng ta có thể lấy kết quả ra theo hai cách: Lấy về từng dòng hay lấy về toàn bộ. Điểm đặc biệt là cũng như với các hàm xử lý MySQL, kết quả trả về thường được lưu trữ dưới dạng một mảng, rất thuận tiện và dễ dùng (hay nói ngắn gọn hơn là ... đại tiện ).

I. Lấy kết quả về theo từng dòng



Để lấy kết quả theo từng dòng (tương tự như hàm mysql_fetch_array()), chúng ta có thể sử dụng phương thức PDOStatement->fetch().


PHP Code:


PDOStatement {mixed fetch(int fetch_style,int cursor_orientation,int cursor_offset);



Hàm này sử dụng 3 tham số tuỳ chọn:

- Fetch_style xác định cách thức lấy dữ liệu. Nó có thể nhận một trong các giá trị sau:


  • PDO::FETCH_ASSOC: Trả về một mảng được đánh chỉ số bởi tên cột (field) (Tương tự như MYSQL_ASSOC)
  • PDO::FETCH_BOTH (mặc định): trả về một mảng được đánh chỉ số bởi tên cột và chỉ số số nguyên bắt đầu từ 0. (tương tự như MYSQL_BOTH)
  • PDO::FETCH_NUM: trả về một mảng được đánh chỉ số bởi số thứ tự của cột (xuất hiện trên query SELECT), bắt đầu = 0 (Tương tự như MYSQL_NUM)

... và một vài giá trị khác, tham khảo thêm trong manual, nhưng vì chưa đến lúc sờ đến nên CMXQ không liệt kê ở đây.

Cũng tương tự như mysql_fetch_array, phương thức này lấy một dòng từ vị trí con trỏ hiện tại và đẩy con trỏ sang dòng kế tiếp.


Ví dụ:
PHP Code:


$dbh = new PDO('mysql:host=localhost;dbname=thumotti''thumotti''thuhaiti');$stmt $dbh->query("SELECT * FROM user where user_id<10") or die();echo "";while (($row $stmt->fetch(PDO::FETCH_ASSOC))) { echo "
${row['user_name']}${row['e-mail']}\n";}echo "

"



II. Lấy toàn bộ dữ liệu và đưa vào mảng.



Để thực hiện điều này, chúng ta cần phải sử dụng một phương thức khác thay cho fetch, đó là fetchall(). Cách thức dùng cũng tương tư nặng thằng fetch, chỉ có điều nó đưa tất cả dữ liệu vào một mảng.


Ví dụ:
PHP Code:

$dbh = new PDO('mysql:host=localhost;dbname=thumotti''thumotti''thuhaiti');$stmt $dbh->query("SELECT * FROM user where user_id<10") or die();$result=$stmt->fetchAll();print_r($result); 






Sử dụng Prepared statements:

Prepared statement có thể coi là 1 mẫu query được dịch sẵn với các tham số có thể thay đổi được.

Quá trình xử lý (parse/prepare) 1 query của database bao gồm việc phân tích, dịch và tối ưu query trước khi thực hiện nó. Sử dụng prepared statements, query chỉ phải parse 1 lần, sau đó ta có thể dùng lại nó nhiều lần với các tham số khác nhau mà không phải parse lại với mỗi lần thực hiện truy vấn như cách thông thường, nhờ đó sẽ tiết kiệm được tài nguyên và tăng tốc độ ứng dụng. Ngoài ra, sử dụng prepared statements còn giúp tránh được SQL injection.

Để tạo prepared statements, ta sử dụng phương thức: PDO->prepare()

PHP Code:


class PDO PDOStatement prepare string statement [, array driver_options] )


Trong đó tham số statement là 1 chuỗi query có sử dụng các placeholder làm tham số đầu vào. Phương thức này trả về 1 đối tượng PDOStatement.
Ví dụ:
PHP Code:
//Tạo database object$dbh = new PDO('mysql:host=localhost;dbname=thumotti', 'thumotti', 'thuhaiti');//Tạo prepared statement$stmt = $dbh->prepare("INSERT INTO users (name, isMale) VALUES (:name, :ismale)");$stmt->bindParam(':name', $name);$stmt->bindParam(':ismale', $ismale);// Thêm record$name = 'Ácman';$ismale = 1;$stmt->execute();// Thêm record$name = 'cmxq';$ismale = -1;$stmt->execute();?>






Giải thích:
- Ở VD trên ta sử dụng :name, :ismale làm các placeholder, nơi sẽ nhận các giá trị cụ thể khi thực hiện query. Ngoài cách trên, ta có thể dùng ? làm placeholder. VD:
PHP Code:

$dbh->prepare("INSERT INTO users (name, isMale) VALUES (?, ?)");?>





- Phương thức PDOStatement->bindParam() gắn placeholder với 1 biến, giống như PDOStatement->bindValue(). 2 phương thức này hoàn toàn giống nhau ngoại trừ việc bindParam() gắn biến theo tham chiếu. Placeholder khi được sử dụng dưới dạng ? sẽ được phân biệt bởi thứ tự của chúng. VD:
PHP Code:
->bindParam(1$name);$stmt->bindParam(2$ismale);?>





- Phương thức PDOStatemen->execute() thực hiện câu lệnh prepared statement. Tham số truyền vào là 1 mảng. Nếu bỏ trống tham số, phương thức sẽ sử dụng giá trị của các biến mà ta đã gắn cho placeholder. VD:
PHP Code:

->execute(array(':name'=>'Ácman'':ismale'=>1));$stmt->execute(array('cmxq', -1)); //Khi placeholder sử dụng ??>