Enviar un formulario mediante GET
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 SubscribeAhora tenemos un formulario de búsqueda. Pero hay un pequeño problema. Se envía a través de POST porque es el método por defecto de Symfony. Normalmente, eso está muy bien. Pero para un formulario de búsqueda, POST no es la mejor opción. ¿Por qué? Porque cuando buscamos, queremos que la consulta sea visible en la URL. Hace que los resultados de la página se puedan compartir y marcar como favoritos. Además, es como nuestro antiguo formulario. Así que vamos a modificarlo y cambiar nuestro formulario a GET.
Para empezar, abre nuestra clase PartSearchType. En setDefaults()podemos ajustar las opciones de nuestro formulario. En la matriz vacía, añade una nueva clave llamadamethod y establécela en Request::METHOD_GET. En realidad, es sólo una forma rara de escribir la cadena GET:
| // ... lines 1 - 6 | |
| use Symfony\Component\HttpFoundation\Request; | |
| // ... lines 8 - 9 | |
| class PartSearchType extends AbstractType | |
| { | |
| // ... lines 12 - 24 | |
| public function configureOptions(OptionsResolver $resolver): void | |
| { | |
| $resolver->setDefaults([ | |
| // Configure your form options here | |
| 'method' => Request::METHOD_GET, | |
| ]); | |
| } | |
| } |
Tómate un momento para enviar el nuevo formulario con "Legacy". Fíjate en la URL. Los parámetros de consulta parecen un poco locos. En lugar del simple query=legacy, vemos parts_search[query]=legacy - esos %5B y %5D's son simplemente [] codificados .
¿Qué ocurre? Por defecto, los formularios Symfony envían los datos como matrices anidadas, con la clave del nombre del formulario. Esto ayuda a evitar colisiones de nombres cuando tienes varios formularios en la misma página. Es un movimiento inteligente, pero para nuestro formulario de búsqueda, es exagerado y, seamos sinceros, un poco feo.
Limpiar los parámetros de consulta
Esto es lo que haremos. Queremos aplanar los parámetros de consulta. Para deshacernos de este prefijo de nombre de formulario, podemos anular un método especial en el tipo de formulario.
De vuelta en PartSearchType, al final de esta clase, pulsaréCmd + N y elegiré "Anular métodos", luego elegiré el método getBlockPrefix(). Dentro de él, simplemente devuelve una cadena vacía:
| // ... lines 1 - 9 | |
| class PartSearchType extends AbstractType | |
| { | |
| // ... lines 12 - 32 | |
| public function getBlockPrefix(): string | |
| { | |
| return ''; | |
| } | |
| } |
Esto le dice a Symfony que no anteponga a nuestros campos el nombre del formulario. Vuelve a buscar y ¡bam! Tenemos parámetros de consulta limpios y planos. ¡Mucho mejor!
Pero aún no hemos llegado al final. Tenemos esta consulta _token en la URL. Es la protección CSRF haciendo su trabajo, pero es innecesaria para un formulario de búsqueda basado en GET. Sólo estamos filtrando una lista de productos, así que vamos a desactivarla para este formulario en setDefaults().
Para ello, vuelve a PartSearchType y añade una nueva opción llamadacsrf_protection, estableciéndola en false:
| // ... lines 1 - 9 | |
| class PartSearchType extends AbstractType | |
| { | |
| // ... lines 12 - 24 | |
| public function configureOptions(OptionsResolver $resolver): void | |
| { | |
| $resolver->setDefaults([ | |
| // ... lines 28 - 29 | |
| 'csrf_protection' => false, | |
| ]); | |
| } | |
| // ... lines 33 - 37 | |
| } |
Vuelve a enviar el formulario y ¡bingo! Sólo tenemos el parámetro query en la URL, limpia e intencionadamente.
Hacer que el campo de búsqueda sea opcional
¿Qué pasa si intentamos enviar el formulario con una consulta vacía? Hmm, un error de validación HTML5... Puede que eso esté bien para tu aplicación, pero yo preferiría que una búsqueda vacía significara "Muéstramelo todo". Podemos desactivar la validación utilizando el atributo novalidateen la etiqueta del formulario, pero una opción mejor es simplemente hacer que el campo sea opcional. Para el campo query, añade otro parámetro llamado requiredy ajústalo a false:
| // ... lines 1 - 9 | |
| class PartSearchType extends AbstractType | |
| { | |
| public function buildForm(FormBuilderInterface $builder, array $options): void | |
| { | |
| $builder | |
| ->add('query', null, [ | |
| 'required' => false, | |
| // ... lines 17 - 21 | |
| ]) | |
| ; | |
| } | |
| // ... lines 25 - 38 | |
| } |
Actualiza la página, y si intentamos buscar con una entrada vacía, simplemente muestra la lista completa.
Sinceramente, ahora mismo, la búsqueda funciona debido a cierta lógica de negocio heredada en PartController. Concretamente, estamos obteniendo la consulta directamente de la petición con $request->query->getString('query'):
| // ... lines 1 - 11 | |
| final class PartController extends AbstractController | |
| { | |
| // ... line 14 | |
| public function index(StarshipPartRepository $repository, Request $request,): Response | |
| { | |
| // ... line 17 | |
| $query = $request->query->getString('query'); | |
| // ... lines 19 - 25 | |
| } | |
| } |
Y para un pequeño formulario de búsqueda, eso está totalmente bien. Pero ya que estamos aprendiendo el componente Formulario de Symfony, hagámoslo a la manera de Symfony.
Manejo adecuado de formularios en Symfony
Comenta esa línea. A continuación, inicializa una nueva variable query con el valor null. A continuación, dile al formulario que gestione la petición con$searchForm->handleRequest($request):
| // ... lines 1 - 11 | |
| final class PartController extends AbstractController | |
| { | |
| // ... line 14 | |
| public function index(StarshipPartRepository $repository, Request $request,): Response | |
| { | |
| // ... line 17 | |
| //$query = $request->query->getString('query'); | |
| $query = null; | |
| $searchForm->handleRequest($request); | |
| // ... lines 21 - 30 | |
| } | |
| } |
Añade nuestra comprobación habitual de envío de formularios. if ($searchForm->isSubmitted()&& $searchForm->isValid()) . El campo query se considera no mapeado porque el formulario no está asociado a un objeto. Dentro de if, establece la variable$query en $searchForm->get('query')->getData():
| // ... lines 1 - 11 | |
| final class PartController extends AbstractController | |
| { | |
| // ... line 14 | |
| public function index(StarshipPartRepository $repository, Request $request,): Response | |
| { | |
| // ... line 17 | |
| //$query = $request->query->getString('query'); | |
| $query = null; | |
| $searchForm->handleRequest($request); | |
| if ($searchForm->isSubmitted() && $searchForm->isValid()) { | |
| $query = $searchForm->get('query')->getData(); | |
| } | |
| // ... lines 24 - 30 | |
| } | |
| } |
Esto captura el campo de consulta y recupera su valor enviado.
Tip
Para obtener todos los datos del formulario a la vez, como una matriz, puedes utilizar$searchForm->getData().
Toques finales y potencia turbo
Ya casi está. Hagamos el último retoque en la plantilla. Copia el icono SVG y colócalo debajo de todo el formulario. El posicionamiento CSS adecuado debería seguir funcionando, así que no necesitamos colocarlo entre las etiquetas de apertura y cierre del formulario. Ahora ya podemos eliminar por completo el antiguo formulario:
| // ... lines 1 - 4 | |
| {% block body %} | |
| <div class="flex justify-end mt-6 mb-6"> | |
| <div class="relative w-full max-w-md"> | |
| {{ form(searchForm) }} | |
| <svg class="absolute left-3 top-3 w-5 h-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-4.35-4.35m0 0A8.5 8.5 0 1011 19.5a8.5 8.5 0 005.65-2.85z" /> | |
| </svg> | |
| </div> | |
| </div> | |
| // ... lines 15 - 39 | |
| {% endblock %} |
Actualiza la página de nuevo para ver nuestro formulario, y si lo buscamos, ¡todavía funciona! Tiene el mismo aspecto, pero ahora funciona completamente con Symfony Forms.
De vuelta en PartSearchType.php, una última mejora. Por defecto, si no especificas el tipo de campo, Symfony utiliza TextType. Pero como se trata de un campo de búsqueda, seamos un poco más semánticos y cambiémoslo por uno especial SearchType:
| // ... lines 1 - 5 | |
| use Symfony\Component\Form\Extension\Core\Type\SearchType; | |
| // ... lines 7 - 10 | |
| class PartSearchType extends AbstractType | |
| { | |
| public function buildForm(FormBuilderInterface $builder, array $options): void | |
| { | |
| $builder | |
| ->add('query', SearchType::class, [ | |
| // ... lines 17 - 23 | |
| ; | |
| } | |
| // ... lines 26 - 39 | |
| } |
De vuelta en el navegador, actualiza de nuevo la página. A primera vista no ha cambiado nada, pero si empiezas a escribir, hay un pequeño y práctico botón X que te permite borrar fácilmente la entrada.
Activar Turbo en el sitio web
Si recuerdas, desactivé Turbo al principio de este curso para simplificar las cosas. Pero ahora es el momento de desatar su poder. Abre assets/app.js y descomenta import '@hotwired/turbo';:
| // ... line 1 | |
| // import '@hotwired/turbo'; | |
| // ... lines 3 - 13 |
Ahora, la navegación por el sitio se realiza mediante AJAX con Turbo. Puedes verlo en acción en la barra de herramientas de depuración web al hacer clic en los enlaces. El envío de formularios también se realiza mediante Turbo, y los errores de formulario siguen funcionando perfectamente.
En la lista de piezas, si vas a "Crear nueva pieza" e intentas enviar el formulario vacío... Sí, es una petición AJAX y seguimos viendo los errores de validación esperados. Turbo hace que nuestra aplicación sea más rápida, fluida e inmersiva. ¡Y esto es sólo el principio!
Go Deeper!
Si quieres profundizar más, te recomiendo que eches un vistazo a nuestro [curso Turbo] independiente (https://symfonycasts.com/screencast/turbo).
Para terminar
¡Muy bien, amigos! Ya dominas oficialmente los fundamentos del componente Formulario de Symfony. Ya sabes cómo instalarlo y configurarlo, crear tipos de formularios, procesarlos con las funciones de ayuda de Twig y gestionar los envíos correctamente con Form::handleRequest(). Hemos visto cómo se enlazan los formularios con las entidades Doctrine, cómo funciona la validación, cómo personalizar los campos y atributos, y cómo dar estilo a todo con los temas de formulario Symfony incorporados.
Estamos listos para construir formularios reales, listos para producción, con confianza. Así que, ¡a construir formularios increíbles! Son esenciales en tus aplicaciones. Y disfruta de la magia de los formularios Symfony.
hasta la próxima, ¡feliz programación!
7 Comments
maybe on day,you can teach us how to create form theme
Good idea! In the meantime, check out https://github.com/symfony/symfony/pull/40449
This is the PR where I added the tailwind theme.
--Kevin
And again: Please! Please! Include the complete code files with each file you modify. Otherwise, it becomes difficult to track the changes.
FYI - The blocks have been added.
Oh sorry about that. This is the latest chapter we've released, and it doesn't have the code blocks yet
When I use the "finish" version, the following error appears when I run the project:
Fatal error: Uncaught Error: Failed opening required 'C:\Proyectos\formularios2026finish/vendor/autoload_runtime.php' (include_path='C:\xampp\php\PEAR') in C:\Proyectos\formularios2026finish\public\index.php:5 Stack trace: #0 {main} thrown in C:\Proyectos\formularios2026finish\public\index.php on line 5
What's happening?
Hey @giorgiocba
Can you double-check that the
vendor/autoload_runtime.phpfile exists under that directory? I believe you forgot to runcomposer install, or, if that's not the case, your vendors were installed incorrectly for some reason. You can try deleting the folder and installing them againCheers!
"Houston: no signs of life"
Start the conversation!