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 SubscribeHemos conseguido cambiar el valor de la propiedad votes
. Ahora tenemos que hacer una consulta de actualización para guardarlo en la base de datos.
Para insertar un VinylMix
, utilizamos el servicio EntityManagerInterface
, y luego llamamos apersist()
y flush()
. Para actualizar, utilizaremos exactamente el mismo servicio.
Añade un nuevo argumento al método vote()
, indicado con EntityManagerInterface
. Lo llamaré $entityManager
. Luego, muy sencillamente, después de haber establecido la propiedad votes
con el nuevo valor, llama a $entityManager->flush()
.
... lines 1 - 12 | |
class MixController extends AbstractController | |
{ | |
... lines 15 - 44 | |
public function vote(VinylMix $mix, Request $request, EntityManagerInterface $entityManager): Response | |
{ | |
... lines 47 - 53 | |
$entityManager->flush(); | |
... line 55 | |
} | |
} |
¡Eso es todo, gente! Antes de explicar esto, vamos a asegurarnos de que funciona. Actualiza. Ahora mismo tenemos 49 votos. Voy a pulsar arriba. Dice 50. Pero la verdadera prueba es que cuando refrescamos... ¡sigue mostrando 50! ¡Se ha guardado!
Vale, cuando antes creamos un nuevo VinylMix
, tuvimos que llamar a persist()
-pasando el objeto VinylMix
- y luego a flush()
. Pero ahora, lo único que necesitamos es flush()
. ¿Por qué?
Esta es la historia completa. Cuando llamas a flush()
, Doctrine hace un bucle sobre todos los objetos de entidad que "conoce" y los "guarda". Y ese "guardado" es inteligente. Si Doctrine determina que una entidad no se ha guardado todavía, ejecutará una consulta INSERT, pero si se trata de un objeto que ya existe en la base de datos, Doctrine averiguará qué ha cambiado en el objeto -si es que ha cambiado algo- y ejecutará una consulta UPDATE
. ¡Sí! Sólo tenemos que llamar a flush()
y Doctrine averiguará qué hacer. Es... lo mejor desde las gominolas Starburst.
Pero... ¿por qué no tenemos que llamar a persist()
cuando estamos actualizando? Bueno, puedes decir $entityManager->persist($mix)
si quieres. Es que es... ¡totalmente redundante!
Cuando llamas a persist()
, le dice a Doctrine:
Quiero que conozcas este objeto para que, la próxima vez que llame a
flush()
, sepas que debes guardarlo.
Cuando creas un nuevo objeto entidad, Doctrine no conoce realmente ese objeto hasta que llamas a persist()
. Pero cuando actualizas una entidad, significa que ya has pedido a Doctrine que consulte por ese objeto. Así que Doctrine ya lo conoce... y cuando llamemos a flush()
, Doctrine comprobará -de forma automática- ese objeto para ver si se ha realizado algún cambio en él.
Así que... ¡hemos guardado con éxito el nuevo recuento de votos en la base de datos! ¿Y ahora qué? Porque... No creo que esta declaración die
vaya a quedar bien en producción.
Bueno, cada vez que se envía un formulario con éxito, siempre se hace lo mismo: redirigir a otra página. ¿Cómo redirigimos en Symfony? Conreturn $this->redirect()
pasando la URL a la que quieras redirigir. Aunque, por lo general, estamos redirigiendo a otra página de nuestro sitio... así que utilizamos un atajo similar llamado redirectToRoute()
y luego pasamos un nombre de ruta.
Vamos a redirigir a la página del programa. Copia el nombre de la ruta app_mix_show
, pégalo... y al igual que con la función Twig path()
, ésta acepta un segundo argumento: una matriz de los comodines de la ruta que debemos rellenar. En este caso, tenemos un comodín{id}
... así que pasa id
ajustado a $mix->getId()
.
... lines 1 - 44 | |
public function vote(VinylMix $mix, Request $request, EntityManagerInterface $entityManager): Response | |
{ | |
... lines 47 - 55 | |
return $this->redirectToRoute('app_mix_show', [ | |
'id' => $mix->getId(), | |
]); | |
} | |
... lines 60 - 61 |
Ahora, recuerda: los controladores siempre devuelven un objeto Response
. Y resulta que una redirección es una respuesta. Es una respuesta que, en lugar de contener HTML, básicamente dice
Por favor, envía al usuario a esta otra URL
El método redirectToRoute()
es un atajo que devuelve este objeto de respuesta especial, llamado RedirectResponse
.
De todos modos, ¡probemos todo el flujo! Refresca, y... ¡lo tienes! Después de votar, acabamos de nuevo en esta página. Y, gracias a Turbo, todo esto sucede a través de llamadas Ajax... lo cual es un buen plus.
El único problema es que... es tan suave que no es súper obvio que mi voto se haya guardado realmente, aparte de ver cómo cambia el número de voto. Sería mejor si mostráramos un mensaje de éxito. Vamos a hacer eso a continuación, aprendiendo sobre los mensajes flash. También vamos a hacer más moderna nuestra entidad VinylMix
explorando el concepto de modelos inteligentes frente a los anémicos.
Hey Thephilosoft,
Yes, you're totally correct, the price should be changed to make the 2nd query, otherwise the Doctrine is smart enough to ignore the last flush()
call. Thank you for reporting it, I changed the price in the last setPrice()
call to avoid editing the explanation message.
Cheers!
// composer.json
{
"require": {
"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
"babdev/pagerfanta-bundle": "^3.7", // v3.7.0
"doctrine/doctrine-bundle": "^2.7", // 2.7.0
"doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
"doctrine/orm": "^2.12", // 2.12.3
"knplabs/knp-time-bundle": "^1.18", // v1.19.0
"pagerfanta/doctrine-orm-adapter": "^3.6", // v3.6.1
"pagerfanta/twig": "^3.6", // v3.6.1
"sensio/framework-extra-bundle": "^6.2", // v6.2.6
"stof/doctrine-extensions-bundle": "^1.7", // v1.7.0
"symfony/asset": "6.1.*", // v6.1.0
"symfony/console": "6.1.*", // v6.1.2
"symfony/dotenv": "6.1.*", // v6.1.0
"symfony/flex": "^2", // v2.2.2
"symfony/framework-bundle": "6.1.*", // v6.1.2
"symfony/http-client": "6.1.*", // v6.1.2
"symfony/monolog-bundle": "^3.0", // v3.8.0
"symfony/proxy-manager-bridge": "6.1.*", // v6.1.0
"symfony/runtime": "6.1.*", // v6.1.1
"symfony/twig-bundle": "6.1.*", // v6.1.1
"symfony/ux-turbo": "^2.0", // v2.3.0
"symfony/webpack-encore-bundle": "^1.13", // v1.15.1
"symfony/yaml": "6.1.*", // v6.1.2
"twig/extra-bundle": "^2.12|^3.0", // v3.4.0
"twig/twig": "^2.12|^3.0" // v3.4.1
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
"symfony/debug-bundle": "6.1.*", // v6.1.0
"symfony/maker-bundle": "^1.41", // v1.44.0
"symfony/stopwatch": "6.1.*", // v6.1.0
"symfony/web-profiler-bundle": "6.1.*", // v6.1.2
"zenstruck/foundry": "^1.21" // v1.21.0
}
}
In a challenge after this video. Shouldn't
1
be the answer? Or at least there should be a call forsetPrice
with a different value...