Añadir elementos a una propiedad de colección
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeVamos a buscar un único usuario en nuestra API: Sé que existe uno con ID 2. Y ¡genial!
Como hemos aprendido antes, exponer una propiedad de relación de colección es como cualquier otro campo: simplemente asegúrate de que está en el grupo de serialización correcto. Y luego puedes ir más allá con los grupos de serialización para elegir entre hacer que se devuelva como una matriz de cadenas IRI o como una matriz de objetos incrustados, como tenemos ahora.
Nueva pregunta: ¿podríamos modificar también el dragonTreasures
que posee un usuario desde una de las operaciones de usuario? La respuesta es, por supuesto, ¡sí! Y vamos a hacerlo de formas cada vez más locas.
Hacer que el campo Colección sea escribible
Mira la ruta POST. Ahora mismo no vemos un campo dragonTreasures
porque... el campo simplemente no es escribible: no está en el grupo correcto. Para remediarlo, sabemos qué hacer: añadir user:write
.
// ... lines 1 - 22 | |
class User implements UserInterface, PasswordAuthenticatedUserInterface | |
{ | |
// ... lines 25 - 51 | |
'user:read', 'user:write']) | ([|
private Collection $dragonTreasures; | |
// ... lines 54 - 170 | |
} |
¡Muy fácil! Cuando actualizamos los documentos y comprobamos esa ruta... ya está:dragonTreasures
. Y dice que este campo debe ser una matriz de cadenas: una matriz de cadenas IRI.
Intentemos crear un nuevo usuario. Rellenemos los campos email
y username
. A continuación, asignemos el nuevo usuario a algunos tesoros existentes. Vamos a colarnos en la ruta GET de colección de tesoros... e impresionante. Tenemos los ids 2, 3 y 4.
Aquí abajo, asigna owner
a una matriz con /api/treasures/2
, /api/treasures/3
y /api/treasures/4
.
Tiene sentido, ¿verdad? Si la API puede devolver dragonTreasures
como una matriz de cadenas IRI, ¿por qué no podemos enviar una matriz de cadenas IRI? Cuando pulsamos Ejecutar... ¡efectivamente! ¡Funcionó perfectamente!
Y como cada tesoro sólo puede tener un propietario... ¡eso significa que, en cierto modo, robamos esos tesoros a otra persona! ¡Perdón!
Los métodos sumador y eliminador de colecciones
Pero... espera un segundo, ¿cómo ha funcionado? Sabemos que cuando enviamos campos comoemail
, password
, y username
, como son propiedades privadas, el serializador llama a los métodos setter. Cuando pasamos username
, llama asetUsername()
.
// ... lines 1 - 22 | |
class User implements UserInterface, PasswordAuthenticatedUserInterface | |
{ | |
// ... lines 25 - 134 | |
public function setUsername(string $username): self | |
{ | |
$this->username = $username; | |
return $this; | |
} | |
// ... lines 141 - 170 | |
} |
Así que cuando pasamos dragonTreasures
, debe llamar a setDragonTreasures
, ¿no?
Pues, ¿adivina qué? ¡No tenemos un método setDragonTreasures()
! Pero tenemos un método addDragonTreasure()
y un método removeDragonTreasure()
.
// ... lines 1 - 22 | |
class User implements UserInterface, PasswordAuthenticatedUserInterface | |
{ | |
// ... lines 25 - 149 | |
public function addDragonTreasure(DragonTreasure $treasure): self | |
{ | |
if (!$this->dragonTreasures->contains($treasure)) { | |
$this->dragonTreasures->add($treasure); | |
$treasure->setOwner($this); | |
} | |
return $this; | |
} | |
public function removeDragonTreasure(DragonTreasure $treasure): self | |
{ | |
if ($this->dragonTreasures->removeElement($treasure)) { | |
// set the owning side to null (unless already changed) | |
if ($treasure->getOwner() === $this) { | |
$treasure->setOwner(null); | |
} | |
} | |
return $this; | |
} | |
} |
El serializador es muy inteligente. Ve que el nuevo objeto User
no tienedragonTreasures
. Así que reconoce que cada uno de estos tres objetos son nuevos para este usuario y por eso llama a addDragonTreasure()
una vez para cada uno.
Y la forma en que MakerBundle ha generado estos métodos es fundamental. Toma el nuevo DragonTreasure
y establece que el owner
sea este objeto. Esto es importante por la forma en que Doctrine maneja las relaciones: establecer el propietario establece lo que se denomina el lado "propietario" de la relación. Básicamente, sin esto, Doctrine no guardaría este cambio en la base de datos.
Las conclusiones son que, gracias a addDragonTreasure()
y sus poderes mágicos, el owner
del DragonTreasure
se cambia de su antiguo propietario al nuevo User
, y todo se guarda exactamente como queremos.
A continuación, vamos a ponernos más complejos permitiendo que se creen tesoros cuando estamos creando un nuevo User
. También vamos a permitir que se eliminen tesoros de un User
... para el improbable caso de que los enanos recuperen la montaña. Como si tal cosa.
embedded items are useful, but when compared to graphQL (which can query / embed relations as needed and ad hoc), it seems archaic to program in default behaviour (i.e. to manually setup embed items via apiPlatform config) for an endpoint. This should be mentioned in the video maybe?
I'd love to see a video on setting up graphQL on apiPlatform as it has a lot of advantages over REST endpoints - particularly in multiple relationship retrieval / "embedded" items.