Doctrine2: Cách tốt nhất để xử lý nhiều-nhiều với các cột bổ sung trong bảng tham chiếu


282

Tôi đang tự hỏi đâu là cách tốt nhất, sạch nhất và đơn giản nhất để làm việc với các mối quan hệ nhiều-nhiều trong Doctrine2.

Giả sử rằng chúng ta đã có một album như Master of Puppets của Metallica với nhiều bản nhạc. Nhưng xin lưu ý thực tế là một bản nhạc có thể xuất hiện trong nhiều album hơn, như Battery by Metallica - ba album được trình bày bản nhạc này.

Vì vậy, điều tôi cần là mối quan hệ nhiều-nhiều giữa album và bản nhạc, sử dụng bảng thứ ba với một số cột bổ sung (như vị trí của bản nhạc trong album được chỉ định). Trên thực tế tôi phải sử dụng, như tài liệu của Doctrine gợi ý, mối quan hệ một-nhiều-nhiều để đạt được chức năng đó.

/** @Entity() */
class Album {
    /** @Id @Column(type="integer") */
    protected $id;

    /** @Column() */
    protected $title;

    /** @OneToMany(targetEntity="AlbumTrackReference", mappedBy="album") */
    protected $tracklist;

    public function __construct() {
        $this->tracklist = new \Doctrine\Common\Collections\ArrayCollection();
    }

    public function getTitle() {
        return $this->title;
    }

    public function getTracklist() {
        return $this->tracklist->toArray();
    }
}

/** @Entity() */
class Track {
    /** @Id @Column(type="integer") */
    protected $id;

    /** @Column() */
    protected $title;

    /** @Column(type="time") */
    protected $duration;

    /** @OneToMany(targetEntity="AlbumTrackReference", mappedBy="track") */
    protected $albumsFeaturingThisTrack; // btw: any idea how to name this relation? :)

    public function getTitle() {
        return $this->title;
    }

    public function getDuration() {
        return $this->duration;
    }
}

/** @Entity() */
class AlbumTrackReference {
    /** @Id @Column(type="integer") */
    protected $id;

    /** @ManyToOne(targetEntity="Album", inversedBy="tracklist") */
    protected $album;

    /** @ManyToOne(targetEntity="Track", inversedBy="albumsFeaturingThisTrack") */
    protected $track;

    /** @Column(type="integer") */
    protected $position;

    /** @Column(type="boolean") */
    protected $isPromoted;

    public function getPosition() {
        return $this->position;
    }

    public function isPromoted() {
        return $this->isPromoted;
    }

    public function getAlbum() {
        return $this->album;
    }

    public function getTrack() {
        return $this->track;
    }
}

Dữ liệu mẫu:

             Album
+----+--------------------------+
| id | title                    |
+----+--------------------------+
|  1 | Master of Puppets        |
|  2 | The Metallica Collection |
+----+--------------------------+

               Track
+----+----------------------+----------+
| id | title                | duration |
+----+----------------------+----------+
|  1 | Battery              | 00:05:13 |
|  2 | Nothing Else Matters | 00:06:29 |
|  3 | Damage Inc.          | 00:05:33 |
+----+----------------------+----------+

              AlbumTrackReference
+----+----------+----------+----------+------------+
| id | album_id | track_id | position | isPromoted |
+----+----------+----------+----------+------------+
|  1 |        1 |        2 |        2 |          1 |
|  2 |        1 |        3 |        1 |          0 |
|  3 |        1 |        1 |        3 |          0 |
|  4 |        2 |        2 |        1 |          0 |
+----+----------+----------+----------+------------+

Bây giờ tôi có thể hiển thị danh sách các album và bản nhạc liên quan đến chúng:

$dql = '
    SELECT   a, tl, t
    FROM     Entity\Album a
    JOIN     a.tracklist tl
    JOIN     tl.track t
    ORDER BY tl.position ASC
';

$albums = $em->createQuery($dql)->getResult();

