Login to bookmark this video
Buy Access to Course
13.

Añadir una búsqueda + el objeto de petición

|

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

Es hora de desviarse un poco de las Relaciones Doctrine. Sé que las Relaciones Doctrine molan, ¡pero esto también! Quiero añadir una barra de búsqueda a nuestra página. Confía en mí, esto va a ser bueno.

Abre la plantilla index.html.twig. Justo en la parte superior, pegaré una entrada de búsqueda:

40 lines | templates/part/index.html.twig
// ... lines 1 - 4
{% block body %}
<div class="flex justify-end mt-6 mb-6">
<div class="relative w-full max-w-md">
<input type="text"
placeholder="Search..."
class="w-full p-3 pl-10 border border-gray-300 rounded-lg shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<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 18 - 38
{% endblock %}

Nada del otro mundo: sólo un <input type="text" "placeholder="search", y luego un puñado de clases y un elegante SVG para que quede bonito.

Para que este chico malo se envíe, envuélvelo en una etiqueta form. Para la acción, haz que se envíe directamente a esta página: {{ path('app_part_index') }}. Añade también name="query" ymethod="get" al formulario:

43 lines | templates/part/index.html.twig
// ... lines 1 - 4
{% block body %}
<div class="flex justify-end mt-6 mb-6">
<div class="relative w-full max-w-md">
<form method="get" action="{{ path('app_part_index') }}">
<input type="text"
placeholder="Search..."
name="query"
class="w-full p-3 pl-10 border border-gray-300 rounded-lg shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<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>
</form>
</div>
</div>
// ... lines 21 - 41
{% endblock %}

De esta forma, cuando enviemos el formulario, añadirá la consulta de búsqueda a la URL como parámetro de consulta.

Obtener la petición

A continuación, dirígete a PartController. ¿Cómo leemos el parámetro de consulta namede la URL? Bueno, es información de la petición, igual que las cabeceras de la petición o los datos POST. Symfony empaqueta todo eso en un objeto Request. ¿Cómo lo obtenemos? En un controlador, es superfácil. Añade un argumento Request al método de tu controlador.

Probablemente recuerdes que puedes autoconectar servicios de este modo. El objeto Requestno es técnicamente un servicio, pero Symfony es lo suficientemente guay como para permitir autocablearlo de todas formas. Coge el de Symfony\Component\HttpFoundation\Request. Puedes llamarlo como quieras, pero para mantener la cordura, vamos a llamarlo $request:

26 lines | src/Controller/PartController.php
// ... lines 1 - 6
use Symfony\Component\HttpFoundation\Request;
// ... lines 8 - 10
final class PartController extends AbstractController
{
#[Route('/parts', name: 'app_part_index')]
public function index(StarshipPartRepository $repository, Request $request,): Response
{
// ... lines 16 - 23
}
}

Establece $query = $request->query->get('query'): el primer query se refiere a los parámetros de consulta, y el segundo query es el nombre del campo de entrada. Para asegurarte de que funciona, dd($query):

26 lines | src/Controller/PartController.php
// ... lines 1 - 10
final class PartController extends AbstractController
{
#[Route('/parts', name: 'app_part_index')]
public function index(StarshipPartRepository $repository, Request $request,): Response
{
$query = $request->query->get('query');
dd($query);
// ... lines 18 - 23
}
}

Gira y pruébalo. ¡Fíjate! Es la cadena "holodeck".

Mejorar la búsqueda

A continuación, vamos a mejorar el método findAllOrderedByPrice() para permitir una búsqueda. Elimina el dd($query); y pásalo al método:

25 lines | src/Controller/PartController.php
// ... lines 1 - 10
final class PartController extends AbstractController
{
#[Route('/parts', name: 'app_part_index')]
public function index(StarshipPartRepository $repository, Request $request,): Response
{
$query = $request->query->get('query');
$parts = $repository->findAllOrderedByPrice($query);
// ... lines 19 - 22
}
}

Divídelo en varias líneas y añade una sentencia if. También voy a cambiar el retorno por $qb = $this->createQueryBuilder('sp') y a deshacerme de getQuery()y getResult(): por ahora sólo queremos QueryBuilder.

Ahora viene la magia. Si tenemos una búsqueda, añade un andWhere() que compruebe si el nombre en minúsculas de nuestra parte Starship es como nuestra búsqueda. Ya sé que parece un poco raro, pero es porque PostgreSQL distingue entre mayúsculas y minúsculas.

Por último, devuelve el resultado de la consulta:

58 lines | src/Repository/StarshipPartRepository.php
// ... lines 1 - 13
class StarshipPartRepository extends ServiceEntityRepository
{
// ... lines 16 - 40
public function findAllOrderedByPrice(string $search = ''): array
{
$qb = $this->createQueryBuilder('sp')
->orderBy('sp.price', 'DESC')
->innerJoin('sp.starship', 's')
->addSelect('s')
;
if ($search) {
$qb->andWhere('LOWER(sp.name) LIKE :search')
->setParameter('search', '%'.strtolower($search).'%');
}
return $qb->getQuery()
->getResult();
}
}

Conservar el valor de búsqueda

Puede que notes que perdemos nuestro valor de búsqueda después de una búsqueda. Ya no vemos "holodeck" ahí, y eso es una grosería. Para solucionarlo, vuelve a la plantilla y añade un objeto value="{{ app.request.query.get('query') }}". Sí, ese práctico objeto Requestestá disponible en cualquier plantilla como app.request:

44 lines | templates/part/index.html.twig
// ... lines 1 - 4
{% block body %}
<div class="flex justify-end mt-6 mb-6">
<div class="relative w-full max-w-md">
<form method="get" action="{{ path('app_part_index') }}">
<input type="text"
// ... lines 11 - 12
value="{{ app.request.query.get('query') }}"
// ... line 14
>
// ... lines 16 - 18
</form>
</div>
</div>
// ... lines 22 - 42
{% endblock %}

Buscar en varios campos

¿No sería genial poder buscar también en las notas de las piezas? Busca "controles". Ahora mismo, nada. Realmente queremos buscar en el nombre y en las notas.

Necesitamos algo de lógica OR. De vuelta al repositorio, añade un OR a la cláusula andWhere():

58 lines | src/Repository/StarshipPartRepository.php
// ... lines 1 - 13
class StarshipPartRepository extends ServiceEntityRepository
{
// ... lines 16 - 40
public function findAllOrderedByPrice(string $search = ''): array
{
// ... lines 43 - 48
if ($search) {
$qb->andWhere('LOWER(sp.name) LIKE :search OR LOWER(sp.notes) LIKE :search')
->setParameter('search', '%'.$search.'%');
}
// ... lines 53 - 55
}
}

Podrías tener la tentación de utilizar orWhere(), ¡pero eso es una trampa! No puedes garantizar dónde estarán los paréntesis lógicos. Confía en mí, me lo agradecerás más tarde. En lugar de eso, utiliza andWhere() y coloca el OR justo dentro.

¡Y ya lo tenemos! Ahora podemos buscar en las notas, en el nombre, o en ambos. La conclusión es que cuando quieras utilizar orWhere(), no lo hagas: incrusta el ORdentro de un andWhere(), y tendrás pleno control sobre dónde van los paréntesis lógicos.

Bien, una vez completado este emocionante desvío, volvamos al tema y hablemos del último tipo de relación: de muchos a muchos.