Jaka jest różnica między inversedBy i mappedBy?

103

Tworzę swoją aplikację przy użyciu Zend Framework 2 i Doctrine 2.

Pisząc adnotacje, nie jestem w stanie zrozumieć różnicy między mappedByi inversedBy.

Kiedy stosować mappedBy?

Kiedy stosować inversedBy?

Kiedy nie należy ich używać?

Oto przykład:

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

Zrobiłem szybkie wyszukiwanie i znalazłem następujące, ale nadal jestem zdezorientowany:

Deweloper
źródło

Odpowiedzi:

159
  • mappedBy należy określić po odwrotnej stronie asocjacji (dwukierunkowej)
  • inversedBy należy określić po stronie będącej właścicielem (dwukierunkowej) asocjacji

z dokumentacji doktrynalnej:

  • ManyToOne jest zawsze właścicielem dwukierunkowego powiązania.
  • OneToMany jest zawsze odwrotną stroną powiązania dwukierunkowego.
  • Stroną będącą właścicielem asocjacji OneToOne jest jednostka z tabelą zawierającą klucz obcy.

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

Andreas Linden
źródło
1
Co dziwne, dokumenter Doctrine zdecydował się pominąć przykład yaml dwukierunkowego mapowania wiele do jednego, prawdopodobnie najczęściej używanego!
Peter Wooster,
4
@PeterWooster, najlepszą praktyką jest używanie adnotacji, ponieważ masz wtedy wszystkie informacje o encji w jednym miejscu!
Andreas Linden
Dotyczy to również wielu do wielu relacji. Dla nich: możesz samodzielnie wybrać stronę będącą właścicielem asocjacji wiele do wielu.
MB
5
@AndreasLinden powszechnie używane nie oznacza najlepszych praktyk. Coś używającego komentarzy do pisania kodu w locie nigdy nie było uważane za najlepszą praktykę, nie jest natywne dla php, a nawet nie jest domyślnie zawarte we wszystkich frameworkach. Posiadanie wszystkich informacji o jednostce w jednym miejscu jest anty-argumentacją. Od kiedy zgrupowanie całego kodu w jednym miejscu to dobra rzecz? Trudno jest pisać, utrzymywać i ograniczać organizację projektu. najlepsze praktyki ? Duh.
JesusTheHun
4
@JesusTheHun porównujesz jabłka i gruszki. „cały kod” bardzo różni się od „wszystkich informacji o jednostce”;)
Andreas Linden,
56

Powyższe odpowiedzi nie były dla mnie wystarczające, abym zrozumiał, co się dzieje, więc po dokładniejszym zagłębieniu się w to myślę, że mam sposób, aby to wyjaśnić, który będzie miał sens dla ludzi, którzy zmagali się ze zrozumieniem tak, jak ja.

inversedBy i mappedBy są używane przez silnik INTERNAL DOCTRINE w celu zmniejszenia liczby zapytań SQL, które musi wykonać, aby uzyskać potrzebne informacje. Dla jasności, jeśli nie dodasz inversedBy lub mappedBy, twój kod będzie nadal działał, ale nie zostanie zoptymalizowany .

Na przykład spójrz na poniższe klasy:

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

Te klasy, jeśli uruchomisz polecenie do wygenerowania schematu (na przykład bin/console doctrine:schema:update --force --dump-sql), zauważysz, że tabela Kategoria nie zawiera kolumny dla zadań. (dzieje się tak, ponieważ nie ma na sobie adnotacji w kolumnie)

Ważną rzeczą do zrozumienia jest to, że zmienne zadania są dostępne tylko po to, aby wewnętrzny silnik doktryny mógł użyć odniesienia powyżej niego, które mówi, że jego kategoria mappedBy. Teraz ... nie daj się zmylić, tak jak ja ... Kategoria NIE odnosi się do NAZWY KLASY , ale odnosi się do właściwości klasy Task o nazwie „protected $ category”.

Podobnie jak mądrze, w klasie Tasks właściwość $ category wspomina, że ​​jest inversedBy = "zadania", zauważ, że jest to liczba mnoga, NIE JEST TO LICZBA LICZBOWA NAZWY KLASY , ale tylko dlatego, że właściwość ta nosi nazwę "chronione $ zadania" w kategorii klasa.

Kiedy już to zrozumiesz, bardzo łatwo będzie zrozumieć, co robią inversedBy i mappedBy oraz jak ich używać w tej sytuacji.

