Doctrine2 : 참조 테이블의 추가 열을 사용하여 다대 다를 처리하는 가장 좋은 방법


282

Doctrine2에서 다 대다 관계로 작업하는 가장 좋고, 깨끗하고, 가장 간단한 방법이 무엇인지 궁금합니다.

Metallica의 Master of Puppets 와 같은 앨범 에 여러 트랙 이 있다고 가정 해 봅시다 . 그러나 Battery by Metallica 처럼 하나의 트랙이 하나 이상의 앨범에 나타날 수 있다는 사실에 유의하십시오 .이 트랙에는 3 개의 앨범이 있습니다.

그래서 필요한 것은 앨범과 트랙 사이의 다 대다 관계이며, 지정된 열의 트랙 위치와 같은 몇 가지 추가 열이있는 세 번째 테이블을 사용합니다. 실제로 Doctrine의 문서에서 알 수 있듯이 그 기능을 달성하기 위해서는 일대 다 관계가 두 배가됩니다.

/** @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;
    }
}

샘플 데이터 :

             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 |
+----+----------+----------+----------+------------+

이제 앨범과 관련된 앨범 및 트랙 목록을 표시 할 수 있습니다.

$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!' : ''
        );
    }   
}

결과는 내가 기대하는 것입니다. 즉, 적절한 순서로 트랙이 있고 앨범이 프로모션으로 표시되는 앨범 목록입니다.

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) 

무슨 일이야?

이 코드는 무엇이 잘못되었는지 보여줍니다.

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

Album::getTracklist()AlbumTrackReference객체 대신 객체 배열을 반환 Track합니다. 나는 무엇을 둘 경우 원인 프록시 방법을 만들 수 없습니다, Album그리고 TrackgetTitle()방법을? Album::getTracklist()메소드 내에서 추가 처리를 할 수 있지만 가장 간단한 방법은 무엇입니까? 그런 식으로 글을 써야합니까?

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

편집하다

@beberlei는 프록시 메소드 사용을 제안했습니다.

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

그거 좋은 아이디어가 될 것입니다하지만 난 양쪽에서 그 "참조 오브젝트"를 사용하고 있습니다 $album->getTracklist()[12]->getTitle()$track->getAlbums()[1]->getTitle()그래서, getTitle()방법은 호출의 문맥에 따라 다른 데이터를 반환해야합니다.

나는 다음과 같은 일을해야 할 것이다.

 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();
 }

그리고 그것은 매우 깨끗한 방법이 아닙니다.


2
AlbumTrackReference를 어떻게 처리합니까? 예를 들어 $ album-> addTrack () 또는 $ album-> removeTrack ()?
Daniel

나는 당신이 문맥에 대한 당신의 의견을 이해하지 못했습니다. 내 의견으로는 데이터가 컨텍스트에 의존하지 않습니다. 정보 $album->getTracklist()[12]AlbumTrackRef객체이므로 $album->getTracklist()[12]->getTitle()항상 트랙의 제목을 반환합니다 (프록시 방법을 사용하는 경우). 동안 $track->getAlbums()[1]Album목적은, 그래서 $track->getAlbums()[1]->getTitle()항상 앨범의 제목을 반환합니다.
Vinícius Fagundes

또 다른 아이디어에 사용하는 AlbumTrackReference두 개의 프록시 방법, getTrackTitle()getAlbumTitle.
Vinícius Fagundes

답변:


158

Doctrine 사용자 메일 링리스트에서 비슷한 질문을했고 정말 간단한 답변을 얻었습니다.

다 대다 관계를 실체 자체로 생각하면, 일대 다 관계와 다 대일 관계로 연결된 3 개의 객체가 있다는 것을 알게됩니다.

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

관계에 데이터가 있으면 더 이상 관계가 아닙니다!


아무도이 새로운 엔티티를 yml 스키마 파일로 생성하기 위해 교리 명령 줄 도구를 얻는 방법을 알고 있습니까? 이 명령 : app/console doctrine:mapping:import AppBundle yml원래 두 테이블에 대해 여전히 manyToMany 관계를 생성하고 엔티티로 간주하지 않고 세 번째 테이블을 무시하십시오.:/
Stphane

foreach ($album->getTracklist() as $track) { echo $track->getTrack()->getTitle(); }@Crozin과 제공하는 것의 차이점은 무엇 consider the relationship as an entity입니까? 그가 물어보고 싶은 것은 관계형 엔티티를 건너 뛰고foreach ($album->getTracklist() as $track) { echo $track->getTitle(); }
panda

6
"관계가 데이터를 가지면 더 이상 관계가 없습니다"이것은 정말로 깨달았습니다. 나는 단지 엔티티 관점에서 관계에 대해 생각할 수 없었습니다!
양파

관계가 이미 만들어져 다 대다로 사용된다면 어떨까요? 우리는 다 대다에서 추가 필드가 필요하다는 것을 깨달았으므로 다른 엔티티를 만들었습니다. 문제는 기존 데이터와 이름이 같은 기존 테이블을 사용하는 것이 친구가되고 싶지 않은 것입니다. 아무도 전에 이것을 시도 했습니까?
tylerism

궁금해하는 사람들을 위해 : 테이블이 작동함에 따라 (이미 존재하는) 다 대다로 결합 가능한 엔티티를 작성하지만 다대 다를 보유한 엔티티는 새 엔티티에 일대 다로 적응해야합니다. 또한 외부와의 인터페이스 (이전의 다 대다에 대한 getter / setter)는 대부분 조정되어야합니다.
Jakumi

17

$ album-> getTrackList ()에서 "AlbumTrackReference"엔티티를 다시 얻었으므로 Track과 프록시에서 메소드를 추가하는 것은 어떻습니까?

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

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

이렇게하면 모든 방법이 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 AlbumTrackReference의 이름을 바꿔야합니다 (예 : "AlbumTrack"). 분명히 참조 일뿐 아니라 추가 논리가 포함되어 있습니다. 앨범에 연결되지 않았지만 프로모션 CD 또는 기타 항목을 통해 사용 가능한 트랙도있을 수 있으므로 더 명확하게 분리 할 수 ​​있습니다.


1
프록시 메소드는 100 %로 문제를 해결하지 못합니다 (편집 확인). Btw You should rename the AlbumT(...)-좋은 지적
Crozin

3
왜 두 가지 방법이 없습니까? AlbumTrackReference 객체의 getAlbumTitle () 및 getTrackTitle ()? 각각의 하위 객체에 대한 프록시입니다.
beberlei

목표는 가장 자연스러운 객체 API입니다. $album->getTracklist()[1]->getTrackTitle()만큼 좋고 / 나쁩니다 $album->getTracklist()[1]->getTrack()->getTitle(). 그러나 앨범-> 트랙 참조와 트랙-> 앨범 참조에 대한 두 가지 클래스가 있어야하며 구현하기가 너무 어렵습니다. 아마 지금까지는 이것이 최고의 솔루션 일 것입니다 ...
Crozin

13

좋은 예는 없습니다

관계에 추가 속성을 저장하기 위해 3 개의 참여 클래스 간의 일대 다 / 다 대일 연관의 깨끗한 코딩 예를 찾고있는 사람들은이 사이트를 확인하십시오.

3 개의 참여 클래스 사이의 일대 다 / 다 대일 연관의 좋은 예

기본 키에 대해 생각하십시오

기본 키에 대해서도 생각하십시오. 이와 같은 관계에 종종 복합 키를 사용할 수 있습니다. 교리는 기본적으로 이것을 지원합니다. 참조 된 엔터티를 ID로 만들 수 있습니다. 복합 키에 대한 설명서를 여기에서 확인하십시오.


10

프록시 메소드 사용에 대한 @beberlei의 제안과 함께 할 것이라고 생각합니다. 이 프로세스를 단순화하기 위해 할 수있는 것은 두 가지 인터페이스를 정의하는 것입니다.

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

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

그런 다음 귀하 Album와 귀하 TrackAlbumTrackReference모두 구현할 수 있으며, 다음과 같이 여전히 구현할 수 있습니다.

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();
    }
}

이 방법은 직접 참조하는 로직 제거 Track또는를 Album하고, 그냥이를 사용하므로 교체 TrackInterface또는 AlbumInterface, 당신은 당신을 사용하여 얻을 AlbumTrackReference가능한 모든 경우에. 필요한 것은 인터페이스 간 메소드를 조금씩 구별하는 것입니다.

이것은 DQL이나 저장소 논리를 구분하지 않지만 당신의 서비스는 당신이 통과하고 있다는 사실을 무시 Album하거나 AlbumTrackReference, 또는 Track또는 AlbumTrackReference인터페이스 뒤에 당신이 숨긴 때문에 모든 :)

도움이 되었기를 바랍니다!


7

첫째, 나는 주로 그의 제안에 대해 beberlei에 동의합니다. 그러나 함정에 빠지게 될 수도 있습니다. 귀하의 도메인은 타이틀이 트랙의 자연스러운 열쇠라고 생각하는 것 같습니다. 이는 시나리오의 99 %에 해당 될 것입니다. 그러나 어떤 경우 배터리인형의 마스터 의 버전보다 다른 버전 (리마스터 다른 길이, 라이브, 음향, 리믹스, 등)입니다 메탈리카 컬렉션 .

이 경우를 처리 (또는 무시)하려는 방법에 따라 beberlei의 제안 된 경로로 이동하거나 Album :: getTracklist ()에서 제안 된 추가 논리를 사용할 수 있습니다. 개인적으로 API를 깨끗하게 유지하기 위해 추가 논리가 정당하다고 생각하지만 둘 다 장점이 있습니다.

내 유스 케이스를 수용하려면 트랙에 다른 트랙 (예 : $ similarTracks)을 참조하는 자체 참조 OneToMany를 포함시킬 수 있습니다. 이 경우 트랙 배터리에 대한 두 개의 엔티티가 있습니다 . 하나는 메탈리카 컬렉션 과 하나는 마스터 오브 퍼펫 입니다. 그런 다음 각 유사한 트랙 엔터티는 서로에 대한 참조를 포함합니다. 또한 현재 AlbumTrackReference 클래스를 제거하고 현재 "문제"를 제거합니다. 복잡성을 다른 지점으로 옮기는 데 동의하지만 이전에는 불가능했던 유스 케이스를 처리 할 수 ​​있습니다.


6

"가장 좋은 방법"을 요구하지만 최선의 방법은 없습니다. 여러 가지 방법이 있으며 이미 그 중 일부를 발견했습니다. 연관 클래스를 사용할 때 연관 관리를 관리 및 / 또는 캡슐화하는 방법은 전적으로 귀하와 귀하의 구체적인 영역에 달려 있습니다.

그 외에도, 교리와 관계형 데이터베이스를 방정식에서 제거함으로써 문제를 크게 단순화 할 수 있습니다. 귀하의 질문의 본질은 일반 OOP에서 연관 클래스를 처리하는 방법에 대한 질문으로 요약됩니다.


6

연결 클래스 (추가 사용자 정의 필드 포함) 주석에 정의 된 조인 테이블과 다 대다 주석에 정의 된 조인 테이블과의 충돌에서 왔습니다.

직접 다 대 다 관계를 갖는 두 엔티티의 맵핑 정의는 'joinTable'주석을 사용하여 결합 테이블을 자동으로 작성하는 것으로 나타났습니다. 그러나 조인 테이블은 이미 기본 엔터티 클래스의 주석에 의해 정의되었으며 추가 사용자 정의 필드로 조인 테이블을 확장하기 위해이 연관 엔터티 클래스의 자체 필드 정의를 사용하고 싶었습니다.

설명과 해결책은 위의 FMaz008에 의해 식별 된 것입니다. 내 상황에서, 그것은 ' Doctrine Annotation Question ' 포럼 의이 게시물 덕분이었습니다 . 이 글은 ManyToMany 단방향 관계 에 관한 교리 문서에 주목합니다 . 'association entity class'를 사용하는 방법에 대한 참고 사항을 확인하여 두 개의 주요 엔티티 클래스 사이의 다 대다 주석 맵핑을 기본 엔티티 클래스의 일대 다 주석과 두 개의 '다 대다'로 직접 대체하십시오. 연관 엔티티 클래스에서 -1 '주석. 이 포럼 게시물 에 추가 필드가있는 Association 모델 의 예제가 있습니다 .

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

이것은 정말 유용한 예입니다. 문서 교리 2에는 부족합니다.

매우 감사합니다.

프록시의 경우 기능을 수행 할 수 있습니다.

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

당신이 말하는 것은 메타 데이터, 데이터에 관한 데이터입니다. 나는 현재 작업중 인 프로젝트에 대해 동일한 문제를 겪었고 그것을 알아 내려고하는 데 약간의 시간을 소비해야했습니다. 여기에 게시하기에는 너무 많은 정보가 있지만 아래에는 유용한 두 개의 링크가 있습니다. Symfony 프레임 워크를 참조하지만 Doctrine ORM을 기반으로합니다.

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/

행운과 멋진 메탈리카 참조!


3

해결책은 교리 문서에 있습니다. FAQ에서 당신은 이것을 볼 수 있습니다 :

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

튜토리얼은 다음과 같습니다.

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

따라서 더 이상 수행하지 manyToMany않지만 추가 엔터티를 만들어 manyToOne두 엔터티에 넣어야 합니다.

ADD f00bar 코멘트 @ 위해 :

간단합니다. 다음과 같이하면됩니다.

Article  1--N  ArticleTag  N--1  Tag

따라서 당신은 엔티티 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

나는 그것이 도움이되기를 바랍니다


1
업데이트 된 링크 : 1-FAQ , 2-튜토리얼 : 복합 기본 키
m14t

그건 내가 찾던 것입니다, 감사합니다! 불행히도, 세 번째 사용 사례에 대한 yml 예제는 없습니다! :(누구든지 yml 형식을 사용하는 세 번째 유스 케이스를 공유 할 수 있습니까? 나는 정말로 Appriace:#
Stphane

답변에 추가했습니다;)
Mirza Selimovic 2016 년

잘못되었습니다. 엔티티는 id (id) AUTO 일 필요는 없습니다. 틀린 예, 올바른 예제를 만들려고합니다
Gatunox

형식이 올바르게 설정되면 새 답변을 게시 할 것입니다.
Gatunox

3

단방향. inversedBy : (Foreign Column Name)을 추가하여 양방향으로 만드십시오.

# 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

도움이 되길 바랍니다. 또 봐요


2

AlbumTrackReference를 AlbumTrack으로 변경 하는 Class Table Inheritance를 사용 하여 원하는 것을 얻을 수 있습니다 .

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

그리고 원하는대로 사용할 수 getTrackList()있는 AlbumTrack객체 가 포함 됩니다.

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!' : ''
    );
}

성능 측면에서 어려움을 겪지 않도록이를 철저히 검토해야합니다.

현재 설정은 간단하고 효율적이며 일부 시맨틱이 사용자와 잘 맞지 않아도 이해하기 쉽습니다.


0

앨범 클래스 내에서 모든 앨범 트랙을 가져 오는 동안 하나 이상의 레코드에 대한 쿼리를 하나 더 생성합니다. 프록시 방법 때문입니다. 내 코드의 또 다른 예가 있습니다 (주제 마지막 게시물 참조) : http://groups.google.com/group/doctrine-user/browse_thread/thread/d1d87c96052e76f7/436b896e83c10868#436b896e83c10868

그것을 해결할 다른 방법이 있습니까? 단일 조인이 더 나은 솔루션이 아닙니까?


1
이 이론적으로 질문에 대답 할 수 있습니다 동안, 바람직 할 것이다 여기에 대한 대답의 본질적인 부분을 포함하고 참조 할 수 있도록 링크를 제공합니다.
Spontifixus

0

다음은 Doctrine2 Documentation에 설명 된 솔루션입니다.

<?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();
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.