Bạn là một PHP Programmer, bạn sử dụng PHP và MySQL cho công việc của mình ? Nhưng bạn có biết có bao nhiêu cách kết nối với MySQL ? Kết nối và sử dụng chúng như thế nào cho hiệu quả ? Bài viết này sẽ phần nào giải đáp những thắc mắc của bạn về vấn đề này.
Phụ mục
PDO là gì ?
PDO (PHP Data Object) nói một cách nôm na là các API có sẵn của PHP cho phép bạn có thể kết nối với cơ sở dữ liệu MySQL của mình. Hiện nay PHP cung cấp bao nhiêu API để kết nối ? Ba, nhiều bạn sẽ có câu trả lời như vậy và dưới đây là danh sách các API đó:
- mysql– MySQL Improved
- mysqli
- pdo– PHP Data Objects
Cách sử dụng truyền thống được sử dụng nhiều nhất, rộng rãi nhất, được biết đến nhiều nhất và thường được đưa vào các giáo trình PHP đó là sử dụng API mysql. Chủ yếu là do các dùng này khá thuận tiện khi cần kết nối và thu hồi dữ liệu từ cơ sở dữ liệu. Ví dụ:
[code language=”php”] /** babyinternet
* From: www.vnwebmaster.com
*/
# Ket noi voi database
mysql_connect(‘localhost’, ‘username’, ‘password’) or die(‘Could not connect: ‘ . mysql_error());
# Chon database
mysql_select_db(‘someDatabase’) or die(‘Could not select database’);
# Xay dung va thuc thi query
$query = "SELECT * from someTable";
$result = mysql_query($query) or die(‘Query failed: ‘ . mysql_error());
# Lấy kết quả và in ra thông tin cần thiết
while ($row = mysql_fetch_object($result)) {
echo $row->name;
}
[/code]
Code trên có vẻ ổn, nhưng nó cũng kèm theo vài ba nhược điểm quan trọng của mình:
- Deprecated: mặc dù chưa được công nhận rộng rãi. Tuy nhiên tôi nghĩ là so với mặc dễ tiếp nhận đối với người mới bắt đầu, đoạn mã trên vẫn có thể chấp nhận được.
- Escaping: Câu truy vấn có thể gặp vấn đề đối với chuỗi hoặc dữ liệu người dùng được đưa vào. Nhiều người không hiểu hoặc không biết làm thế nào để định dạng lại dữ liệu hợp lý cho nhu cầu của mình.
- Flexibility: Hay nói cách khác là nó không linh hoạt. Đoạn mã trên chỉ giải quyết cho việc bạn sử dụng một Database duy nhất. Nếu bạn có từ 2 database phải truy vấn đồng thời trở lên, điều gì sẽ xảy ra nếu bạn liên tục chuyển đổi qua lại giữa các cơ sở dữ liệu ?
Làm thế nào ?
Cũng giống như mọi developer khác, khi tôi bắt đầu tìm hiểu và học về PHP & MySQL, tôi khá tù mù và hoa mắt với các API PDO. Cảm giác đầu tiên là thấy có nhiều cách và nhiều thứ phải học, mã lệnh nhiều và khá rối rắm (điều này không phải như vậy) nên tôi thường chọn cách được sách giáo khoa mô tả nhiều và dễ tương tác nhất : API mysql với đoạn mã tôi đã nói ở trên.
Bây giờ tôi ngồi lại đây, giúp bạn hiểu hơn và không phải lo lắng khi tiếp xúc với các API khác như tôi ngày xưa. Chúng cũng dễ tiếp cận thôi và bạn không phải lo lắng quá nhiều như tôi ngày xưa.
Kết nối
Nếu bạn đã thông thuộc đoạn mã trên, chúng ta sẽ bắt đầu bằng việc phân tích kết nối để làm thế nào đó kế thừa lại những gì chúng ta đã biết, đã học nhờ đó chúng ta sẽ nhớ lâu hơn và hiểu hơn về nó.
Quay lại với đoạn mã kết nối mà tôi đã đăng ở trên:
[code language=”php”]# Ket noi voi databasemysql_connect(‘localhost’, ‘username’, ‘password’) or die(‘Could not connect: ‘ . mysql_error());[/code]
Bây giờ chúng ta sử dụng PDO API để kết nối, đoạn mã sẽ như thế này:
[code language=”php”]$conn = new PDO(‘mysql:host=localhost;dbname=myDatabase’, $username, $password);[/code]Hãy dừng lại để phân tích, nó thực sự rất đơn giản: bạn chỉ cần định tên của các trình điều khiển(trong trường hợp này là mysql), tiếp theo là các giá trị tương ứng kèm theo (connection string) để kết nối với trình điều khiển tương ứng. Điều hữu dụng hơn của phương án sử dụng PDO so với mysql_connect là gì ? Đó là một khi bạn cần kết nối với trình điều khiển khác (ví dụ như SQLite chẳng hạn), bạn chỉ cần cập nhật lại DSN hay còn gọi là Data Source Name phù hợp là xong. Chúng ta sẽ không cần phải phụ thuộc vào MySQL quá nhiều và cũng không cần phải sử dụng các hàm riêng biệt chỉ dành cho MySQL như mysql_connect làm gì.
Có lỗi kết nối
Nếu có lỗi kết nối, thay vì sử dụng hàm die() như đoạn mã đầu tiên, chúng ta sẽ đưa vào khối try/catch để bắt lỗi như sau:
[code language=”php”] try {$conn = new PDO(‘mysql:host=localhost;dbname=myDatabase’, $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
echo ‘ERROR: ‘ . $e->getMessage();
}
[/code]
Bạn lưu ý rằng chế độ bắt lỗi trong DPO mặc định là PDO::ERRMODE_SILENT. Với thiết lập này không thay đổi, bạn sẽ cần tự lấy lỗi thông qua những câu truy vấn của mình.
[code language=”php”]echo $conn->errorCode();echo $conn->errorInfo();[/code]
Đó là môi trường thực tế, còn trong môi trường của nhà phát triển, bạn sẽ cần dừng kịch bản lại ngay và sửa lỗi. Trong trường hợp này chúng ta cần thay đổi sang tùy chọn PDO::ERRMODE_EXCEPTION,. Hãy tham khảo một vài tùy chọn sau:
– PDO::ERRMODE_SILENT
– PDO::ERRMODE_WARNING
– PDO::ERRMODE_EXCEPTION
Fetch dữ liệu
Bây giờ chúng ta đã tạo xong một kết nối tới cơ sở dữ liệu, hãy thử tiến hành lấy vài thông tin nào đó ra. Có 2 cách căn bản để lấy dữ liệu trong PDO đó là query và excute. Chúng ta sẽ thấy chúng ngay sau đây:
[code language=”php”]/** The Query Method
* Anti-Pattern
*/
$name = ‘Joe’; # user-supplied data
try {
$conn = new PDO(‘mysql:host=localhost;dbname=myDatabase’, $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$data = $conn->query(‘SELECT * FROM myTable WHERE name = ‘ . $conn->quote($name));
foreach($data as $row) {
print_r($row);
}
} catch(PDOException $e) {
echo ‘ERROR: ‘ . $e->getMessage();
}[/code]
Mặc dù nó làm việc, nhưng bạn lưu ý rằng chúng ta vẫn đang tự thoát dữ liệu người dùng với PDO::quote. Hãy suy nghĩ về phương pháp này tương tự như khi bạn sử dụng mysql_real_escape_string, cả 2 chuỗi sẽ escaping và báo chuỗi mà bạn vượt qua.Trong tình huống này, tôi khuyên bạn nên ràng buộc dữ liệu đầu vào thay vì sử dụng một dạng báo cáo sẵn có để tránh lỗi. Nói cách khác, nếu truy vấn của bạn không phụ thuộc vào dữ liệu mẫu, phương pháp truy vấn (query) là một lựa chọn hữu ích và làm cho quá trình vòng lặp thông qua kết quả dễ dàng như một câu lệnh foreach.
Prepared Statements
Hãy quan sát tiếp theo:
[code language=”php”]/** The Prepared Statements Method
* Best Practice
*/
$id = 5;
try {
$conn = new PDO(‘mysql:host=localhost;dbname=myDatabase’, $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare(‘SELECT * FROM myTable WHERE id = :id’);
$stmt->execute(array(‘id’ => $id));
while($row = $stmt->fetch()) {
print_r($row);
}
} catch(PDOException $e) {
echo ‘ERROR: ‘ . $e->getMessage();
}[/code]
Trong ví dụ này, chúng ta sử dụng phương pháp prepare để định nghĩa và chuẩn bị câu truy vấn trước khi dữ liệu của người dùng được đính kèm vào câu truy vấn. Với phương pháp này, SQL injection là hầu như không thể bởi vì câu truy vấn không nhận được dữ liệu của người dùng. Thay vào đó, chúng ta sử dụng thông số :id để giữ chỗ cho giá trị mà dữ liệu người dùng sẽ thế chỗ.
Tiếp theo là thay thế:
[code language=”php”] $stmt->execute(array(‘id’ => $id));[/code]Ngoài ra bạn có một phương pháp tiếp cận khác đó là sử dụng bindParam:
[code language=”php”]$stmt->bindParam(‘:id’, $id, PDO::PARAM_INT);$stmt->execute();[/code]
Chỉ định dữ liệu đầu ra
Sau khi excute(), bạn có nhiều cách để nhận dữ liệu: một mảng hoặc một đối tượng …trong ví dụ ở trên, mặc định chúng ta xuất ra dạng mảng PDO::FETCH_ASSOC;.
[code language=”php”]while($row = $stmt->fetch(PDO::FETCH_OBJ)) {print_r($row);
}[/code]
Tuy nhiên tùy chọn này có thể được ghi đè nếu cần thiết. Hãy xem các tùy chọn bạn có thể lựa chọn cho dữ liệu của mình:
– DO::FETCH_ASSOC: trả về dạng mảng (array)
– PDO::FETCH_BOTH: Trả về một mảng, key giữa tên cột và giá trị index bắt đầu từ 0
– PDO::FETCH_BOUND: Trả về TRUE và gán các giá trị của các cột trong kết quả tương ứng vào các biến bạn đã thiết lập cho nó.
– PDO::FETCH_CLASS: Trả lại một lớp(class)
– PDO::FETCH_OBJ: Trả về một đối tượng vô danh với tên tương ứng với các cột
Một vấn đề đối với đoạn mã ở trên là nó sẽ không cung cấp bất kì một thông tin phản hồi nào nếu không có bất kì một thông tin trả về nào. Chúng ta sẽ cùng chỉnh sửa điều đó:
[code language=”php”]$stmt->execute(array(‘id’ => $id));# Get array containing all of the result rows
$result = $stmt->fetchAll();
# If one or more rows were returned…
if ( count($result) ) {
foreach($result as $row) {
print_r($row);
}
} else {
echo "No rows returned.";
}[/code]
Và đoạn mã của chúng ta sẽ như thế này:
[code language=”php”]$id = 5;try {
$conn = new PDO(‘mysql:host=localhost;dbname=someDatabase’, $username, $password);
$stmt = $conn->prepare(‘SELECT * FROM myTable WHERE id = :id’);
$stmt->execute(array(‘id’ => $id));
$result = $stmt->fetchAll();
if ( count($result) ) {
foreach($result as $row) {
print_r($row);
}
} else {
echo "No rows returned.";
}
} catch(PDOException $e) {
echo ‘ERROR: ‘ . $e->getMessage();
}[/code]
Thực thi nhiều câu lệnh
PDO chứng minh mình vượt trội hơn mysql khi thực thi nhiều câu lệnh đồng thời. Hãy xem ví dụ sau:
[code language=”php”]try {$conn = new PDO(‘mysql:host=localhost;dbname=someDatabase’, $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
# Prepare the query ONCE
$stmt = $conn->prepare(‘INSERT INTO someTable VALUES(:name)’);
$stmt->bindParam(‘:name’, $name);
# First insertion
$name = ‘Keith’;
$stmt->execute();
# Second insertion
$name = ‘Steven’;
$stmt->execute();
} catch(PDOException $e) {
echo $e->getMessage();
}[/code]
Bạn chỉ prepare một lần cho câu query, sau đó bạn có thể thực thi nó hàng trăm lần với đối số truyền vào khác nhau.
CRUD
Bạn đã có cái nhìn tổng qua về PDO, bây giờ là lúc chúng ta thực hiện các lệnh CRUD(create, read, update và delete). Let’s go !
Create (Insert)
[code language=”php”]try {$pdo = new PDO(‘mysql:host=localhost;dbname=someDatabase’, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare(‘INSERT INTO someTable VALUES(:name)’);
$stmt->execute(array(
‘:name’ => ‘Justin Bieber’
));
# Affected Rows?
echo $stmt->rowCount(); // 1
} catch(PDOException $e) {
echo ‘Error: ‘ . $e->getMessage();[/code]
Update
[code language=”php”]$id = 5;$name = "Joe the Plumber";
try {
$pdo = new PDO(‘mysql:host=localhost;dbname=someDatabase’, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare(‘UPDATE someTable SET name = :name WHERE id = :id’);
$stmt->execute(array(
‘:id’ => $id,
‘:name’ => $name
));
echo $stmt->rowCount(); // 1
} catch(PDOException $e) {
echo ‘Error: ‘ . $e->getMessage();
}[/code]
Delete
[code language=”php”]$id = 5; // From a form or something similartry {
$pdo = new PDO(‘mysql:host=localhost;dbname=someDatabase’, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare(‘DELETE FROM someTable WHERE id = :id’);
$stmt->bindParam(‘:id’, $id); // this time, we’ll use the bindParam method
$stmt->execute();
echo $stmt->rowCount(); // 1
} catch(PDOException $e) {
echo ‘Error: ‘ . $e->getMessage();
}[/code]
Object Mapping
Một trong những khả năng của PDO(mysqli cũng có) đó là nó có thể giúp chúng ta lập bản đồ kết quả đến một trường hoặc một lớp, đối tượng nào đó. Sau đây là một ví dụ:
[code language=”php”]class User {public $first_name;
public $last_name;
public function full_name()
{
return $this->first_name . ‘ ‘ . $this->last_name;
}
}
try {
$pdo = new PDO(‘mysql:host=localhost;dbname=someDatabase’, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$result = $pdo->query(‘SELECT * FROM someTable’);
# Map results to object
$result->setFetchMode(PDO::FETCH_CLASS, ‘User’);
while($user = $result->fetch()) {
# Call our custom full_name method
echo $user->full_name();
}
} catch(PDOException $e) {
echo ‘Error: ‘ . $e->getMessage();
}[/code]
Suy nghĩ của tôi
Nếu bạn là một PHP Programmer và bạn đang làm việc với MySQL bằng cách sử dụng [I]mysql API[/I], tôi cho rằng bạn nên ngừng việc này lại và bắt đầu tìm hiểu về PDO API được rồi. Mặc dù mysql API không bị phản đối, nhưng mã của bạn sẽ an toàn hơn, sắp xếp hợp lý hơn nếu bạn sử dụng thành phần mở rộng PDO.
Nguồn: www.vnwebmaster.com