Login to bookmark this video
Buy Access to Course
15.

Relación Muchos-Muchos

|

Share this awesome video!

|

Keep on Learning!

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Muy bien, tenemos una entidad Starship y una entidad Droidconfiguradas y listas para mezclarse. ¿Cómo conseguimos que estas dos entidades se conecten?

Imagínatelo así: Cada Starship va a necesitar un equipo de Droids para que las cosas funcionen sin problemas... y para el alivio cómico ocasional. Cada Droid, a su vez, debería poder servir a muchos Starships. Olvídate de la base de datos y céntrate en los objetos. Nuestra entidad Starshipnecesita una propiedad droids que contenga una colección de todos los Droids que tiene asignados.

¡Genial! Vuelve a tu terminal y ejecuta:

symfony console make:entity

Actualiza Starship y añade una propiedad droids. Utiliza "relación" para entrar en el práctico asistente. Esta vez, necesitamos una relación ManyToMany:

Cada Starship puede tener muchos Droids, y cada Droid puede servir en muchos Starships. ¡Suena perfecto!

A continuación, nos pregunta si queremos mapear el lado inverso de la relación. Esto es preguntarnos si queremos dar a nuestro Droids la capacidad de listar todos los Starships a los que está conectado: $droid->getShips() eso suena útil. Así que digamos "sí". Para el nuevo nombre de campo dentro de Droid, starships servirá perfectamente.

Observa que ha actualizado tanto Starship como Droid. Echa un vistazo a los cambios en cada uno.

La magia "Muchos a muchos

En Starship, ahora tenemos una nueva propiedad droids, que es unaManyToMany. También ha inicializado droids en ArrayCollection y ha añadido los métodos getDroids(), addDroid() y removeDroid():

224 lines | src/Entity/Starship.php
// ... lines 1 - 15
class Starship
{
// ... lines 18 - 50
/**
* @var Collection<int, Droid>
*/
#[ORM\ManyToMany(targetEntity: Droid::class, inversedBy: 'starships')]
private Collection $droids;
public function __construct()
{
// ... line 59
$this->droids = new ArrayCollection();
}
// ... lines 62 - 199
/**
* @return Collection<int, Droid>
*/
public function getDroids(): Collection
{
return $this->droids;
}
public function addDroid(Droid $droid): static
{
if (!$this->droids->contains($droid)) {
$this->droids->add($droid);
}
return $this;
}
public function removeDroid(Droid $droid): static
{
$this->droids->removeElement($droid);
return $this;
}
}

Si estás pensando que esto se parece mucho a una relación OneToMany, ¡ding, ding! ¡Pídete una pizza! ¡Porque lo es!

En Droid, la historia es parecida. Tenemos una propiedad starships, que es una ManyToMany, y se inicializa en el constructor. Luego tenemos las mismasgetStarships(), addStarship(), y removeStarship():

91 lines | src/Entity/Droid.php
// ... lines 1 - 10
class Droid
{
// ... lines 13 - 23
/**
* @var Collection<int, Starship>
*/
#[ORM\ManyToMany(targetEntity: Starship::class, mappedBy: 'droids')]
private Collection $starships;
public function __construct()
{
$this->starships = new ArrayCollection();
}
// ... lines 34 - 63
/**
* @return Collection<int, Starship>
*/
public function getStarships(): Collection
{
return $this->starships;
}
public function addStarship(Starship $starship): static
{
if (!$this->starships->contains($starship)) {
$this->starships->add($starship);
$starship->addDroid($this);
}
return $this;
}
public function removeStarship(Starship $starship): static
{
if ($this->starships->removeElement($starship)) {
$starship->removeDroid($this);
}
return $this;
}
}

Genera la migración para esto. Vuelve al terminal y ejecuta:

symfony console make:migration

Desvelar la tabla de unión

¡Maravilloso! Echa un vistazo a lo que ha generado: es fascinante. ¡Tenemos una nueva tabla llamada starship_droid! Incluye una clave ajena starship_id parastarship y una clave ajena droid_id para droid:

38 lines | migrations/Version20250311014256.php
// ... lines 1 - 12
final class Version20250311014256 extends AbstractMigration
{
// ... lines 15 - 19
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE starship_droid (starship_id INT NOT NULL, droid_id INT NOT NULL, PRIMARY KEY(starship_id, droid_id))');
$this->addSql('CREATE INDEX IDX_1C7FBE889B24DF5 ON starship_droid (starship_id)');
$this->addSql('CREATE INDEX IDX_1C7FBE88AB064EF ON starship_droid (droid_id)');
$this->addSql('ALTER TABLE starship_droid ADD CONSTRAINT FK_1C7FBE889B24DF5 FOREIGN KEY (starship_id) REFERENCES starship (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE starship_droid ADD CONSTRAINT FK_1C7FBE88AB064EF FOREIGN KEY (droid_id) REFERENCES droid (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
}
// ... lines 29 - 36
}

Así es como se estructura una relación ManyToMany en la base de datos: con una tabla join. La verdadera magia de Doctrine es que sólo tenemos que pensar en objetos. Un objeto Starshiptiene muchos objetos Droid, y un objeto Droid tiene muchos objetos Starship. Doctrine se encarga de los tediosos detalles de guardar esa relación en la base de datos.

Antes de continuar, ejecuta esa migración. Vuelve al terminal y hazlo:

symfony console doctrine:migrations:migrate

¡Genial! Ya tenemos una nueva y reluciente tabla de unión. Vale... ¿pero cómo relacionamos los objetos Droid con los objetos Starship? Eso a continuación... ¡y te va a encantar!