foreach ($albums as $album) {
    echo $album->getTitle() . PHP_EOL;

    foreach ($album->getTracklist() as $track) {
        echo sprintf("\t#%d - %-20s (%s) %s\n", 
            $track->getPosition(),
            $track->getTrack()->getTitle(),
            $track->getTrack()->getDuration()->format('H:i:s'),
            $track->isPromoted() ? ' - PROMOTED!' : ''
        );
    }   
}

Kết quả là những gì tôi mong đợi, tức là: một danh sách các album với các bài hát của họ theo thứ tự phù hợp và những album được quảng cáo sẽ được đánh dấu là quảng cáo.

The Metallica Collection
    #1 - Nothing Else Matters (00:06:29) 
Master of Puppets
    #1 - Damage Inc.          (00:05:33) 
    #2 - Nothing Else Matters (00:06:29)  - PROMOTED!
    #3 - Battery              (00:05:13) 

Vì vậy những gì là sai?

Mã này cho thấy những gì sai:

foreach ($album->getTracklist() as $track) {
    echo $track->getTrack()->getTitle();
}

Album::getTracklist()trả về một mảng các AlbumTrackReferenceđối tượng thay vì các Trackđối tượng. Tôi không thể tạo phương thức proxy gây ra điều gì nếu cả hai AlbumTracksẽ có getTitle()phương thức? Tôi có thể thực hiện một số xử lý bổ sung trong Album::getTracklist()phương thức nhưng cách đơn giản nhất để làm điều đó là gì? Tôi có bị buộc phải viết một cái gì đó như thế không?

public function getTracklist() {
    $tracklist = array();

    foreach ($this->tracklist as $key => $trackReference) {
        $tracklist[$key] = $trackReference->getTrack();

        $tracklist[$key]->setPosition($trackReference->getPosition());
        $tracklist[$key]->setPromoted($trackReference->isPromoted());
    }

    return $tracklist;
}

// And some extra getters/setters in Track class

BIÊN TẬP

@beberlei đề xuất sử dụng các phương thức proxy:

class AlbumTrackReference {
    public function getTitle() {
        return $this->getTrack()->getTitle()
    }
}

Đó sẽ là một ý tưởng tốt nhưng tôi đang sử dụng "đối tượng tham chiếu" đó từ cả hai phía: $album->getTracklist()[12]->getTitle()$track->getAlbums()[1]->getTitle(), vì vậy getTitle()phương thức sẽ trả về dữ liệu khác nhau dựa trên bối cảnh của lệnh gọi.

Tôi sẽ phải làm một cái gì đó như:

 getTracklist() {
     foreach ($this->tracklist as $trackRef) { $trackRef->setContext($this); }
 }

 // ....

 getAlbums() {
     foreach ($this->tracklist as $trackRef) { $trackRef->setContext($this); }
 }

 // ...

 AlbumTrackRef::getTitle() {
      return $this->{$this->context}->getTitle();
 }

Và đó không phải là một cách rất sạch sẽ.


2
Làm thế nào để bạn xử lý AlbumTrackReference? Ví dụ $ album-> addTrack () hoặc $ album-> removeTrack ()?
Daniel

Tôi không hiểu bạn nhận xét về bối cảnh. Theo ý kiến ​​của tôi, dữ liệu không phụ thuộc vào ngữ cảnh. Giới thiệu $album->getTracklist()[12]AlbumTrackRefđối tượng, vì vậy $album->getTracklist()[12]->getTitle()sẽ luôn trả về tiêu đề của bản nhạc (nếu bạn đang sử dụng phương thức proxy). Trong khi $track->getAlbums()[1]Albumđối tượng, vì vậy $track->getAlbums()[1]->getTitle()sẽ luôn luôn trả lại tiêu đề của album.
Vinícius Fagundes

Một ý tưởng khác là sử dụng trên AlbumTrackReferencehai phương thức proxy getTrackTitle()getAlbumTitle.
Vinícius Fagundes

Câu trả lời:


158

Tôi đã mở một câu hỏi tương tự trong danh sách gửi thư của người dùng Doctrine và nhận được câu trả lời thực sự đơn giản;

