Proxy trong Doctrine 2 là gì?


112

Tôi vừa đọc xong tất cả tài liệu của Doctrine 2, tôi bắt đầu sandbox của riêng mình, tôi hiểu hầu hết các nguyên tắc, nhưng vẫn còn một câu hỏi và tôi không thể tìm thấy bất kỳ lời giải thích đầy đủ nào trong tài liệu.

  1. Các Proxylớp học là gì?
  2. Khi nào tôi nên sử dụng chúng trên các thực thể?

Theo như tôi hiểu, các lớp proxy thêm một lớp để cho phép bạn thêm một số tính năng khác vào các thực thể của mình, nhưng tại sao lại sử dụng proxy thay vì triển khai chính các phương thức trong lớp thực thể?

Câu trả lời:


160

CẬP NHẬT

Câu trả lời này chứa thông tin sai về sự khác biệt giữa các đối tượng proxy và các đối tượng một phần. Xem câu trả lời của @ Kontrollfreak để biết thêm chi tiết: https://stackoverflow.com/a/17787070/252591


Đối tượng proxy được sử dụng bất cứ khi nào truy vấn của bạn không trả về tất cả dữ liệu cần thiết để tạo một thực thể. Hãy tưởng tượng tình huống sau:

@Entity
class User {
     @Column protected $id;
     @Column protected $username;
     @Column protected $firstname;
     @Column protected $lastname;

     // bunch of setters/getters here
}

DQL query:

SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id

Như bạn có thể thấy, truy vấn này không trả về firstnamelastnamethuộc tính, do đó bạn không thể tạo Userđối tượng. Việc tạo thực thể không hoàn chỉnh có thể dẫn đến lỗi không mong muốn.

Đó là lý do tại sao Doctrine sẽ tạo UserProxyđối tượng hỗ trợ tải lười biếng. Khi bạn cố gắng truy cập thuộc firstnametính (chưa được tải), trước tiên nó sẽ tải giá trị đó từ cơ sở dữ liệu.


Ý tôi là tại sao tôi nên sử dụng proxy?

Bạn nên luôn viết mã của mình như thể bạn không sử dụng các đối tượng proxy. Chúng có thể được coi là các đối tượng bên trong được Doctrine sử dụng.

Tại sao việc tải chậm không thể được thực hiện trong chính Entitiy?

Về mặt kỹ thuật, nó có thể là nhưng hãy xem một số lớp của đối tượng proxy ngẫu nhiên. Nó chứa đầy mã bẩn, ugh. Thật tuyệt khi có một mã sạch trong các thực thể của bạn.

Bạn có thể cung cấp cho tôi một trường hợp sử dụng?

Bạn đang hiển thị danh sách 25 bài báo mới nhất và bạn muốn hiển thị chi tiết của bài báo đầu tiên. Mỗi người trong số họ chứa một lượng lớn văn bản, vì vậy việc tìm nạp tất cả dữ liệu đó sẽ rất lãng phí bộ nhớ. Đó là lý do tại sao bạn không tìm nạp dữ liệu không cần thiết.

SELECT a.title, a.createdAt
FROM Entity\Article a
ORDER BY a.createdAt DESC
LIMIT 25

$isFirst = true;
foreach ($articles as $article) {
    echo $article->getTitle();
    echo $article->getCreatedAt();

    if ($isFirst) {
        echo $article->getContent(); // Article::content is not loaded so it is transparently loaded 
                                     // for this single article.

        $isFirst = false;
    }
}

Cảm ơn câu trả lời của bạn, nó khác gì với Đối tượng một phần? Ý tôi là tại sao tôi nên sử dụng proxy? Tại sao việc tải chậm không thể được thực hiện trong chính Entitiy? Bạn có thể cung cấp cho tôi một trường hợp sử dụng?
Jérémy

1
Các đối tượng một phần và các đối tượng proxy đều giống nhau - chúng có thể được coi là từ đồng nghĩa. Đối với phần còn lại của câu hỏi, hãy kiểm tra câu trả lời cập nhật của tôi.
Crozin

