Filtros: Buscar resultados
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 SubscribeAlgunos de nuestros tesoros de dragón están publicados y otros no. Eso es gracias a DragonTreasureFactory, donde publicamos aleatoriamente algunos pero no otros.
Ahora mismo, la API devuelve hasta el último tesoro dragón. En el futuro, haremos que nuestra API devuelva automáticamente sólo los tesoros publicados. Pero para empezar, al menos hagamos posible que nuestros clientes de la API puedan filtrar los resultados no publicados si lo desean.
Hola ApiFiltro
¿Cómo? Aprovechando los filtros. API Platform viene con un montón de filtros incorporados que te permiten filtrar las colecciones de resultados por texto, booleanos, fechas y mucho más.
Funciona así: sobre tu clase, añade un atributo llamado ApiFilter.
Normalmente hay dos ingredientes que debes pasarle. El primero es qué clase de filtro quieres utilizar. Y si miras la documentación, hay un montón de ellas, como una llamada BooleanFilter que utilizaremos ahora y otra llamadaSearchFilter que utilizaremos dentro de unos minutos.
Pasa este BooleanFilter -el de ORM, ya que estamos utilizando el ORM Doctrine- porque queremos permitir al usuario filtrar en un campo booleano.
Lo segundo que tienes que pasar es properties a una matriz de los campos o propiedades en los que quieres utilizar este filtro. Establécelo en isPublished:
| // ... lines 1 - 4 | |
| use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter; | |
| use ApiPlatform\Metadata\ApiFilter; | |
| // ... lines 7 - 38 | |
| (BooleanFilter::class, properties: ['isPublished']) | |
| class DragonTreasure | |
| { | |
| // ... lines 42 - 164 | |
| } |
Utilizar el filtro en la petición
¡Muy bien! Vuelve a la documentación y comprueba la ruta de recolección GET. Cuando probemos esto... ¡habrá un nuevo campo isPublished! Primero, pulsa "Ejecutar" sin configurarlo. Cuando nos desplacemos hasta abajo, ¡ahí lo tenemos!hydra:totalItems: 40. Ahora establece isPublished en true e inténtalo de nuevo.
¡Sí! Tenemos hydra:totalItems: 16. ¡Está vivo! Y comprueba cómo se produce el filtrado. Es muy sencillo, mediante un parámetro de consulta: isPublished=true. Y la cosa se pone más chula. Mira la respuesta: tenemos hydra:view, que muestra la paginación y ahora también tenemos un nuevo hydra:search. Sí, la API Platform documenta esta nueva forma de buscar directamente en la respuesta. Dice:
Oye, si quieres, puedes añadir un parámetro de consulta
?isPublished=truepara filtrar estos resultados.
Bastante guay.
Añadir filtros directamente sobre las propiedades
Ahora bien, cuando lees sobre filtros en los documentos de la API Platform, casi siempre los muestran encima de la clase, como hemos hecho nosotros. Pero también puedes poner el filtro encima de la propiedad a la que se refiere.
Observa: copia la línea ApiFilter, elimínala y baja a $isPublished. Pégala encima. Y ahora, ya no necesitamos la opción properties... API Platform lo resuelve por sí sola:
| // ... lines 1 - 38 | |
| class DragonTreasure | |
| { | |
| // ... lines 41 - 68 | |
| (BooleanFilter::class) | |
| private bool $isPublished = false; | |
| // ... lines 71 - 164 | |
| } |
¿El resultado? El mismo que antes. No lo probaré, pero si echas un vistazo a la ruta de la colección, sigue teniendo el campo de filtro isPublished.
Filtro de búsqueda: Filtrar por texto
¿Qué más podemos hacer? Otro filtro realmente útil es SearchFilter. Hagamos que sea posible buscar por texto en la propiedad name. Esto es casi lo mismo: encima de $name, añade ApiFilter. En este caso queremos SearchFilter: de nuevo, coge el del ORM. Este filtro también acepta una opción. Aquí puedes ver que, además de properties, ApiFilter tiene un argumento llamado strategy. Eso no se aplica a todos los filtros, pero sí a éste. Establece strategyen partial:
| // ... lines 1 - 5 | |
| use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; | |
| // ... lines 7 - 39 | |
| class DragonTreasure | |
| { | |
| // ... lines 42 - 48 | |
| (SearchFilter::class, strategy: 'partial') | |
| private ?string $name = null; | |
| // ... lines 51 - 166 | |
| } |
Esto nos permitirá buscar en la propiedad name una coincidencia parcial. Es una búsqueda "difusa". Otras estrategias son exact, start y más.
¡Vamos a intentarlo! Actualiza la página de documentos. Y... ahora la ruta de la colección tiene otro cuadro de filtro. Busca rare y pulsa Ejecutar. Veamos, aquí abajo... ¡sí! Al parecer, 15 de los resultados tienen rare en algún lugar de name.
Y de nuevo, esto funciona añadiendo un simple ?name=rare a la URL.
Oh, hagamos también que se pueda buscar en el campo description:
| // ... lines 1 - 5 | |
| use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; | |
| // ... lines 7 - 39 | |
| class DragonTreasure | |
| { | |
| // ... lines 42 - 48 | |
| (SearchFilter::class, strategy: 'partial') | |
| private ?string $name = null; | |
| // ... lines 51 - 53 | |
| (SearchFilter::class, strategy: 'partial') | |
| private ?string $description = null; | |
| // ... lines 56 - 167 | |
| } |
Y ahora... ¡también aparece en la API!
SearchFilter es fácil de configurar... pero es una búsqueda difusa bastante simple. Si quieres algo más complejo -como ElasticSearch- API Platform lo admite. Incluso puedes crear tus propios filtros personalizados, cosa que haremos en un futuro tutorial.
Muy bien: a continuación, veamos dos filtros más: uno sencillo y otro extraño... Un filtro que, en lugar de ocultar los resultados, permite al usuario de la API ocultar determinados campos en la respuesta.
23 Comments
If you have problem with /api Entrypoint:
use this:
not this
Hey Domin,
Thank you for this tip! Yeah, we definitely want the filter for ORM not ODM as we use Doctrine ORM in this project :) But yeah, easy to miss this tiny difference in the long namespace, so it might be helpful for others.
Cheers!
In the "Script" version of this tutorial, you forgot the call to ApiFilter class here:
Hi,
when you want to activate filters for all booleans by default in your resource you need to only put:
#[ApiFilter(BooleanFiler::class)]above class and of course, remember about proper 'use' statements.
Jakub
Ha! I didn't know that - very cool!
Filters are so easy to use!
But from what I get they always work by generating some query parameter.
How can I set headers in swaggerUI similar to query parameters so that the user can set those headers in the UI?
I'd like to have some filters as headers instead of query parameters.
Hey @Fireball
I'm not sure if ApiPlatform gives you that functionality out of the box, it sounds to me like you'll need to create a custom filter
Cheers!
It might be a litte late for the topic starter, but for everybody else trying to do the same – you might want to look into declaring parameters on resources: https://api-platform.com/docs/core/filters/#declaring-parameters
Cool, thank you for sharing it!
Hello @weaverryan
The problem with the ARRAY error is still here "Undefined constant Doctrine\DBAL\Types\Types::ARRAY" despite this https://github.com/symfony/maker-bundle/issues/1437
I didn't want to downgrade to dbal 3.8 has advise by others.
To make it works, i've just add "public const ARRAY = 'array';" in vendor/doctrine/dbal/src/Types/Types.php
So far so good
Hey @weaverryan
I'm updating from version 2.7 to 3.0.
The filters are completely ignored, nothing absolutely changes at all.
I tried even with a vanilla project without success.
I know it is a long shot, but where should I start to look ?
Hey @Thomas-K!
Ah! Hmm. So, just "normal" built-in (not custom) filters don't work? You don't see them in the documentation and trying to manually add the query parameters doesn't work? That's very bizarre, especially in a fresh project. It makes me think (?) that maybe you're doing some subtle formatting wrong (e.g. on the attributes) and API Platform isn't seeing them? It's definitely a very odd thing, which often means it's something really subtle that's causing the issues. Do you have any special config setup - or are you using PHP attributes for the
#[ApiResource]like normal?Cheers!
Across the SearchFilter: Filter by Text section, you mention multiple times the
titleproperty, that should be thenameproperty.Hey @Alex-K
You're right! Ryan got confused. However, there's not a
titleproperty on the entity, so we could say thenameproperty is actually the title :DCheers!
Hi,
I have the following filter
but it returns an array of elements.
Is it possible to make it return a single element like findOneBy method inside Entity Repository?
Hey @Dmitriy!
Hmm, probably not. The problem is that the filters are only used on your "collection" endpoint - e.g.
/api/treasures, and these always return an array of items, even if it's an array of 1 thing. And that's by design: no matter what, when you use the "collection" endpoint, you get a collection back - and you can predict it's structure. It might actually be possible to return a single item (by creating a custom state provider, calling the core provider, then changing the array of 1 item to just an object and returning it from your state provider), but that's a lot of extra work to make your endpoint operate in a non-standard way. So my advice is: keep it as an array and keep it simple :).Cheers!
Thank you. It's a pity that everything is so complicated. I'll keep it simple)
Good morning.
I am very interested in the integration of the Api platform with Elastic Search to implement it.
Would you have a link, manual or video that explains it.
Or are you going to take a course on that?
Thank you.
Hey @Patricio-RM!
We don't have any plans to cover that right now. But API Platform does have docs and official support for Elastic Search - https://api-platform.com/docs/core/elasticsearch/ - and especially in episode 3 - https://symfonycasts.com/screencast/api-platform-extending - we go deeper into understanding how state providers work, which would at least clarify how that is working. I've never used the Elastic Search integration myself, but it looks a lot like the Doctrine support: there is a built-in
CollectionProviderthat knows how to pull data from ElasticSearch and populate your custom class.I hope this short answer at least helps a little bit :).
Cheers!
I see that there are 2 classes BooleanFilter, one for mangoDB(ODM) and one for MySQL5(ORM), how would i make my application support both of them?
Hey @Wael-G
I'm not sure about the answer but your application will run on two different databases?
I am just assuming that I have 2 clients with different Database preferences.
In that case, I think you would have to implement some sort of a "select database" system and remember that option somehow (perhaps in cache). Then, any service communicating to the DB will have to check that config and use the corresponding DB. I'm not sure if there are bundles that can help you with it
Oh, also, you'll have to set up Doctrine to work with multiple databases https://symfony.com/doc/current/doctrine/multiple_entity_managers.html
I hope it helps. Cheers!
"Houston: no signs of life"
Start the conversation!