Chapters
39 Chapters
|
4:45:13
|
Login to bookmark this video
-
Course Code
Subscribe to download the code!Compatible PHP versions: ^7.1.3, <8.0
Subscribe to download the code!Compatible PHP versions: ^7.1.3, <8.0
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
37.
Query Extension: Auto-Filter a Collection
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Subscribe to jump to this part in the video!
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.
This tutorial works great for Symfony 5 and API Platform 2.5/2.6.
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": "^7.1.3, <8.0",
"ext-ctype": "*",
"ext-iconv": "*",
"api-platform/core": "^2.1", // v2.4.5
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/annotations": "^1.0", // 1.13.2
"doctrine/doctrine-bundle": "^1.6", // 1.11.2
"doctrine/doctrine-migrations-bundle": "^2.0", // v2.0.0
"doctrine/orm": "^2.4.5", // v2.7.2
"nelmio/cors-bundle": "^1.5", // 1.5.6
"nesbot/carbon": "^2.17", // 2.21.3
"phpdocumentor/reflection-docblock": "^3.0 || ^4.0", // 4.3.1
"symfony/asset": "4.3.*", // v4.3.2
"symfony/console": "4.3.*", // v4.3.2
"symfony/dotenv": "4.3.*", // v4.3.2
"symfony/expression-language": "4.3.*", // v4.3.2
"symfony/flex": "^1.1", // v1.21.6
"symfony/framework-bundle": "4.3.*", // v4.3.2
"symfony/http-client": "4.3.*", // v4.3.3
"symfony/monolog-bundle": "^3.4", // v3.4.0
"symfony/security-bundle": "4.3.*", // v4.3.2
"symfony/twig-bundle": "4.3.*", // v4.3.2
"symfony/validator": "4.3.*", // v4.3.2
"symfony/webpack-encore-bundle": "^1.6", // v1.6.2
"symfony/yaml": "4.3.*" // v4.3.2
},
"require-dev": {
"hautelook/alice-bundle": "^2.5", // 2.7.3
"symfony/browser-kit": "4.3.*", // v4.3.3
"symfony/css-selector": "4.3.*", // v4.3.3
"symfony/maker-bundle": "^1.11", // v1.12.0
"symfony/phpunit-bridge": "^4.3", // v4.3.3
"symfony/stopwatch": "4.3.*", // v4.3.2
"symfony/web-profiler-bundle": "4.3.*" // v4.3.2
}
}
9 Comments
Hi all,
I have added a Query Extension to add an autofilter to my unpublised content, as in your tutorial and it works GREAT.
Thanks for your work, first of all!
Let you have an endpoint that show an entity WITH related resources; let we want to apply the same business rule ( hide unpublished content ) also on related resource ( es: a company endpoint that show related products, loaded lazy as doctrine entities )
Actually, I have create a Normalizer related to the main resource and I loop over related resource and I skip di unpublished content.
Do you know other way to hide subresources?
I think there is no other way to perform this autofilter ALSO on related resource
Hey Gianluca!
Apologies for the slow reply! But thank you for the kind words ❤️.
I agree. As I'm sure you're already aware, if you make a
GET /api/categories/1/products, then, behind the scenes, API Platform simply calls$category->getProducts()to fetch that data. Your idea about a normalize actually isn't one I had thought of - nice job 👍. The only other solution I can think of is to create something like this:I actually don't know if this works :). But in theory, this might create a
/api/categories/1/published_productstype of subresource. IF that works, that would be another solution. In general, subresources are pretty limited in API Platform 2. I've heard that got a bit more robust in API Platform 3, but I haven't looked yet (that's my homework over the next few weeks!)Cheers!
Is it possible to completely rewrite the query in applyToItem rather than modify it? I have tried the following
[Semantical Error] line 0, col 116 near 'mealAudience': Error: Class App\Entity\MealAudienceCategory has no association named mealAudienceCategories
`If i run
dd($queryBuilder->getQuery());It shows the correct SQL ofSELECT mac FROM App\Entity\MealAudienceCategory macHey David B. !
Hmm, interesting question! So, the system doesn't like this - you're bending it in a way it doesn't like. But, probably you can get this to work.
The query extension stuff is called from your "data provider". For example, the ItemDataProvider from Doctrine in this case: https://github.com/api-plat...
I'd recommend replacing the data provider entirely for this situation. We talk about that around this section - https://symfonycasts.com/sc... - including leveraging the logic from the normal data provider. For example, in this ONE situation, you might completely "take over" and do the query and return the result. In every other case, you would call the normal data provider.
This should work, though you will (in this one case where you "take over") lose things like pagination and filtering, because the query logic from those is added via other "query extensions". However, since you're doing an "item provider", which doesn't have pagination or filtering... you probably won't be losing much or anything. But, I've never tried this, so we'll have to see!
Cheers!
If i have a more complicated logic which decides if a user can se a CheeseListing, which i put in a Voter, which i call by using isGranted("COMPLICATED_LOGIC", $cheeseListing), is there a way i can implement the Voter to decide on each individual item in the collection? Or do i have to replicate the stuff my voter does inside the querybuilder?
Hey Alex T.!
Excellent question! I remember (WAY before API Platform, just in programming in general) this question driving me crazy :p.
The answer is this: (unless I've absolutely missed some clever way of doing it f or the past 10 years) you need to replicate the voter logic in the query builder. The voter is used AFTER you already have an individual item to determine if access is granted while the query builder is used to get a list of items. Those jobs are *super* similar... but surprisingly tricky (impossible) to share. I don't believe this is an API Platform or Symfony problem either: it's just tricky to share :).
Cheers!
Thanks for the answer, i wonder, would it be possible, and "good practice" to create one Doctrine Criteria and share it between the Voter and the Extension? Or is there a other way to "centralize" the code for my access controlls? I worry that ill use the voter for most of the access controll stuff and then forget that the decision taking on whether the user sees certain items inside collections is completly detatched from that
Hey Alex T.!
I hear you... but I can't think of a really good way to share the code... I'm trying to think. For example, if a user only had access to posts where some isPublished field = true, then to check that in PHP, you would do
if ($listing->isPublished())but in a query you would be effectively addingWHERE listing.isPublished = 1. Those are frustratingly similar... but not identical.If you're worried, one idea is just to keep the logic "in the same place". Like, create a CheeseListingAccessManager service and put methods in there that relate to access - like
findAllForUser()that would return an array of CheeseListing and alsocanUserEditListing(CheeseListing $listing): bool. The code wouldn't be shared, but it would live in one spot at least.Sorry I can't offer any magic solution - if you DO think of something clever, let me know - I'd love to hear it ;).
Cheers!
Thank you for your help. If my app was a Cheese App, i'd have Users which can Join a "CheeseLoversGroup". Inside that group there are group specific roles, one user might be able to create cheeseListings in GROUP_A but only read them in GROUP_B. In the Future there might be the possibility to share data between some but not all groups. My App seemd so easy to me but i have the feeling i need everything API platform has to offer 😂 Also the fear to now, unexperienced with symfony, write some code i will suffer from later is unpleasant. But "complaining does not help" and thanks to your and your teams awesome tutorials im confident that i'll succeed 👍😁
"Houston: no signs of life"
Start the conversation!