Trong Doctrine bạn có thể lấy một kết quả phức tạp từ RDBMS và chuyển chúng vào một mảng hay object mô tả cấu trúc quan hệ của bạn. Điều này được thực hiện với Doctrine Query Language. Đó là cách tốt nhất để lấy dữ liệu với ít câu truy vấn nhất.
DQL
Doctrine sử dụng DQL để lấy dữ liệu và cung cấp một
Doctrine_Query
API hoàn chỉnh để xây dựng chúng. Dưới đây bạn sẽ tìm thấy danh sách đầy đủ các phương thức được sử dụng kèm ví dụ.Query API
Các API thông dụng
Tên hàm | SQL | Nối thêm | Mô tả |
---|---|---|---|
where('u.username = ?', 'jwage') | u.username = ? | Không | Thiết lập WHERE và override các điều kiện WHERE đã có |
andWhere('u.username = ?', 'jwage') | AND u.username = ? | Có | Thêm một điều kiện vào mệnh đềWHERE với AND |
whereIn('u.id', array(1, 2, 3)) | AND u.id IN (?, ?, ?) | Có | Thêm AND IN vào điều kiện WHERE |
andWhereIn('u.id', array(1, 2, 3)) | ^ | Có | Thêm một whereIn() nữa |
orWhereIn('u.id', array(1, 2, 3)) | OR u.id IN (?, ?, ?) | Có | Thêm OR IN vào điều kiện WHERE |
whereNotIn('u.id', array(1, 2, 3)) | AND u.id NOT IN (?, ?, ?) | Có | Thêm AND NOT IN vào điều kiệnWHERE |
andWhereNotIn('u.id', array(1, 2, 3)) | ^ | Có | Thêm một whereNotIn() nữa |
orWhereNotIn('u.id', array(1, 2, 3)) | OR u.id NOT IN (?, ?, ?) | Có | Thêm OR NOT IN vào điều kiện WHERE |
orWhere('u.username = ?', 'jwage') | OR u.username = ? | Có | Thêm OR vào điều kiện WHERE |
groupBy('u.id') | GROUP BY u.id, u.username | Không | Thiêt lập GROUP BY và override cácGROUP BY đã có |
addGroupBy('u.username') | GROUP BY u.username | Có | Thêm điều kiện vào GROUP BY |
having('num_phonenumbers > 0') | HAVINGnum_phonenumbers > 0 | Không | Thiết lập HAVING và override cácHAVING đã có |
addHaving('u.username = ?', 'jwage') | HAVING u.username = ? | Có | Thêm điều kiện vào HAVING |
Select API
Tên hàm | Mô tả |
---|---|
distinct($flag = true) | Thiết lập cờ cho mỗi select |
select('u.id, u.username, COUNT(p.id) as num_phonenumbers') | Thiết lập SELECT và override các select đã có |
addSelect('u.email_address') | Add a select that is appended |
from('User u, u.Phonenumber p') | Thiết lập FROM và override các FROM và joins đã có |
leftJoin('u.Phonenumber p') | Thêm LEFT JOIN vào mệnh đề FROM |
innerJoin('u.Profile p') | Thêm INNER JOIN vào mệnh đề FROM |
addFrom('u.Phonenumber p') | Thêm điều kiện join vào mệnh đề FROM |
orderBy('u.username') | Thiết lập ORDER BY và override các ORDER BY đã có |
addOrderBy('u.is_active = ?', 1) | Thêm điều kiện vào ORDER BY |
limit(20) | Giới hạn số bản ghi nhận được ở kết quả trả về |
offset(5) | Giới hạn số offset của bản ghi |
Update API
Tên hàm | Mô tả |
---|---|
forUpdate($flag = true) | Thay đổi câu truy vấn dùng FOR UPDATE |
update('User u') | Xác định model để UPDATE |
set('u.username', '?', 'jwage') | Thiết lập giá trị mới cho câu truy vấn UPDATE . Tham số đầu tiên là dữ liệu cần thay đổi, tham số thứ 2 là biểu thức đặt trực tiếp trong DQL (có thể là ? hoặc một DBMS function), và tham số thứ 3 là giá trị mới. |
Delete API
Tên hàm | Mô tả |
---|---|
delete() | Chuyển câu truy vấn thành delete |
Tạo câu truy vấn
Tạo câu truy vấn từ Doctrine_Table instance.
$q = Doctrine::getTable('User')->createQuery('u') ->where('u.username = ?', 'jwage');
Tạo câu truy vấn theo cách bình thường
$q = Doctrine_Query::create() ->from('User u') ->where('u.username = ?', 'jwage');
2 cách truy vấn trên là như nhau, cách thứ nhất sẽ được tự động chuyển sang cách thứ 2.
Các ví dụ truy vấn
Dưới đây là ví dụ một số truy vấn để giúp bạn tìm hiểu cách lấy dữ liệu với Doctrine.
Tính số cột
When using DBMS functions to calculate columns, they are hydrated in to the component/model that is the first involved in the expression. In the example below, the model is hydrated in to the
Phonenumber
relation because it is the first component encountered in the query.$q = Doctrine_Query::create() ->select('u.*, COUNT(DISTINCT p.id) AS num_phonenumbers') ->from('User u') ->leftJoin('u.Phonenumbers p') ->groupBy('u.id'); $users = $q->fetchArray(); echo $users[0]['num_phonenumbers'];
Lấy Users và các Groups liên quan
$q = Doctrine_Query::create() ->from('User u') ->leftJoin('u.Groups g'); $users = $q->fetchArray(); foreach ($users[0]['Groups'] as $group) { echo $group['name']; }
WHERE với 1 tham số
$q = Doctrine_Query::create() ->from('User u') ->where('u.username = ?', 'jwage'); $users = $q->fetchArray();
WHERE với nhiều tham số
$q = Doctrine_Query::create() ->from('User u') ->where('u.is_active = ? AND u.is_online = ?', array(1, 1)); $users = $q->fetchArray(); // You can also optionally use the andWhere() to add to the existing where parts $q = Doctrine_Query::create() ->from('User u') ->where('u.is_active = ?', 1) ->andWhere('u.is_online = ?', 1); $users = $q->fetchArray();
whereIn()
$q = Doctrine_Query::create() ->from('User u') ->whereIn('u.id', array(1, 2, 3)); $users = $q->fetchArray(); // This is the same as above $q = Doctrine_Query::create() ->from('User u') ->where('u.id IN (1, 2, 3)'); $users = $q->fetchArray();
Dùng function của DBMS trong mệnh đề WHERE
$userEncryptedKey = 'a157a558ac00449c92294c7fab684ae0'; $q = Doctrine_Query::create() ->from('User u') ->where("MD5(CONCAT(u.username, 'secret_user_key')) = ?", $userEncryptedKey); $user = $q->fetchOne(); $q = Doctrine_Query::create() ->from('User u') ->where('LOWER(u.username) = LOWER(?)', 'jwage'); $user = $q->fetchOne();
groupBy
// Users with more than 1 phonenumber $q = Doctrine_Query::create() ->select('u.*, COUNT(DISTINCT p.id) AS num_phonenumbers') ->from('User u') ->leftJoin('u.Phonenumbers p') ->having('num_phonenumbers > 1') ->groupBy('u.id'); $users = $q->fetchArray();
Chỉ Join với primary phonenumbers sử dụng WITH
$q = Doctrine_Query::create() ->from('User u') ->leftJoin('u.Phonenumbers p WITH p.primary_num = ?', true); $users = $q->fetchArray();
Override JOIN dùng ON
$q = Doctrine_Query::create() ->from('User u') ->leftJoin('u.Phonenumbers p ON u.id = p.user_id AND p.primary_num = ?', true); $users = $q->fetchArray();
Chỉ lấy các cột cần thiết
$q = Doctrine_Query::create() ->select('u.username, p.phone') ->from('User u') ->leftJoin('u.Phonenumbers p'); $users = $q->fetchArray();
Lấy tất cả các cột
// Select all User columns but only the phone phonenumber column $q = Doctrine_Query::create() ->select('u.*, p.phone') ->from('User u') ->leftJoin('u.Phonenumbers p'); $users = $q->fetchArray();
DQL delete với WHERE
// Delete phonenumbers for user id = 5 $deleted = Doctrine_Query::create() ->delete() ->from('Phonenumber') ->andWhere('user_id = 5') ->execute();
DQL update 1 cột
// Set user id = 1 to active Doctrine_Query::create() ->update('User u') ->set('u.is_active', '?', true) ->where('u.id = ?', 1) ->execute();
DQL update với hàm của DBMS
// Make all usernames lowercase Doctrine_Query::create() ->update('User u') ->set('u.username', 'LOWER(u.username)') ->execute();
Dùng LIKE của mysql để tìm bản ghi
$q = Doctrine_Query::create() ->from('User u') ->where('u.username LIKE ?', '%jwage%'); $users = $q->fetchArray();
Dùng INDEXBY để xác định key cho mảng trả về
$q = Doctrine_Query::create() ->from('User u INDEXBY u.username'); $users = $q->fetchArray(); print_r($users['jwage']); // Will print the user with the username of jwage
Dùng positional & named parameter
// Positional parameters $q = Doctrine_Query::create() ->from('User u') ->where('u.username = ?', array('Arnold')); $users = $q->fetchArray(); // Named parameters $q = Doctrine_Query::create() ->from('User u') ->where('u.username = :username', array(':username' => 'Arnold')); $users = $q->fetchArray();
Dùng subquery trong mệnh đề WHERE
// Find uers not in group named Group 2 $q = Doctrine_Query::create() ->from('User u') ->where('u.id NOT IN (SELECT u.id FROM User u2 INNER JOIN u2.Groups g WHERE g.name = ?)', 'Group 2'); $users = $q->fetchArray(); // You can accomplish this without subqueries like the 2 below // This is similar as above $q = Doctrine_Query::create() ->from('User u') ->innerJoin('u.Groups g WITH g.name != ?', 'Group 2') $users = $q->fetchArray(); // or this $q = Doctrine_Query::create() ->from('User u') ->leftJoin('u.Groups g') ->where('g.name != ?', 'Group 2'); $users = $q->fetchArray();
Doctrine có nhiều cách khác nhau đề thực thi câu truy vấn và trả về dữ liệu. Dưới đây là danh sách tất cả các cách để thực thi câu truy vấn.
$q = Doctrine_Query::create() ->from('User u'); // Array hydration $users = $q->fetchArray(); // Fetch the results as a hydrated array $users = $q->execute(array(), Doctrine::HYDRATE_ARRAY); // This is the same as above $users = $q->setHydrationMode(Doctrine::HYDRATE_ARRAY)->execute(); // So is this // No hydration $users = $q->execute(array(), Doctrine::HYDRATE_NONE); // Execute the query with plain PDO and no hydration $users = $q->setHydrationMode(Doctrine::HYDRATE_NONE)->execute(); // This is the same as above // Fetch one $user = $q->fetchOne(); // Fetch all and get the first from collection $user = $q->execute()->getFirst();
Finders
Doctrine cung cấp một số phương thức finder đơn giản, tự động tạo Doctrine_Query object. Dưới đây là một số ví dụ cách sử dụng những phương thức này.
Phương thức Find By
Bạn có thể sử dụng
findBy*()
và findOneBy*()
để tìm bản ghi theo giá trị của một trường nào đó.$user = Doctrine::getTable('User')->findOneByUsername('jwage'); $users = Doctrine::getTable('User')->findByIsActive(1);
Tìm theo khóa chính
Phương thức Doctrine_Table::find() giúp tìm bản ghi theo khóa chính. Nó làm việc với cả model có khóa chính hỗn hợp.
$user = Doctrine::getTable('User')->find(1); $userGroup = Doctrine::getTable('UserGroup')->find(array(1, 2));
Cập nhât dữ liệu
Với Doctrine bạn có thể cập nhật dữ liệu bằng cách sử dụng trực tiếp DQL update và delete hoặc bạn có thể lấy các object, sửa đổi các property của nó rồi lưu lại. Dưới đây là ví dụ theo các cách trên.
Các property của Object
Doctrine hỗ trợ 3 cách để sửa đổi properties của object và sfDoctrinePlugin thêm một cách thứ 4 (mô phỏng propel). Đó là object access, array access, function access và propel style access.
$user = new User(); $user->username = 'jwage'; // Object $user['username'] = 'jwage'; // Array $user->set('username', 'jwage'); // Function $user->setUsername('jwage'); // Propel access $user->save();
Overriding Accessors & Mutators
class User extends BaseUser { public function setPassword($password) { return $this->_set('password', md5($password)); } public function getUsername() { return 'PREFIX_' . $this->_get('username'); } } $user = new User(); $user->username = 'jwage'; $user->password = 'changeme'; // Invokes setPassword() echo $user->username; // Invokes getPassword() and returns PREFIX_jwage
Dữ liệu quan hệ
Với Doctrine thật dễ dàng để thao tác với dữ liệu thông qua PHP objects.
User hasOne Profile
$user = new User(); $user->username = 'jwage'; $user->password = 'changeme'; $user->Profile->name = 'Jonathan H. Wage'; $user->Profile->about = 'His name is Jonathan'; $user->save();
User hasMany Phonenumber as Phonenumbers
$user = new User(); $user->username = 'jwage'; $user->password = 'changeme'; $user->Phonenumbers[]->phonenumber = '6155139185'; $user->Phonenumbers[]->phonenumber = '1234567890'; $phonenumber = $user->Phonenumbers[2]; $phonenumber->phonenumber = '0987654321';
BlogPost hasMany Tag as Tags
$blogPost = new BlogPost(); $blogPost->title = 'Test blog post'; $blogPost->body = 'This is the content of the test blog post'; $tag = Doctrine::getTable('Tag')->findOneByName('doctrine'); if ( ! $tag) { $tag = new Tag; $tag->name = 'doctrine'; } $blogPost->Tags[] = $tag;
Code trên chưa được tốt lắm, chúng ta có thể thay đổi lại bằng cách thêm vào lớp
TagTable
ởlib/model/doctrine/TagTable.class.php
.class TagTable extends Doctrine_Table { public function findOneByName($name) { $tag = $this->findOneBy('name', $name); if ( ! $tag) { $tag = new Tag(); $tag->name = $name; } return $tag; } }
Code bên trên trở thành.
$blogPost = new BlogPost(); $blogPost->title = 'Test blog post'; $blogPost->body = 'This is the content of the test blog post'; $tag = Doctrine::getTable('Tag')->findOneByName('doctrine'); $blogPost->Tags[] = $tag;
Chúng ta cũng nên override
Tag
name
mutator bằng cách tạo phương thức setName()
trong lớp Tag
ởlib/model/doctrine/Tag.class.php
.class Tag extends BaseTag { public function setName($name) { $tag = Doctrine::getTable('Tag')->findOneByName($name); if ($tag) { $this->assignIdentifier($tag->identifier()); } else { $this->_set('name', $name); } } }
Bây giờ code trở nên đơn giản hơn để chắc rằng ta ko lưu vào database các tag giống nhau.
$blogPost = new BlogPost(); $blogPost->title = 'Test blog post'; $blogPost->body = 'This is the content of the test blog post'; $blogPost->Tags[]->name = 'doctrine';
Xóa dữ liệu
Có 2 cách để xóa dữ liệu. Lấy object rồi gọi phương thức
Doctrine_Record::delete()
hoặc thực hiện câu truy vấn DQL delete.$user = Doctrine::getTable('User')->find(1); $user->delete();
Sử dụng câu truy vấn DQL delete hiệu quả hơn do chỉ sử dụng một câu truy vấn.
$deleted = Doctrine_Query::create() ->delete() ->from('User u') ->where('u.id = ?', 1) ->execute();
Bạn có thể tìm hiểu thêm về cách tương tác với dữ liệu trong Doctrine Manual ở đây.