Strona, która odwołuje się do klucza obcego, taka jak `` task '' w moim przykładzie, zawsze otrzymuje atrybut inversedBy, ponieważ musi wiedzieć, która klasa (za pomocą polecenia targetEntity) i jaka zmienna (inversedBy =) tej klasy ma `` działać wstecz '', aby mówić i uzyskać informacje o kategorii. Łatwym sposobem na zapamiętanie tego jest to, że klasa, która miałaby klucz obcy_id, jest tą, która musi mieć parametr inversedBy.

Tam, gdzie tak jak w przypadku category, a jej właściwość $ task (której nie ma w tabeli pamiętajcie, tylko część klasy do celów optymalizacji) to MappedBy 'task', tworzy to oficjalnie relację między dwoma bytami, tak aby doktryna mogła teraz bezpiecznie użyj instrukcji JOIN SQL zamiast dwóch oddzielnych instrukcji SELECT. Bez mappedBy silnik doktryny nie wiedziałby z instrukcji JOIN, że utworzy zmienną w klasie „Zadanie”, aby umieścić informacje o kategorii.

Mam nadzieję, że to wyjaśnia to trochę lepiej.

Joseph Astrahan
źródło
6
Jest to bardzo dobrze wyjaśnione i dziękuję za twój wysiłek. Przybyłem z Laravel Eloquent do doktryny i trudno mi było zrozumieć tę logikę. Dobra robota. Category is NOT referring TO THE CLASS NAME, its referring to the property on the Task class called 'protected $category'wszystko, czego potrzebowałem. To nie tylko rozwiązało mój problem, ale pomogło mi to zrozumieć. Najlepsza odpowiedź IMO :-)
Alpha
1
Ja też pochodzę z Eloquent, co bardzo mi pomogło. moim jedynym punktem spornym jest teraz ustawienie setera / gettera do tego, wciąż uczę się na tym lin
Eman
1
Tak, to jest naprawdę łatwe, a nawet zautomatyzowane z niektórymi narzędziami, zacznij ze mną rozmawiać później, a mogę ci pomóc
Joseph Astrahan
1
Sprawdź tę odpowiedź, stackoverflow.com/questions/16629397/ ...
Joseph Astrahan
1
symfony.com/doc/current/doctrine/associations.html , właściwie tego lepiej się nauczyć, symfony używa doktryny, więc nauczy Cię tego samego.
Joseph Astrahan
21

W relacji dwukierunkowej ma zarówno stronę właścicielską, jak i odwrotną

mappedBy : put into Odwrotna strona relacji dwukierunkowej Odnosi się do strony będącej jej właścicielem

inversedBy : put into Strona będąca właścicielem relacji dwukierunkowej Odnosi się do jej odwrotnej strony

I

mappedBy atrybut używany z deklaracją mapowania OneToOne, OneToMany lub ManyToMany.

InversedBy atrybut używany z deklaracją mapowania OneToOne, ManyToOne lub ManyToMany.

Uwaga : Strona będąca właścicielem relacji dwukierunkowej, strona zawierająca klucz obcy.

są tam dwie wzmianki o inversedBy i mappedBy w Doctrine Documentation: First Link , Second Link

ahmed hamdy
źródło
1
linki są martwe?
Scaramouche,
2

5.9.1. Posiadanie i odwrotna strona

W przypadku asocjacji wiele do wielu możesz wybrać, który byt jest właścicielem, a który odwrotnie. Z punktu widzenia deweloperów istnieje bardzo prosta reguła semantyczna, która decyduje, która strona jest bardziej odpowiednia do pełnienia roli właściciela. Wystarczy zadać sobie pytanie, który podmiot jest odpowiedzialny za zarządzanie połączeniem i wybrać go jako właściciela.

Weź przykład dwóch podmiotów Artykuł i Tag. Ilekroć chcesz połączyć artykuł z tagiem i odwrotnie, to głównie artykuł jest odpowiedzialny za tę relację. Za każdym razem, gdy dodajesz nowy artykuł, chcesz połączyć go z istniejącymi lub nowymi tagami. Twój formularz tworzenia artykułu prawdopodobnie będzie wspierał to pojęcie i pozwoli bezpośrednio określić tagi. Dlatego powinieneś wybrać artykuł jako stronę właściciela, ponieważ dzięki temu kod jest bardziej zrozumiały:

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

Micheal Mouner Mikhail Youssif
źródło