xem xét nhiều mối quan hệ như là một thực thể, và sau đó bạn nhận ra rằng bạn có 3 đối tượng, được liên kết giữa chúng với mối quan hệ một-nhiều và nhiều-một.

http://groups.google.com/group/doctrine-user/browse_thread/thread/d1d87c96052e76f7/436b896e83c10868#436b896e83c10868

Khi một mối quan hệ có dữ liệu, nó không còn là mối quan hệ nữa!


Có ai biết làm thế nào tôi có thể lấy công cụ dòng lệnh học thuyết để tạo thực thể mới này dưới dạng tệp lược đồ yml không? Lệnh này: app/console doctrine:mapping:import AppBundle ymlvẫn tạo nhiều mối quan hệ ToMany cho hai bảng ban đầu và chỉ cần bỏ qua bảng thứ ba thay vì xem nó như một thực thể:/
Stphane 18/03/2015

sự khác biệt giữa foreach ($album->getTracklist() as $track) { echo $track->getTrack()->getTitle(); }được cung cấp bởi @Crozin và là consider the relationship as an entitygì? Tôi nghĩ những gì anh ấy muốn hỏi là làm thế nào để bỏ qua thực thể quan hệ và lấy lại tiêu đề của một bản nhạc bằng cách sử dụngforeach ($album->getTracklist() as $track) { echo $track->getTitle(); }
gấu trúc

6
"Một khi mối quan hệ có dữ liệu, nó không còn là mối quan hệ nữa" Điều này thực sự đã khai sáng. Tôi không thể nghĩ về mối quan hệ từ góc độ thực thể!
Hành tây

Điều gì sẽ xảy ra nếu mối quan hệ đã được tạo và sử dụng như nhiều đối với nhiều người. Chúng tôi nhận ra rằng chúng tôi cần thêm các trường trong nhiều đến nhiều vì vậy chúng tôi đã tạo ra một thực thể khác. Vấn đề là, với dữ liệu hiện có và một bảng hiện có cùng tên, dường như không muốn làm bạn. Có ai đã thử điều này trước đây?
chế độ chuyên chế

