¿Cuál es la diferencia entre inversedBy y mappedBy?

102

Estoy desarrollando mi aplicación usando Zend Framework 2 y Doctrine 2.

Mientras escribo anotaciones, no puedo entender la diferencia entre mappedByy inversedBy.

¿Cuándo debo usar mappedBy?

¿Cuándo debo usar inversedBy?

¿Cuándo debo usar ninguno?

Aquí hay un ejemplo:

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

Hice una búsqueda rápida y encontré lo siguiente, pero todavía estoy confundido:

Desarrollador
fuente

Respuestas:

159
  • mappedBy debe especificarse en el lado inverso de una asociación (bidireccional)
  • inversedBy debe especificarse en el lado propietario de una asociación (bidireccional)

de la documentación de la doctrina:

  • ManyToOne es siempre el lado propietario de una asociación bidireccional.
  • OneToMany es siempre el lado inverso de una asociación bidireccional.
  • El lado propietario de una asociación OneToOne es la entidad con la tabla que contiene la clave externa.

Ver https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/unitofwork-associations.html

Andreas Linden
fuente
1
Curiosamente, el documentalista de Doctrine decidió omitir el ejemplo de yaml de un mapeo bidireccional de muchos a uno, ¡probablemente el más utilizado!
Peter Wooster
4
@PeterWooster, la mejor práctica es usar Anotaciones, ¡ya que tienes toda la información sobre la entidad en un solo lugar!
Andreas Linden
Esto también se aplica a muchas relaciones. Para aquellos: usted mismo puede elegir el lado propietario de una asociación de muchos a muchos.
11mb
5
@AndreasLinden, ampliamente utilizado, no significa mejores prácticas. Algo que use comentarios para escribir código sobre la marcha nunca se puede considerar una mejor práctica, no es nativo de php e incluso no se incluye por defecto en todos los marcos. Tener toda la información sobre una entidad en un solo lugar es un anti-argumento. ¿Desde cuándo agrupar todo su código en un solo lugar es algo bueno? Es una molestia escribir, una molestia mantener y reducir la organización de su proyecto. mejores prácticas ? Duh.
JesusTheHun
4
@JesusTheHun estás comparando manzanas y peras. "todo el código" es muy diferente a "toda la información sobre una entidad";)
Andreas Linden
55

Las respuestas anteriores no fueron suficientes para que yo entendiera lo que estaba pasando, así que después de ahondar más en ello creo que tengo una forma de explicarlo que tendrá sentido para las personas que lucharon como yo para entender.

inversedBy y mappedBy son utilizados por el motor INTERNAL DOCTRINE para reducir el número de consultas SQL que tiene que hacer para obtener la información que necesita. Para ser claros, si no agrega inversedBy o mappedBy, su código seguirá funcionando pero no se optimizará .

Entonces, por ejemplo, mire las clases a continuación:

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

Estas clases, si tuviera que ejecutar el comando para generar el esquema (por ejemplo bin/console doctrine:schema:update --force --dump-sql), notará que la tabla Categoría no tiene una columna para las tareas. (esto se debe a que no tiene una anotación de columna)

Lo importante que hay que entender aquí es que las tareas variables solo están allí para que el motor de doctrina interno pueda usar la referencia que se encuentra arriba que dice su mappedBy Category. Ahora ... no se confunda aquí como yo ... La categoría NO se refiere al NOMBRE DE LA CLASE , se refiere a la propiedad en la clase de Tarea llamada 'categoría $ protegida'.

Del mismo modo, en la clase Tasks, la propiedad $ categoría menciona que está invertedBy = "tasks", observe que esto es plural, NO ES EL PLURAL DEL NOMBRE DE LA CLASE , pero solo porque la propiedad se llama 'protected $ tasks' en la Categoría clase.

Una vez que comprenda esto, será muy fácil comprender qué están haciendo inversedBy y mappedBy y cómo usarlos en esta situación.

El lado que hace referencia a la clave externa como 'tareas' en mi ejemplo siempre obtiene el atributo inversedBy porque necesita saber qué clase (a través del comando targetEntity) y qué variable (inversedBy =) en esa clase para 'trabajar hacia atrás' para hablar y obtener la información de la categoría. Una forma fácil de recordar esto es que la clase que tendría el Foreignkey_id es la que necesita tener invertedBy.

Donde, al igual que con la categoría, y su propiedad $ tasks (que no está en la tabla, recuerde, solo una parte de la clase para fines de optimización) está asignada por 'tareas', esto crea la relación oficialmente entre las dos entidades para que la doctrina ahora pueda utilice sentencias JOIN SQL en lugar de dos sentencias SELECT independientes. Sin mappedBy, el motor de doctrina no sabría de la declaración JOIN que creará qué variable en la clase 'Tarea' para poner la información de la categoría.

Espero que esto lo explique un poco mejor.

José Astrahan
fuente
6
Esto está muy bien explicado y gracias por su esfuerzo. Vine de Laravel Eloquent a la doctrina y esto me costó entender la lógica aquí. Bien hecho. Category is NOT referring TO THE CLASS NAME, its referring to the property on the Task class called 'protected $category'todo lo que necesitaba. No solo resolvió mi problema, sino que me ayudó a comprender. La mejor respuesta IMO :-)
The Alpha
1
Yo también vengo de Eloquent, esto me ayudó mucho. mi único punto de fricción ahora es cómo configurar el setter / getter para esto, todavía estoy aprendiendo cómo funciona
Eman
1
Sí, eso es realmente fácil e incluso automatizado con algunas herramientas, inicie un chat conmigo más tarde y puedo ayudarlo
Joseph Astrahan
1
Consulte esta respuesta, stackoverflow.com/questions/16629397/…
Joseph Astrahan
1
symfony.com/doc/current/doctrine/associations.html , en realidad es mejor aprender, Symfony usa doctrina, así que te enseñará lo mismo.
Joseph Astrahan
21

En la relación bidireccional tiene tanto un lado propietario como un lado inverso

mappedBy : poner en El lado inverso de una relación bidireccional Para referirse a su lado propietario

inversedBy : poner en El lado propietario de una relación bidireccional Para referirse a su lado inverso

Y

El atributo mappedBy se usa con la declaración de mapeo OneToOne, OneToMany o ManyToMany.

inversedBy atributo se utiliza con el OneToOne, ManyToOne, o declaración mapeo ManyToMany.

Aviso : el lado propietario de una relación bidireccional el lado que contiene la clave externa.

Hay dos referencias sobre inversedBy y mappedBy en la documentación de Doctrine: primer enlace , segundo enlace

ahmed hamdy
fuente
1
los enlaces están muertos?
Scaramouche
2

5.9.1. Poseer y reverso

Para asociaciones Many-To-Many, puede elegir qué entidad es la propietaria y cuál el lado inverso. Existe una regla semántica muy simple para decidir qué lado es más adecuado para ser el propietario desde la perspectiva de los desarrolladores. Solo tiene que preguntarse qué entidad es responsable de la gestión de la conexión y elegirla como propietario.

Tomemos un ejemplo de dos entidades Artículo y Etiqueta. Siempre que desee conectar un artículo a una etiqueta y viceversa, es principalmente el artículo el responsable de esta relación. Siempre que agrega un artículo nuevo, desea conectarlo con etiquetas nuevas o existentes. Su formulario de creación de artículo probablemente apoyará esta noción y permitirá especificar las etiquetas directamente. Es por eso que debe elegir el artículo como lado propietario, ya que hace que el código sea más comprensible:

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

Micheal Mouner Mikhail Youssif
fuente