Qual é a diferença entre inversedBy e mappedBy?

102

Estou desenvolvendo meu aplicativo usando Zend Framework 2 e Doctrine 2.

Enquanto escrevo anotações, não consigo entender a diferença entre mappedBy e inversedBy.

Quando devo usar mappedBy ?

Quando devo usar inversedBy ?

Quando devo usar nenhum dos dois?

Aqui está um exemplo:

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

Fiz uma pesquisa rápida e encontrei o seguinte, mas ainda estou confuso:

Desenvolvedor
fonte

Respostas:

159
  • mappedBy deve ser especificado no lado inverso de uma associação (bidirecional)
  • inversedBy deve ser especificado no lado proprietário de uma associação (bidirecional)

da documentação da doutrina:

  • ManyToOne é sempre o lado proprietário de uma associação bidirecional.
  • OneToMany é sempre o lado inverso de uma associação bidirecional.
  • O lado proprietário de uma associação OneToOne é a entidade com a tabela que contém a chave estrangeira.

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

Andreas Linden
fonte
1
Estranhamente, o documentador do Doctrine decidiu deixar de fora o exemplo yaml de um mapeamento bidirecional muitos-para-um, provavelmente o mais comumente usado!
Peter Wooster
4
@PeterWooster, a prática recomendada é usar Anotações, já que você tem todas as informações sobre a entidade em um só lugar!
Andreas Linden
Isso também se aplica a muitas relações. Para aqueles: você mesmo pode escolher o lado proprietário de uma associação muitos para muitos.
11mb de
5
@AndreasLinden amplamente usado não significa melhor prática. Algo que usa comentários para escrever código em tempo real nunca pode ser considerado a melhor prática, não é nativo do php e nem mesmo incluído por padrão em todos os frameworks. Ter todas as informações sobre uma entidade em um único lugar é um anti-argumento. Desde quando agrupar todo o seu código em um lugar é uma coisa boa? É uma dor de escrever, de manter e reduzir a organização em seu projeto. Melhor prática ? Duh.
JesusTheHun
4
@JesusTheHun você está comparando maçãs e peras. "todo o código" é muito diferente de "todas as informações sobre uma entidade";)
Andreas Linden
55

As respostas acima não foram suficientes para eu entender o que estava acontecendo, então, depois de me aprofundar mais, acho que tenho uma maneira de explicar que fará sentido para as pessoas que lutaram como eu entender.

inversedBy e mappedBy são usados ​​pelo mecanismo INTERNAL DOCTRINE para reduzir o número de consultas SQL necessárias para obter as informações de que você precisa. Para ficar claro, se você não adicionar inversedBy ou mappedBy, seu código ainda funcionará, mas não será otimizado .

Então, por exemplo, veja as classes abaixo:

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

Essas classes, se você executar o comando para gerar o esquema (por exemplo, bin/console doctrine:schema:update --force --dump-sql), notará que a tabela Categoria não possui uma coluna para tarefas. (isso ocorre porque ele não tem uma anotação de coluna nele)

O importante a entender aqui é que as tarefas variáveis ​​estão lá apenas para que o mecanismo de doutrina interno possa usar a referência acima dela que diz sua categoria mappedBy. Agora ... não se confunda aqui como eu estava ... Categoria NÃO está se referindo ao NOME DA CLASSE , está se referindo à propriedade na classe Task chamada 'categoria $ protegida'.

Da mesma forma, na classe Tasks a propriedade $ category menciona que é inversedBy = "tasks", observe que isso é plural, NÃO É O PLURAL DO NOME DA CLASSE , mas apenas porque a propriedade é chamada de 'protected $ tasks' na categoria classe.

Depois de entender isso, torna-se muito fácil entender o que inversedBy e mappedBy estão fazendo e como usá-los nessa situação.

O lado que está referenciando a chave estrangeira como 'tarefas' no meu exemplo sempre obtém o atributo inversedBy porque precisa saber qual classe (por meio do comando targetEntity) e qual variável (inversedBy =) nessa classe 'trabalhar para trás' para fale e obtenha as informações da categoria. Uma maneira fácil de lembrar isso é que a classe que teria o Foreignkey_id é aquela que precisa ter inversedBy.

Onde, como acontece com a categoria, e sua propriedade $ tasks (que não está na mesa, lembre-se, apenas parte da classe para fins de otimização) é MappedBy 'tarefas', isso cria o relacionamento oficialmente entre as duas entidades para que a doutrina agora possa com segurança use instruções SQL JOIN em vez de duas instruções SELECT separadas. Sem mappedBy, o mecanismo de doutrina não saberia a partir da instrução JOIN, ele criará qual variável na classe 'Tarefa' para colocar as informações da categoria.

Espero que isso explique um pouco melhor.

Joseph Astrahan
fonte
6
Isso está muito bem explicado e obrigado pelo seu esforço. Eu vim do Laravel Eloquent para a doutrina e isso foi difícil para mim entender a lógica aqui. Bem feito. Category is NOT referring TO THE CLASS NAME, its referring to the property on the Task class called 'protected $category'tudo que eu precisava. Isso não só resolveu meu problema, mas também me ajudou a entender. A melhor resposta IMO :-)
The Alpha
1
Eu também vim do Eloquent, isso me ajudou muito. meu único ponto crítico agora é como definir o setter / getter para isso, ainda estou aprendendo como funciona
Eman
1
Sim, isso é realmente fácil e até automatizado com algumas ferramentas por aí, comece um bate-papo comigo mais tarde e eu posso te ajudar
Joseph Astrahan
1
Verifique esta resposta, stackoverflow.com/questions/16629397/…
Joseph Astrahan
1
symfony.com/doc/current/doctrine/associations.html , é melhor realmente aprender, o symfony usa a doutrina, então ele vai te ensinar a mesma coisa.
Joseph Astrahan
21

No relacionamento bidirecional tem um lado proprietário e um lado inverso

mappedBy : colocado no lado inverso de um relacionamento bidirecional Para se referir ao seu lado proprietário

inversedBy : colocado no lado proprietário de uma relação bidirecional Para se referir ao seu lado inverso

E

mappedByAtributo usado com a declaração de mapeamento OneToOne, OneToMany ou ManyToMany.

Atributo inversedBy usado com a declaração de mapeamento OneToOne, ManyToOne ou ManyToMany.

Aviso : o lado proprietário de um relacionamento bidirecional, o lado que contém a chave estrangeira.

há duas referências sobre inversedBy e mappedBy na Documentação do Doctrine: Primeiro Link , Segundo Link

ahmed hamdy
fonte
1
os links estão mortos?
Scaramouche
2

5.9.1. Possuir e lado inverso

Para associações muitos para muitos, você pode escolher qual entidade é proprietária e qual é o lado inverso. Existe uma regra semântica muito simples para decidir qual lado é mais adequado para ser o lado proprietário da perspectiva dos desenvolvedores. Você só precisa se perguntar qual entidade é responsável pelo gerenciamento da conexão e escolher isso como o lado proprietário.

Tome um exemplo de duas entidades Artigo e Tag. Sempre que você deseja conectar um Artigo a uma Tag e vice-versa, é principalmente o Artigo o responsável por esta relação. Sempre que você adiciona um novo artigo, deseja conectá-lo a tags existentes ou novas. Seu formulário de criação de artigo provavelmente apoiará essa noção e permitirá especificar as tags diretamente. É por isso que você deve escolher o Artigo como lado proprietário, pois torna o código mais compreensível:

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

Micheal Mouner Mikhail Youssif
fonte