Tuy nhiên, đối với những người thắc mắc: tạo một Thực thể với nhiều đối tượng (đã tồn tại) khi bảng của nó hoạt động, tuy nhiên, các thực thể giữ nhiều-nhiều-nhiều phải được điều chỉnh thay vì một-nhiều cho thực thể mới. cũng giao diện với bên ngoài (getters / setters cho nhiều người trước đây nhiều khả năng phải được điều chỉnh.
Jakumi

17

Từ $ album-> getTrackList () bạn sẽ nhận được các thực thể "AlbumTrackReference", vậy còn việc thêm các phương thức từ Track và proxy thì sao?

class AlbumTrackReference
{
    public function getTitle()
    {
        return $this->getTrack()->getTitle();
    }

    public function getDuration()
    {
        return $this->getTrack()->getDuration();
    }
}

Bằng cách này, vòng lặp của bạn đơn giản hóa đáng kể, cũng như tất cả các mã khác liên quan đến việc lặp các bản nhạc của album, vì tất cả các phương thức chỉ được ủy quyền trong AlbumTrakcReference:

foreach ($album->getTracklist() as $track) {
    echo sprintf("\t#%d - %-20s (%s) %s\n", 
        $track->getPosition(),
        $track->getTitle(),
        $track->getDuration()->format('H:i:s'),
        $track->isPromoted() ? ' - PROMOTED!' : ''
    );
}

Btw Bạn nên đổi tên AlbumTrackReference (ví dụ "AlbumTrack"). Nó rõ ràng không chỉ là một tài liệu tham khảo, mà còn chứa logic bổ sung. Vì có thể cũng có các Bản nhạc không được kết nối với album nhưng chỉ có sẵn thông qua quảng cáo-cd hoặc thứ gì đó cho phép phân tách rõ ràng hơn.


1
Các phương thức proxy không giải quyết được vấn đề trong 100% (kiểm tra chỉnh sửa của tôi). Btw You should rename the AlbumT(...)- điểm tốt
Crozin

3
Tại sao bạn không có hai phương pháp? getAlbumTitle () và getTrackTitle () trên đối tượng AlbumTrackReference? Cả hai proxy cho các tiểu dự án tương ứng của họ.
beberlei

Mục tiêu là API đối tượng tự nhiên nhất . $album->getTracklist()[1]->getTrackTitle()là tốt / xấu như $album->getTracklist()[1]->getTrack()->getTitle(). Tuy nhiên, dường như tôi phải có hai lớp khác nhau: một lớp cho album-> tài liệu tham khảo theo dõi và lớp khác cho tài liệu theo dõi-> album - và điều đó quá khó để thực hiện. Vì vậy, có lẽ đó là giải pháp tốt nhất cho đến nay ...
Crozin

13

Không có gì đánh bại một ví dụ tốt đẹp

Đối với những người đang tìm kiếm một ví dụ mã hóa sạch của một liên kết một-nhiều / nhiều-một giữa 3 lớp tham gia để lưu trữ các thuộc tính bổ sung trong mối quan hệ, hãy kiểm tra trang web này:

ví dụ hay về sự liên kết một-nhiều / nhiều-một giữa 3 lớp tham gia

Hãy suy nghĩ về các khóa chính của bạn

Cũng nghĩ về khóa chính của bạn. Bạn thường có thể sử dụng các phím tổng hợp cho các mối quan hệ như thế này. Học thuyết thực sự hỗ trợ này. Bạn có thể làm cho các thực thể được tham chiếu của bạn thành id. Kiểm tra tài liệu về các phím tổng hợp tại đây


10

Tôi nghĩ rằng tôi sẽ đi với đề xuất của @ beberlei về việc sử dụng các phương thức proxy. Những gì bạn có thể làm để làm cho quá trình này đơn giản hơn là xác định hai giao diện:

interface AlbumInterface {
    public function getAlbumTitle();
    public function getTracklist();
}

interface TrackInterface {
    public function getTrackTitle();
    public function getTrackDuration();
}

Sau đó, cả bạn Albumvà bạn Trackđều có thể thực hiện chúng, trong khi AlbumTrackReferencevẫn có thể thực hiện cả hai, như sau:

class Album implements AlbumInterface {
    // implementation
}

class Track implements TrackInterface {
    // implementation
}

/** @Entity whatever */
class AlbumTrackReference implements AlbumInterface, TrackInterface
{
    public function getTrackTitle()
    {
        return $this->track->getTrackTitle();
    }

    public function getTrackDuration()
    {
        return $this->track->getTrackDuration();
    }

    public function getAlbumTitle()
    {
        return $this->album->getAlbumTitle();
    }

    public function getTrackList()
    {
        return $this->album->getTrackList();
    }
}

Bằng cách này, bằng cách loại bỏ logic của bạn đang trực tiếp tham chiếu một Trackhoặc một Album, và chỉ cần thay thế nó để nó sử dụng một TrackInterfacehoặc AlbumInterface, bạn có thể sử dụng của bạn AlbumTrackReferencetrong mọi trường hợp có thể. Những gì bạn sẽ cần là để phân biệt các phương thức giữa các giao diện một chút.

Điều này sẽ không phân biệt DQL và logic Kho lưu trữ, nhưng các dịch vụ của bạn sẽ bỏ qua thực tế là bạn đang chuyển một Albumhoặc một AlbumTrackReferencehoặc một Trackhoặc AlbumTrackReferencevì bạn đã ẩn mọi thứ đằng sau một giao diện :)

Hi vọng điêu nay co ich!


7

