Qual'è la differenza tra inversedBy e mappedBy?


102

Sto sviluppando la mia applicazione utilizzando Zend Framework 2 e Doctrine 2.

Mentre scrivo annotazioni, non riesco a capire la differenza tra mappedBye inversedBy.

Quando dovrei usarlo mappedBy?

Quando dovrei usarlo inversedBy?

Quando dovrei usare nessuno dei due?

Ecco un esempio:

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

Ho fatto una rapida ricerca e ho trovato quanto segue, ma sono ancora confuso:

Risposte:


159
  • mappedBy deve essere specificato sul lato inverso di un'associazione (bidirezionale)
  • inversedBy deve essere specificato sul lato proprietario di un'associazione (bidirezionale)

dalla documentazione della dottrina:

  • ManyToOne è sempre il lato proprietario di un'associazione bidirezionale.
  • OneToMany è sempre il rovescio di un'associazione bidirezionale.
  • Il lato proprietario di un'associazione OneToOne è l'entità con la tabella contenente la chiave esterna.

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


1
Stranamente il documentatore di Doctrine ha deciso di tralasciare l'esempio yaml di una mappatura bidirezionale molti-a-uno, probabilmente la più comunemente usata!
Peter Wooster

4
@PeterWooster, la best practice consiste nell'usare le annotazioni, poiché tutte le informazioni sull'entità sono in un unico posto!
Andreas Linden

Questo vale anche per molti a molti rapporti. Per quelli: puoi scegliere tu stesso il lato proprietario di un'associazione molti-a-molti.
MB

5
@AndreasLinden ampiamente utilizzato non significa migliore pratica. Qualcosa che usa i commenti per scrivere codice al volo non può mai essere considerato una best practice, non è nativo di php e nemmeno incluso di default in tutti i framework. Avere tutte le informazioni su un'entità in un unico posto è un anti-argomento. Da quando raggruppare tutto il tuo codice in un unico posto è una buona cosa? È un dolore scrivere, mantenere e ridurre l'organizzazione nel tuo progetto. la migliore pratica ? Duh.
JesusTheHun

4
@JesusTheHun stai confrontando mele e pere. "tutto il codice" è molto molto diverso da "tutte le informazioni su un'entità";)
Andreas Linden

55

Le risposte di cui sopra non erano sufficienti per capire cosa stava succedendo, quindi dopo aver approfondito l'argomento penso di avere un modo per spiegarlo che avrà senso per le persone che hanno lottato come ho fatto io per capire.

inversedBy e mappedBy vengono utilizzati dal motore INTERNAL DOCTRINE per ridurre il numero di query SQL che deve eseguire per ottenere le informazioni necessarie. Per essere chiari se non aggiungi inversedBy o mappedBy il tuo codice continuerà a funzionare ma non sarà ottimizzato .

Quindi, ad esempio, guarda le classi seguenti:

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

