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!
38.
Automatic 404 on Unpublished Items
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
}
}
12 Comments
Hi there!
I am using a custom doctrinefilter in my project on lets say entity "child" (using QueryCollectionExtensionInterface).
Entity "child" is a subresource of entity "parent".
When i ask for "api/child" the filter does its job but this is not the case when I ask for "api/parent", then the subresource "child" has the filter not applied anymore.
Is there a way to fix this?
Thank you!
Wannes
Hey Wannes V.!
I understand :). First, let me explain why this happens.
A) When you make a request to
GET /api/child, API Platform makes a query for the "child" entities. And when it does that, it includes ysour QueryCollectionExtensionInterface.B) When you make a request to
GET /api/parent, API Platform makes a query for the "parent" entities. Then, when it outputs the "children" property, internally, it simply calls$parent->getChildren(). What I mean is, there is no explicit query for the "children" in this situation. The "children" (or maybe it's a single "child" property - it doesn't matter, but I might be using different names than your app!) works just like any other property: to get the "children" property, API Platform simply calls$parent->getChildren(). This, of course, returns the full, unfiltered collection of objects. This is expected :).So, what's the fix? The fix is to create a new "getters" in your Parent entity - e.g.
getFilteredChildren()and expose this to your serialization (via @Groups) instead of the "children" property. You can even use @SerializedName to make it still be called "children". Inside this method, you would perform the filter and return the filtered collection of "children". For a small collection, you could do this in PHP (e.g. filter overall children, apply your filtering logic, and return only the matching children) or if your collection is a bit bigger or you want to maximize performance, then use a Criteria - here's an example: https://symfonycasts.com/screencast/collections/criteria-collection-filteringLet me know if that helps!
Cheers!
Thanks a lot Ryan!
The Criteria were indeed what I was looking for. Tbh, I recently saw it in the doctrine course but I totally must have forgotten!
Anyways, thanks for the nice description and your super-fast reply, I appreciate !
Best!
Wannes
How would the owner fetch his unpublished CheeseListings to update/publish them?
Hey hacktic!
Excellent question :). The most straightforward way would be to modify the query inside this "extension class" to allow for items that are owned by the user - something like this:
You would need to make this a bit smarter, however: if the user is NOT logged in, then you don't want to include the
OR %s.owner...part at all.Let me know if this helps! Cheers!
Hello there ,
With this Query Extention I realize that I have to put many default filters, isRemoved (instead of deleting I put a flag), isPublic (you could create a private item) and even more if you want to filter depends on price, creation date, owner, location... I wish there will be some performance topic in the next API Platform course, because with a big database mysql could be low performing :)
Yo Rakodev !
> I wish there will be some performance topic in the next API Platform course, because with a big database mysql could be low performing
That's true! However, I also think it comes down to three simple strategies to handle this:
A) It's not a problem so you do nothing :). Unless you know your database *will* be huge (which certainly *is* the case sometimes), I wouldn't optimize too early.
B) If you do want to optimize (or you already know there is a problem), most things can be fixed by adding some indexes. Really, the performance issues isn't about API Platform, it's about optimizing queries that may have multiple WHERE statements on them. If you focus on optimizing the queries, the problem is "smaller" and you can put your energy at the right spot :).
C) Some filtering (especially the "Search" filter with the fuzzy-searching) can't be optimized beyond a certain point. If you hit this (or you just want a more "intelligent" search), then you should switch to use Elasticsearch. We haven't talked about it yet, but API Platform has integration for Elasticsearch. Basically, instead of querying the database, you index your items inside Elastic and then "query" Elastic to get results.
Let me know if this helps... or doesn't ;).
Cheers!
I really hope, you have ElasticSearch part in your plans :).
For now, in my project, I created custom collection DataProvider, which query ElasticSearch and return results as doctrine entities collection. It works like a charm, but I'm curious what will be your approach :)
Hello,
The
QueryItemExtensionInterfacedoesn't work if you have a DataProvider for the entity you request for.Hey Rakodev!
Hmm, that's true - and I should have mentioned that... and so we might need a note. But question, by "entity", do you mean specifically "Doctrine entity" or are you just using that to mean "model class"? If you're using it to mean "model class", then I understand your point completely and you're right. If you're using it to mean "Doctrine entity", then what is the use-case for having a Doctrine entity with a custom data provider? I can't think of one right now, but I am very likely missing something :).
Cheers!
Hello @weaverryan,
I mean model class. For test purpose, I put an ItemDataProvider with a getItem method who basically do nothing but:
$this->em->getRepository(CheeseListing::class)->find($id);But as I did it few weeks ago, I completely forgot and yesterday I spent some time trying to understand why my QueryItemExtension didn't work.
And even if I have xDebug I didn't know how to solve my issue, I just checked my listeners, providers, subscribers, serializers, voters... With so much event I'm not sure what's the best way to debug your code. I just imagine if it's a big project it could be tricky do understand in which order they are executed, or understand all the way your request go through or why it's not going through a code you think it should be...
Hey Rakodev!
Yep, that makes sense. We're going to add a note about this: it's important to know that if you have *anything* that is loading data in a way *other* than the standard Doctrine loader (custom data provider, or also Elastic search) then you are taking complete control of the data loading and the extension won't work. Thanks for the conversation ;).
Cheers!
"Houston: no signs of life"
Start the conversation!