Đầu tiên, tôi chủ yếu đồng ý với beberlei về đề xuất của anh ấy. Tuy nhiên, bạn có thể đang tự thiết kế thành một cái bẫy. Tên miền của bạn dường như đang xem tiêu đề là khóa tự nhiên cho một bản nhạc, có khả năng là trường hợp cho 99% các tình huống bạn gặp phải. Tuy nhiên, điều gì sẽ xảy ra nếu Pin trên Master of the Puppets là một phiên bản khác (độ dài khác nhau, trực tiếp, âm thanh, phối lại, được làm lại, v.v.) so với phiên bản trên Bộ sưu tập Metallica .

Tùy thuộc vào cách bạn muốn xử lý (hoặc bỏ qua) trường hợp đó, bạn có thể đi tuyến đường được đề xuất của beberlei hoặc chỉ đi với logic bổ sung được đề xuất của bạn trong Album :: getTracklist (). Cá nhân, tôi nghĩ rằng logic bổ sung là hợp lý để giữ cho API của bạn sạch sẽ, nhưng cả hai đều có giá trị của chúng.

Nếu bạn muốn điều chỉnh trường hợp sử dụng của tôi, bạn có thể có các Bản nhạc chứa OneToMany tự tham chiếu đến các Bản nhạc khác, có thể là $ sameTracks. Trong trường hợp này, sẽ có hai thực thể cho Pin theo dõi , một cho Bộ sưu tập Metallica và một cho Master of the Puppets . Sau đó, mỗi thực thể Theo dõi tương tự sẽ chứa một tham chiếu cho nhau. Ngoài ra, điều đó sẽ thoát khỏi lớp AlbumTrackReference hiện tại và loại bỏ "vấn đề" hiện tại của bạn. Tôi đồng ý rằng nó chỉ chuyển sự phức tạp sang một điểm khác, nhưng nó có thể xử lý một usecase mà trước đây nó không thể.


6

Bạn yêu cầu "cách tốt nhất" nhưng không có cách nào tốt nhất. Có nhiều cách và bạn đã phát hiện ra một số trong số họ. Cách bạn muốn quản lý và / hoặc đóng gói quản lý hiệp hội khi sử dụng các lớp kết hợp hoàn toàn phụ thuộc vào bạn và miền cụ thể của bạn, không ai có thể chỉ cho bạn một "cách tốt nhất" mà tôi sợ.

Ngoài ra, câu hỏi có thể được đơn giản hóa rất nhiều bằng cách loại bỏ Học thuyết và cơ sở dữ liệu quan hệ khỏi phương trình. Bản chất của câu hỏi của bạn tập trung vào một câu hỏi về cách đối phó với các lớp kết hợp trong OOP đơn giản.


6

Tôi đã nhận được từ một cuộc xung đột với bảng tham gia được xác định trong một lớp kết hợp (với các trường tùy chỉnh bổ sung) và một bảng tham gia được xác định trong một chú thích nhiều-nhiều.

Các định nghĩa ánh xạ trong hai thực thể có mối quan hệ nhiều-nhiều trực tiếp xuất hiện dẫn đến việc tạo tự động bảng tham gia bằng cách sử dụng chú thích 'tham gia'. Tuy nhiên, bảng tham gia đã được xác định bởi một chú thích trong lớp thực thể bên dưới của nó và tôi muốn nó sử dụng các định nghĩa trường riêng của lớp thực thể liên kết này để mở rộng bảng tham gia với các trường tùy chỉnh bổ sung.

Giải thích và giải pháp là được xác định bởi FMaz008 ở trên. Trong tình huống của tôi, đó là nhờ bài đăng này trong diễn đàn ' Câu hỏi chú thích học thuyết '. Bài đăng này thu hút sự chú ý đến tài liệu Học thuyết liên quan đến nhiều mối quan hệ định hướng của UniToMany . Nhìn vào ghi chú liên quan đến cách tiếp cận sử dụng 'lớp thực thể liên kết', do đó thay thế ánh xạ chú thích nhiều-nhiều trực tiếp giữa hai lớp thực thể chính bằng chú thích một-nhiều trong các lớp thực thể chính và hai 'nhiều-nhiều -one 'chú thích trong lớp thực thể kết hợp. Có một ví dụ được cung cấp trong diễn đàn này mô hình Hiệp hội với các trường bổ sung :

