В чем разница между reverseBy и mappedBy?

103

Я разрабатываю свое приложение с использованием Zend Framework 2 и Doctrine 2.

Когда я пишу аннотации, я не могу понять разницу между mappedByи inversedBy.

Когда я должен использовать mappedBy?

Когда я должен использовать inversedBy?

Когда я не должен использовать ни то, ни другое?

Вот пример:

 /**
 *
 * @ORM\OneToOne(targetEntity="\custMod\Entity\Person", mappedBy="customer")
 * @ORM\JoinColumn(name="personID", referencedColumnName="id")
 */
protected $person;

/**
 *
 * @ORM\OneToOne(targetEntity="\Auth\Entity\User")
 * @ORM\JoinColumn(name="userID", referencedColumnName="id")
 */
protected $user;

/**
 *
 * @ORM\ManyToOne (targetEntity="\custMod\Entity\Company", inversedBy="customer")
 * @ORM\JoinColumn (name="companyID", referencedColumnName="id")
 */
protected $company;

Я провел быстрый поиск и нашел следующее, но все еще не понимаю:

Разработчик
источник

Ответы:

159
  • mappedBy должен быть указан на обратной стороне (двунаправленной) ассоциации
  • InsedBy должен быть указан на стороне владельца (двунаправленной) ассоциации

из документации доктрины:

  • ManyToOne всегда является владельцем двунаправленной связи.
  • OneToMany всегда является обратной стороной двунаправленной связи.
  • Сторона-владелец ассоциации OneToOne - это объект с таблицей, содержащей внешний ключ.

См. Https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/unitofwork-associations.html

Андреас Линден
источник
1
Как ни странно, разработчик документации Doctrine решил опустить yaml-пример двунаправленного сопоставления «многие-к-одному», вероятно, наиболее часто используемый!
Питер Вустер
4
@PeterWooster, лучше всего использовать аннотации, так как тогда у вас будет вся информация об объекте в одном месте!
Андреас Линден
Это также относится ко многим многим отношениям. Для тех: вы можете сами выбрать принадлежащую сторону ассоциации "многие ко многим".
11мб
5
Широкое использование @AndreasLinden не означает передовой практики. Что-то, использующее комментарии для написания кода "на лету", никогда не могло считаться лучшей практикой, это не является родным для PHP и даже не включено по умолчанию во все фреймворки. Наличие всей информации о сущности в одном месте - анти-аргумент. Сгруппировать весь код в одном месте - это хорошо? Больно писать, мучительно поддерживать и сокращать организацию вашего проекта. лучшая практика? Ага.
JesusTheHun
4
@JesusTheHun, ты сравниваешь яблоки и груши. «весь код» очень сильно отличается от «всей информации о сущности»;)
Андреас Линден
56

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

InversedBy и mappedBy используются механизмом ВНУТРЕННЕЙ ДОКТРИНЫ, чтобы уменьшить количество SQL-запросов, которые он должен выполнять для получения необходимой информации. Чтобы прояснить ситуацию, если вы не добавите инвертированныйBy или mappedBy, ваш код все равно будет работать, но не будет оптимизирован .

Так, например, посмотрите на классы ниже:

class Task
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="task", type="string", length=255)
     */
    private $task;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="dueDate", type="datetime")
     */
    private $dueDate;

    /**
     * @ORM\ManyToOne(targetEntity="Category", inversedBy="tasks", cascade={"persist"})
     * @ORM\JoinColumn(name="category_id", referencedColumnName="id")
     */
    protected $category;
}

class Category
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity="Task", mappedBy="category")
     */
    protected $tasks;
}

Эти классы, если вы должны запустить команду для генерации схемы (например bin/console doctrine:schema:update --force --dump-sql), вы заметите, что в таблице категорий нет столбца для задач. (это потому, что на нем нет аннотации столбца)

