Доктрина 2 Многие ко многим с тремя объектами и таблицей соединений

#doctrine #many-to-many #doctrine-orm

#доктрина #многие ко многим #доктрина-orm

Вопрос:

У меня есть 3 таблицы

 People
 - id -pk
 - name

Roles
 - id -pk
 - roleName

Events
 - id -pk
 - title
  

и таблица соединений

 event_performers
 - event_id -pk
 - role_id -pk
 - people_id -pk
  

Событие имеет много ролей.
Роль выполняет человек.
Роль связана со многими событиями.
Человек может выполнять множество ролей.

Итак, я хотел бы, чтобы, когда я получаю событие, я мог получить доступ к коллекции ролей, связанных с этим событием, и из ролей я могу получить человека, который выполнял роль.

Я не уверен, как я мог бы сопоставить это в доктрине 2?

Ответ №1:

Я столкнулся с этой же проблемой около недели назад. Я опросил пользователей IRC-канала Doctrine в поисках наилучшего решения (или, по крайней мере, наиболее часто применяемого). Вот как это делается:

Создайте новую сущность с именем что-то вроде EventsPeopleRoles с тремя свойствами, сопоставленными с помощью @ManyToOne, $event, $person и $role .

Каждая ассоциация должна быть сопоставлена примерно так:

 /**
 * @ManyToOne(targetEntity="Events", inversedBy="eventsPeopleRoles")
 * @JoinColumn(name="event_id", referencedColumnName="id", nullable=false)
 */
private $event;
  

Затем в каждом из трех связанных объектов закодируйте обратную сторону ассоциации следующим образом:

 /**
 * @OneToMany(targetEntity="EventsPeopleRoles", mappedBy="event")
 */
private $eventsPeopleRoles;
  

Затем у вас есть выбор: либо добавить свойство $ id к вашей «сущности объединения», либо использовать составной первичный ключ, как описано здесь, и добавить уникальную аннотацию ограничения в определение класса сущности . Обратите внимание, что составные внешние ключи поддерживаются только начиная с доктрины 2.1.

Я скептически относился к этому решению, потому что мне не нравится идея создания объекта только для целей объединения. Это похоже на обман или, по крайней мере, противоречит принципам проектирования ORM. Но я уверен, что это принятое решение (по крайней мере, на данный момент) среди экспертов по доктрине.

Комментарии:

1. Спасибо за ваш ответ, я попробую это и дам вам знать, как у меня получилось.

2. Кажется, это тоже отлично работает для меня… Спасибо, что поделились!

Ответ №2:

На случай, если кто-то такой же новичок, как и я, я просто добавлю несколько аннотаций к этому замечательному ответу @cantera:

В каждый из трех объектов вы должны добавить код, который он предложил, только позаботьтесь о том, чтобы «ORM » был включен перед «ManyToOne» и «JoinColumn». Я также добавил аннотации «@var», просто чтобы уточнить, насколько это возможно:

В вашем имени объекта =»eventsPeopleRoles» добавьте ссылку на каждый из трех объектов:

 /**
 * @var Events $event
 *
 * @ORMManyToOne(targetEntity="Events", inversedBy="eventsPeopleRoles")
 * @ORMJoinColumn(name="event_id", referencedColumnName="id", nullable=false)
 */
private $event;

/**
 * @var Events $people
 *
 * @ORMManyToOne(targetEntity="Person", inversedBy="eventsPeopleRoles")
 * @ORMJoinColumn(name="person_id", referencedColumnName="id", nullable=false)
 */
private $people;

/**
 * @var Role $role
 *
 * @ORMManyToOne(targetEntity="Role", inversedBy="eventsPeopleRoles")
 * @ORMJoinColumn(name="role_id", referencedColumnName="id", nullable=false)
 */
private $role;
  

В вашем имени объекта =»События»

 /**
 * @var ArrayCollection $eventsPeopleRoles
 *
 * @ORMOneToMany(targetEntity="EventsPeopleRoles", mappedBy="event")
 */
private $eventsPeopleRoles;
  

В вашем имени объекта =»Person»

 /**
 * @var ArrayCollection $eventsPeopleRoles
 *
 * @ORMOneToMany(targetEntity="EventsPeopleRoles", mappedBy="people")
 */
private $eventsPeopleRoles;
  

В вашем имени объекта =»Роль»

 /**
 * @var ArrayCollection $eventsPeopleRoles
 *
 * @ORMOneToMany(targetEntity="EventsPeopleRoles", mappedBy="roles")
 */
private $eventsPeopleRoles;
  

Комментарии:

1. Вы допустили ошибку внутри класса объектов eventsPeopleRoles. Вместо частного события $; в конце это частная роль $;

2. Спасибо, Эдгар, я исправил свой ответ.

Ответ №3:

решение @cantera25 является правильным.

Я хочу добавить к этому мысль.

Обычно, если ваш объект объединения будет объединять более двух объектов вместе, это указывает на то, что он играет довольно важную роль в вашей информационной архитектуре и должен быть переименован.

Например, приложение, над которым я работаю для конюшни, имеет Booking объект.

У каждого Booking есть по крайней мере один Rider , который использует один Horse для этого бронирования.

Я изначально разработал объект с именем BookingRiderHorse , чтобы объединить эти три вместе.

Излишне говорить, что это было бы довольно запутанно и трудно понять позже, когда я вернусь к коду.

Я переименовал прежнюю Booking сущность в Ride и переименовал BookingRiderHorse сущность в Booking .

Теперь в бизнес-логике Bookings создаются и должны существовать Ride Horse Rider записи и. У каждого Booking есть только одно Horse и Rider , но у каждого Ride может быть много Bookings .

Это то же самое, что использовать таблицу соединений со смешным именем, но ее гораздо проще понять и означает, что я могу работать с бизнес-логикой, не задумываясь о том, как работают соединения.