public class Person {

  /** @OneToMany(targetEntity="AssignedItems", mappedBy="person") */
  private $assignedItems;

}

public class Items {

    /** @OneToMany(targetEntity="AssignedItems", mappedBy="item") */
    private $assignedPeople;
}

public class AssignedItems {

    /** @ManyToOne(targetEntity="Person")
    * @JoinColumn(name="person_id", referencedColumnName="id")
    */
private $person;

    /** @ManyToOne(targetEntity="Item")
    * @JoinColumn(name="item_id", referencedColumnName="id")
    */
private $item;

}

3

Đây là ví dụ thực sự hữu ích. Nó thiếu trong học thuyết tài liệu 2.

Rất cảm ơn bạn.

Đối với các chức năng proxy có thể được thực hiện:

class AlbumTrack extends AlbumTrackAbstract {
   ... proxy method.
   function getTitle() {} 
}

class TrackAlbum extends AlbumTrackAbstract {
   ... proxy method.
   function getTitle() {}
}

class AlbumTrackAbstract {
   private $id;
   ....
}

/** @OneToMany(targetEntity="TrackAlbum", mappedBy="album") */
protected $tracklist;

/** @OneToMany(targetEntity="AlbumTrack", mappedBy="track") */
protected $albumsFeaturingThisTrack;

3

Những gì bạn đang đề cập đến là siêu dữ liệu, dữ liệu về dữ liệu. Tôi đã có vấn đề tương tự cho dự án mà tôi hiện đang làm và phải dành thời gian cố gắng để tìm ra nó. Có quá nhiều thông tin để đăng ở đây, nhưng dưới đây là hai liên kết bạn có thể thấy hữu ích. Họ tham chiếu khung Symfony, nhưng dựa trên ORM của Học thuyết.

http://melikedev.com/2010/04/06/symfony-saving-metadata-during-form-save-sort-ids/

http://melikedev.com/2009/12/09/symfony-w-doctrine-saving-many-to-many-mm-relationships/

Chúc may mắn, và tài liệu tham khảo tốt đẹp của Metallica!


3

Giải pháp là trong tài liệu của Học thuyết. Trong FAQ bạn có thể thấy điều này:

http://docs.doctrine-project.org/en/2.1/reference/faq.html#how-can-i-add-columns-to-a-many-to-many-table

Và hướng dẫn ở đây:

http://docs.doctrine-project.org/en/2.1/tutorials/composite-primary-keys.html

Vì vậy, bạn không làm nữa manyToManynhưng bạn phải tạo thêm một Thực thể và đặt manyToOnevào hai thực thể của mình.

THÊM cho bình luận @ f00bar:

Thật đơn giản, bạn chỉ cần làm một cái gì đó như thế này:

Article  1--N  ArticleTag  N--1  Tag

Vì vậy, bạn tạo một thực thể ArticleTag

ArticleTag:
  type: entity
  id:
    id:
      type: integer
      generator:
        strategy: AUTO
  manyToOne:
    article:
      targetEntity: Article
      inversedBy: articleTags
  fields: 
    # your extra fields here
  manyToOne:
    tag:
      targetEntity: Tag
      inversedBy: articleTags

Tôi hy vọng nó sẽ giúp



Đó là những gì tôi đang tìm kiếm, cảm ơn bạn! Thật không may, không có ví dụ yml cho trường hợp sử dụng thứ ba! :(Bất cứ ai cũng có thể chia sẻ một ví dụ về trường hợp sử dụng thứ ba bằng định dạng yml? Tôi thực sự sẽ xuất hiện:#
Stphane

Tôi đã thêm vào câu trả lời cho trường hợp của bạn;)
Mirza Selimovic

Không đúng. Thực thể không phải là với id (id) AUTO. Điều đó sai, tôi đang cố gắng tạo ra ví dụ chính xác
Gatunox

tôi sẽ đăng câu trả lời mới để nhận nếu được định dạng chính xác
Gatunox

3