Здесь важно понимать, что переменные задачи существуют только для того, чтобы внутренний механизм доктрины мог использовать ссылку над ним, в которой указано его mappedBy Category. Теперь ... не путайте здесь, как я ... Категория НЕ относится к ИМЕНИ КЛАССА , это относится к свойству класса Task под названием «protected $ category».

Аналогично, в классе Tasks свойство $ category упоминает, что оно инвертированоBy = "tasks", обратите внимание, что это множественное число, это НЕ МНОЖЕСТВЕННОЕ ЧИСЛО ИМЕНИ КЛАССА , а только потому, что свойство называется 'protected $ tasks' в Категории класс.

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

Сторона, которая ссылается на внешний ключ, например `` задачи '' в моем примере, всегда получает атрибут инвертированного типа, потому что ей нужно знать, какой класс (с помощью команды targetEntity) и какая переменная (инверседби =) в этом классе будет `` работать в обратном направлении '', чтобы говорить и получать информацию о категории от. Легкий способ запомнить это: класс, который будет иметь foreignkey_id, - это тот, который должен иметь инверсию.

Если, как и в случае с категорией, и ее свойство $ tasks (которого нет в таблице, помните, это только часть класса для целей оптимизации) является MappedBy 'tasks', это создает официальную связь между двумя объектами, так что теперь доктрина может безопасно используйте операторы JOIN SQL вместо двух отдельных операторов SELECT. Без mappedBy механизм доктрины не узнает из оператора JOIN, какую переменную в классе «Задача» создаст, чтобы поместить информацию о категории.

Надеюсь, это немного лучше объясняет.

Джозеф Астрахан
источник
6
Это очень хорошо объяснено, и спасибо за ваши усилия. Я пришел из Laravel Eloquent к доктрине, и мне было трудно понять здесь логику. Отлично сработано. Category is NOT referring TO THE CLASS NAME, its referring to the property on the Task class called 'protected $category'все, что мне нужно. Это не только решило мою проблему, но и помогло мне понять. Лучший ответ ИМО :-)
Альфа
1
Я тоже из Eloquent, это мне очень помогло. Моя единственная проблема сейчас - как установить сеттер / получатель для этого, я все еще изучаю
Эман
1
Да, это действительно просто и даже автоматизировано с помощью некоторых инструментов, начните чат со мной позже, и я смогу вам помочь
Джозеф Астрахан
1
Проверьте этот ответ, stackoverflow.com/questions/16629397/…
Джозеф Астрахан
1
symfony.com/doc/current/doctrine/associations.html , на самом деле это лучше для изучения, symfony использует doctrine, поэтому научит вас тому же самому.
Джозеф Астрахан
21

В двунаправленных отношениях есть как сторона владения, так и обратная сторона

mappedBy : положить в обратную сторону двунаправленной связи, чтобы ссылаться на свою сторону

reverseBy : поместить во владение стороны двунаправленного отношения, чтобы сослаться на его обратную сторону

И

Атрибут mappedBy, используемый с объявлением сопоставления OneToOne, OneToMany или ManyToMany.

Атрибут инверседби, используемый с объявлением сопоставления OneToOne, ManyToOne или ManyToMany.

Примечание : сторона-владелец двунаправленного отношения - сторона, содержащая внешний ключ.

в документации Doctrine есть две ссылки на reverseBy и mappedBy: первая ссылка , вторая ссылка

Ахмед Хамди
источник
1
ссылки мертвы?
Scaramouche
2

5.9.1. Собственная и обратная сторона

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

Возьмем для примера две сущности: Article и Tag. Всякий раз, когда вы хотите связать статью с тегом и наоборот, в основном за это отношение отвечает статья. Каждый раз, когда вы добавляете новую статью, вы хотите связать ее с существующими или новыми тегами. Ваша форма создания статьи, вероятно, будет поддерживать это понятие и позволит напрямую указывать теги. Вот почему вы должны выбрать Статью в качестве владельца, так как это делает код более понятным:

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html

Micheal Mouner Михаил Юссиф
источник