1
Tôi không hiểu tại sao học thuyết không thể tạo đối tượng nếu nó chỉ có một nửa các thuộc tính. Trong php, tôi có thể tạo một đối tượng ngay cả khi tôi không đặt tất cả các thuộc tính.
sanders

1
Đây là một câu trả lời hoàn toàn tuyệt vời và nên có trong tài liệu.
Jimbo

7
Câu trả lời này chứa một số quan niệm sai lầm nghiêm trọng về proxy và các đối tượng một phần. Xem câu trả lời của tôi để hiểu tại sao.
Kontrollfreak

81

Proxy

Proxy Doctrine chỉ là một trình bao bọc mở rộng một lớp thực thể để cung cấp Lazy Loading cho nó.

Theo mặc định, khi bạn yêu cầu Trình quản lý thực thể cung cấp một thực thể được liên kết với một thực thể khác, thực thể được liên kết sẽ không được tải từ cơ sở dữ liệu mà được gói vào một đối tượng proxy. Khi ứng dụng của bạn yêu cầu một thuộc tính hoặc gọi một phương thức của thực thể được ủy quyền này, Doctrine sẽ tải thực thể từ cơ sở dữ liệu (ngoại trừ khi bạn yêu cầu ID mà proxy luôn biết).

Điều này xảy ra hoàn toàn minh bạch đối với ứng dụng của bạn do proxy mở rộng lớp thực thể của bạn.

Theo mặc định, Doctrine sẽ hydrate hóa các liên kết dưới dạng các proxy tải chậm nếu bạn không có JOINchúng trong truy vấn của mình hoặc đặt chế độ tìm nạp thành EAGER.


Bây giờ tôi phải thêm điều này vì tôi không có đủ danh tiếng để bình luận ở khắp mọi nơi:

Thật không may, câu trả lời của Crozin chứa thông tin sai lệch.

Nếu bạn thực hiện một truy vấn DQL như

SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id

bạn sẽ không nhận được một đối tượng thực thể (được ủy quyền) mà là một mảng liên kết. Vì vậy, không thể lười tải bất kỳ thuộc tính bổ sung nào.

Với suy nghĩ này, người ta đi đến kết luận rằng ví dụ trường hợp sử dụng cũng sẽ không hoạt động. DQL sẽ phải được thay đổi thành một cái gì đó như thế này để truy cập $articledưới dạng đối tượng:

SELECT a FROM Entity\Article a ORDER BY a.createdAt DESC LIMIT 25

Và thuộc tính được trả về getContent()sẽ phải là một liên kết để không tải các thuộc tính nội dung của tất cả 25 thực thể.


Đối tượng một phần

Nếu bạn muốn tải một phần thuộc tính thực thể không phải là liên kết, bạn phải nói rõ ràng với Doctrine này:

SELECT partial u.{id, username} FROM Entity\User u WHERE u.id = :id

Điều này cung cấp cho bạn một đối tượng thực thể được tải một phần.

Nhưng hãy cẩn thận rằng các đối tượng một phần không phải là proxy! Lazy Loading không áp dụng cho họ. Do đó, việc sử dụng các đồ vật một phần nói chung là nguy hiểm và nên tránh. Đọc thêm: Các Đối tượng Một phần - Tài liệu Giáo lý 2 ORM 2


1
Cảm ơn, điều này cung cấp nhiều chi tiết hơn về cách Doctrine sử dụng proxy và các đối tượng một phần hơn là câu trả lời được chấp nhận! Và việc tham khảo các tài liệu cũng rất hữu ích.
Sean the Bean

1
Ngoài ra để tham khảo, đây là phần tài liệu về các đối tượng Proxy: doct-orm.readthedocs.org/en/latest/reference/…
Sean the Bean,

Vì vậy, khi thực hiện một tải háo hức, về cơ bản nó chỉ là thêm các tập kết quả?
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.