Đơn hướng. Chỉ cần thêm nghịch đảoBy: (Tên cột nước ngoài) để biến nó thành hai chiều.

# config/yaml/ProductStore.dcm.yml
ProductStore:
  type: entity
  id:
    product:
      associationKey: true
    store:
      associationKey: true
  fields:
    status:
      type: integer(1)
    createdAt:
      type: datetime
    updatedAt:
      type: datetime
  manyToOne:
    product:
      targetEntity: Product
      joinColumn:
        name: product_id
        referencedColumnName: id
    store:
      targetEntity: Store
      joinColumn:
        name: store_id
        referencedColumnName: id

Tôi hy vọng nó sẽ giúp. Hẹn gặp lại


2

Bạn có thể đạt được những gì bạn muốn với Kế thừa bảng lớp khi bạn thay đổi AlbumTrackReference thành AlbumTrack:

class AlbumTrack extends Track { /* ... */ }

getTrackList()sẽ chứa AlbumTrackcác đối tượng mà sau đó bạn có thể sử dụng như bạn muốn:

foreach($album->getTrackList() as $albumTrack)
{
    echo sprintf("\t#%d - %-20s (%s) %s\n", 
        $albumTrack->getPosition(),
        $albumTrack->getTitle(),
        $albumTrack->getDuration()->format('H:i:s'),
        $albumTrack->isPromoted() ? ' - PROMOTED!' : ''
    );
}

Bạn sẽ cần kiểm tra điều này một cách xuyên suốt để đảm bảo bạn không bị ảnh hưởng bởi hiệu suất.

Thiết lập hiện tại của bạn rất đơn giản, hiệu quả và dễ hiểu ngay cả khi một số ngữ nghĩa không hoàn toàn phù hợp với bạn.


0

Trong khi nhận tất cả các bản nhạc album trong lớp album, bạn sẽ tạo thêm một truy vấn cho một bản ghi nữa. Đó là vì phương thức proxy. Có một ví dụ khác về mã của tôi (xem bài đăng cuối cùng trong chủ đề): http://groups.google.com/group/doctrine-user/browse_thread/thread/d1d87c96052e76f7/436b896e83c10868#436b896e83c10868

Có phương pháp nào khác để giải quyết điều đó không? Không phải là tham gia một giải pháp tốt hơn sao?


1
Trong khi điều này về mặt lý thuyết có thể trả lời câu hỏi, tốt hơn là nên bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo.
Spontifixus

0

Đây là giải pháp như được mô tả trong Tài liệu Doctrine2

<?php
use Doctrine\Common\Collections\ArrayCollection;

/** @Entity */
class Order
{
    /** @Id @Column(type="integer") @GeneratedValue */
    private $id;

    /** @ManyToOne(targetEntity="Customer") */
    private $customer;
    /** @OneToMany(targetEntity="OrderItem", mappedBy="order") */
    private $items;

    /** @Column(type="boolean") */
    private $payed = false;
    /** @Column(type="boolean") */
    private $shipped = false;
    /** @Column(type="datetime") */
    private $created;

    public function __construct(Customer $customer)
    {
        $this->customer = $customer;
        $this->items = new ArrayCollection();
        $this->created = new \DateTime("now");
    }
}

/** @Entity */
class Product
{
    /** @Id @Column(type="integer") @GeneratedValue */
    private $id;

    /** @Column(type="string") */
    private $name;

    /** @Column(type="decimal") */
    private $currentPrice;

    public function getCurrentPrice()
    {
        return $this->currentPrice;
    }
}

/** @Entity */
class OrderItem
{
    /** @Id @ManyToOne(targetEntity="Order") */
    private $order;

    /** @Id @ManyToOne(targetEntity="Product") */
    private $product;

    /** @Column(type="integer") */
    private $amount = 1;

    /** @Column(type="decimal") */
    private $offeredPrice;

    public function __construct(Order $order, Product $product, $amount = 1)
    {
        $this->order = $order;
        $this->product = $product;
        $this->offeredPrice = $product->getCurrentPrice();
    }
}
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.