Queste classi se si dovesse eseguire il comando per generare lo schema (ad esempio, bin/console doctrine:schema:update --force --dump-sql) si noterà che la tabella Categoria non ha una colonna su di essa per le attività. (questo perché non ha un'annotazione di colonna su di esso)

La cosa importante da capire qui è che la variabile task è presente solo in modo che il motore di dottrine interno possa usare il riferimento sopra di esso che dice la sua mappedBy Category. Ora ... non essere confuso qui come lo ero io ... La categoria NON si riferisce AL NOME DELLA CLASSE , si riferisce alla proprietà sulla classe Task chiamata 'protected $ category'.

Come saggio, nella classe Tasks la proprietà $ category menziona che è inversedBy = "tasks", nota che è plurale, NON è IL PLURALE DEL NOME DELLA CLASSE , ma solo perché la proprietà è chiamata 'protected $ tasks' nella Categoria classe.

Una volta capito questo, diventa molto facile capire cosa stanno facendo inversedBy e mappedBy e come usarli in questa situazione.

Il lato che fa riferimento alla chiave esterna come 'attività' nel mio esempio ottiene sempre l'attributo inversedBy perché ha bisogno di sapere quale classe (tramite il comando targetEntity) e quale variabile (inversedBy =) su quella classe per 'lavorare all'indietro' così da parlare e ottenere le informazioni sulla categoria da. Un modo semplice per ricordarlo è che la classe che avrebbe foreignkey_id è quella che deve essere inversedBy.

Dove come con la categoria, e la sua proprietà $ tasks (che non è sulla tabella, ricorda, solo una parte della classe per scopi di ottimizzazione) è MappedBy 'task', questo crea ufficialmente la relazione tra le due entità in modo che la dottrina possa ora tranquillamente utilizzare istruzioni SQL JOIN invece di due istruzioni SELECT separate. Senza mappedBy, il motore di doctrine non saprebbe dall'istruzione JOIN che creerà quale variabile nella classe "Task" inserire le informazioni sulla categoria.

Spero che questo spieghi un po 'meglio.


6
Questo è molto ben spiegato e grazie per il tuo impegno. Sono passato da Laravel Eloquent alla dottrina e questo è stato difficile per me capire la logica qui. Molto bene. Category is NOT referring TO THE CLASS NAME, its referring to the property on the Task class called 'protected $category'tutto ciò di cui avevo bisogno. Non solo ha risolto il mio problema, ma mi ha aiutato a capire. La migliore risposta IMO :-)
The Alpha

1
Anch'io vengo da Eloquent, questo mi ha aiutato molto. il mio unico punto critico ora è come impostare il setter / getter per questo, sto ancora imparando le basi su di esso
Eman

1
Sì, è davvero facile e persino automatizzato con alcuni strumenti là fuori, inizia una chat con me più tardi e posso aiutarti
Joseph Astrahan

1
Dai un'occhiata a questa risposta, stackoverflow.com/questions/16629397/…
Joseph Astrahan,

1
symfony.com/doc/current/doctrine/associations.html , in realtà è meglio imparare, symfony usa dottrine quindi ti insegnerà la stessa cosa.
Joseph Astrahan,

21

Nella relazione bidirezionale ha sia un lato proprietario che un lato inverso

mappedBy : messo in Il lato inverso di una relazione bidirezionale Per fare riferimento al suo lato proprietario

inversedBy : messo in Il lato proprietario di una relazione bidirezionale Per fare riferimento al suo lato inverso

E

attributo mappedBy utilizzato con la dichiarazione di mappatura OneToOne, OneToMany o ManyToMany.

inversedBy attributo utilizzato con l'OnetoOne, ManyToOne, o dichiarazione di mappatura ManyToMany.

Avviso : il lato proprietario di una relazione bidirezionale è il lato che contiene la chiave esterna.

ci sono due riferimenti su inversedBy e mappedBy nella documentazione di Doctrine: First Link , Second Link


1
i collegamenti sono morti?
Scaramouche

2

5.9.1. Proprietà e lato inverso

Per le associazioni molti-a-molti puoi scegliere quale entità è proprietaria e quale è il lato inverso. Esiste una regola semantica molto semplice per decidere quale lato è più adatto a essere il lato proprietario dal punto di vista degli sviluppatori. Devi solo chiederti quale entità è responsabile della gestione della connessione e sceglierla come proprietaria.

Prendiamo un esempio di due entità Articolo e Tag. Ogni volta che si desidera collegare un articolo a un tag e viceversa, è principalmente l'articolo che è responsabile di questa relazione. Ogni volta che aggiungi un nuovo articolo, desideri collegarlo a tag esistenti o nuovi. Il modulo di creazione dell'articolo probabilmente supporterà questa nozione e consentirà di specificare direttamente i tag. Questo è il motivo per cui dovresti scegliere l'articolo come proprietario, poiché rende il codice più comprensibile:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.