Doctrine 2 khác Eloquent như thế nào?

Doctrine 2 khác Eloquent như thế nào?

Một trong những điều thực sự tuyệt vời về ORM đó là thực thi [Active Record](http://en.wikipedia.org/wiki/Active_record_pattern) giống như [Eloquent](http://laravel.com/docs/eloquent), chúng thực sự trực quan và dễ sử dụng.

Với Active Record , bản chỉ có một object mà bạn có thể thao tác và save nó. Gọi save() trên một object sẽ update database, và tất cả điều đó sẽ ẩn phía sau. Như vậy, việc quản lý business logic sẽ dễ dàng hơn, bạn không cần quan tâm đến logic về phía database.

Entities are just plain old PHP

Một trong những khác biệt lớn nhất giữa Doctrine 2 và Eloquent đó là Doctrine 2 entities (thực thể) chúng chỉ là PHP quá cũ rồi, trong khi Eloquent entities kế thừa tất cả những logic bền vững của ORM. Thông thường khi bạn muốn sử dụng Eloquent, bạn sẽ viết tương tự như sau:

class User extends Eloquent {
 
}

Khi tương tác với User entity, bạn sẽ có tất cả các methods khả dụng của Eloquent bởi vì bạn đã kế thừa Eloquent. Bạn có thể lưu lại entity ở bất cứ điểm nào trong code của bạn chỉ với save() method:

$user = User::find(123);
$user->name = 'Philip Brown';
$user->save();

Với Doctrine, các entities của bạn chỉ đơn giản là PHP objects và chúng không được kế thừa bất kỳ thứ gì khi bạn sử dụng extend một ORM class ở trên đầu file:

<?php
 
use Doctrine\ORM\Mapping AS ORM;
 
/**
 * @ORM\Entity
 * @ORM\Table(name="users")
 */
class User
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;
 
    /**
     * @ORM\Column(type="string")
     */
    private $name;
}

Như bạn có thể thấy, class User chỉ là một class PHP đơn thuần. Do đó nó rất nhẹ vì nó không có những overhead của entity ORM.

Annotations within the entity

Không giống như Eloquent, Doctrine 2 entities có chú thích để mô tả các entity. Điều này có vẻ áp đảo lúc đầu, nhưng nó thực sự chỉ đơn giản là cung cấp thông tin dữ liệu Doctrine Entity của class. Bạn sẽ thấy trong ví dụ trên tôi có liệt kê chi tiết về cách thức thực thể được lưu trữ như table được gọi và kiểu của các trường được sử dụng. Trong Laravel sử dụng migration files để tạo ra database schema,. Trong Doctrine 2, database được tạo tự động từ phản ánh các dữ liệu meta trong các chú thích (annotations) của thực thể.

The Entity Manager is the boss

Doctrine 2 entities chỉ là đơn thuẩn là PHP ojbects. Điều đó có nghĩa là chúng ta không thể gọi method save() để lưu dữ liệu vào database. Doctrine 2 có một service class được biết gọi là Entity Manager. Entity Manager có trách nhiệm duy nhất đối phó với tất cả các logic bền vững (persistence). Điều đó có nghĩa là bất cứ khi nào bạn muốn lưu dữ liệu vào database trên một đối tượng, bạn phải thông qua Entity Manager với persistflush:

$user = new User;
$user->setName('Philip Brown');
 
EntityManager::persist($user);
EntityManager::flush();

Doctrine 2 update dữ liệu thông qua transactions, chứ không phải truy vấn đơn. Điều đó được gọi là Unit of Work.

What is the Unit of Work?

Một khái niệm quan trọng cần phải hiểu khi sử dụng Doctrine 2 là Unit of Work. Chú ý rằng khi chúng ta muôn thao tác với database cần sử dụng persist($user)flush(). Vậy tại sao chúng ta lại cần gọi persist() trước rồi mới đến flush() mà không phải save luôn? Doctrine 2 sử dụng chiến lược gọi là transactional write-behind tương tác với database. Điều này có nghĩa là Doctrine 2 sẽ trì hoãn tương tác với database cho đến khi flush() được gọi.

Khi bạn gọi persist($user) trên Entity Manager, Doctrine sẽ giữ theo dõi điều này và bất kỳ objects khác đang tồn tại. Khi method flush() được gọi, Doctrine 2 sẽ tự động tạo ra câu lệnh SQL update dữ liệu lên database bằng một database transaction.

The Identity Map

Một feature tốt của Doctrine 2 là The Identity Map.

$user = EntityManager::find('Cribbb\Entities\User', 1);
$user->setUsername('philipbrown');
 
$user = EntityManager::find('Cribbb\Entities\User', 1);
echo $user->getUsername();

Trong đoạn code ở trên chúng ta lấy một user trong database mà set giá trị cho thuộc tính username . Tiếp theo chung ta lại lấy user đó trong database ra và gọi thuộc tính username. Thông thường điều này sẽ gây ra nhiều truy vấn SQL với các cơ sở dữ liệu như các đối tượng đã được chuyển tới những lui. Doctrine 2 quản lý các loại kịch bản thông qua Identity Map. Doctrine 2 đủ thông minh để theo dõi các thực thể và vì vậy nó sẽ chỉ trả lại cho bạn cùng một instance trong suốt thời gian request của PHP. Điều đó có nghĩa là bạn có thể request cùng một object bằng nhiều cách khác nhau, nhưng Doctrine 2 sẽ không cần thiết phải query vào database nữa.

Getting entities from the database

Khi bạn muốn lấy một thực thể từ database sử dụng Active Record pattern, bạn chỉ cần gọi phương thức find() từ model:

$user = User::find(1);

Chúng ta cũng có thể dùng Entity Manager thay vì model:

$user = EntityManager::find('Cribbb\Entities\User', 1);

Repositories are really repositories

Chúng ta có thể dùng Repository Pattern để trả về một PHP object tiêu chuẩn thay vì instance của Eloquent model:

$user = EntityManager::getRepository('Cribbb\Entities\User')->find($id);

getRepository() method sẽ trả về một repository cho phép bạn query tới database. Đối tượng repository này có một số phương thức hữu ích để truy vấn cơ sở dữ liệu:

$users = EntityManager::getRepository('Cribbb\Entities\User')->findBy(['location' => 'UK']);
 
$users = EntityManager::getRepository('Cribbb\Entities\User')->findOneBy(['username' => 'philipbrown']);

Mỗi method trên chấp nhận một mảng các điều kiện.

Custom Repositories

Ngoài việc sử dụng các method có sẵn của repository, chúng ta có thể custom hoặc thêm vào các method mà chúng ta tự định nghĩa. Ví dụ:

class UserRepository extends EntityRepository
{
    public function getAllSuperUsers()
    {
      //
    }
}

Khi sử dụng ta chỉ việc gọi UserRepository->getAllSuperUsers() là xong.

Doctrine Query Language

Trong trường hợp query phức tạp ta có thể sử dụng trực tiếp SQL query với Doctrine 2:

$query = EntityManager::createQuery("select u from Cribbb\Entities\User u where u.karma >= 10 and u.karma <= 100");
$users = $query->getResult();