Kdyby/Doctrine – jak na M:N vazbu

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Trsak
Člen | 22
+
0
-

Zdravím,
jaký je nejlepší způsob pro práci v ORM s M:N vazbou?
Jak:

  1. Vytvořit entity
  2. Vkládat data
  3. Vybírat data

Aktálně moje entity vypadají asi takto:

<?php
namespace App;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class Movie extends \Kdyby\Doctrine\Entities\BaseEntity
{

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue
     */
    protected $id;

    /**
     * @ORM\ManyToMany(targetEntity="MovieGenres")
     * @ORM\JoinTable(name="movie_has_genres",
     *      joinColumns={@ORM\JoinColumn(name="movie_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="genre_id", referencedColumnName="id")}
     *      )
     */
    private $genres;
}
<?php
namespace App;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class MovieGenres extends \Kdyby\Doctrine\Entities\BaseEntity
{

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue
     */
    protected $id;

    /**
     * @ORM\Column(type="string")
     */
    protected $genre;
}

Ale není mi zcela jasné, jak s tím niní správně a efektivně pracovat.

Aurielle
Člen | 1281
+
+2
-

Já třeba přímé M:N vazby nepoužívám a vždycky si přidám přímo entitu, která slouží jako vazební tabulka. Nemusím pak předělávat tolik věcí, pokud se u té vazby rozhodnu uchovávat další parametry.

Trsak
Člen | 22
+
0
-

@Aurielle Díky, chtěl jsem to tak udělat, ale doufal jsem v nějaký „pohodlnější“ způsob :).

Svaťa Šimara
Člen | 98
+
0
-

Neexistuje univerzální způsob, každý je vhodný pro jiné použití. Záleží, jak si danou situaci namodeluješ. M:N vazby jsou v reálném světě často přirozeně obousměrné, což je skvěle volné, ale opruz pro implementaci.

Jak se dívám na Tvůj příklad, tak vidím, že modeluješ:

[Movie] ---> 0..* [MovieGenres]

MovieGenres je entita nezávislá na okolí (což je super), a Movie je asociováno k 0..* MovieGenres. Pokud si rozhodneš, že do této asociace nechceš nic zaznamenávat, pak nemá smysl používat další entitu. Nechal bych to přesně, jak máš implementováno. Doctrine Ti vytvoří vazební tabulku, což je ok.

Můžeš volat: $movie->addGenres($genres)

Pokud se časem rozhodneš, že vazba bude mít něco navíc, například váhu, pak se Ti rozpadne model třeba na:

[Movie] ---> 0..* [AssociatedGenres] ---> [MovieGenres]

Ale opět, co se stalo. Mám možnost k Movie přiřadit více AssociatedGenres (které povinně obsahuje vazbu do MovieGenres, a má vlastnost „váha“). MovieGeneres zůstává krásně volné – po refaktoringu jej nemusím měnit. A stejně bude existovat vazební tabulka movie_has_associated_generes.

Můžeš volat: $movie->addGenres($genres, $weight) s tím, že zvenku o entitě AssociatedGenres nebude nikdo vědět, půjde o entitu vlastněnou čistě Movie, a pak si můžeš dovolit finty jako http://doctrine-orm.readthedocs.org/…iations.html#…

Pokud bych od začátku modeloval:

[Movie] <--- [AssociatedGenres] ---> [MovieGenres]

Pak jde o zvláštní model, Movie neví nic o MovieGeneres, což nejspíš není cílový stav, nebo je? Pokud jo, tak se pletu. Pokud bych v Movie chtěl pracovat s MovieGenres, nemám šanci. Něco jako $movie->addGenres($genres) v tomto modelu nezařídím. Leda, že bych vazbu mezi Movie a AssociatedGenres udělal oboustrannou:

[Movie] 0..1 --- 0..* [AssociatedGenres] ---> [MovieGenres]

Do tohoto bych ale nešel, protože udržovat oboustranné asociace je dost drsné, a i Doctrine pro ně má svá omezení, která jsou logická http://doctrine-orm.readthedocs.org/…iations.html