Chapters
48 Chapters
|
5:13:28
|
Login to bookmark this video
-
Course Code
Subscribe to download the code!
Subscribe to download the code!
-
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!
44.
Input DTO Validation
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 also works great with API Platform 2.6.
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": ">=8.2",
"ext-ctype": "*",
"ext-iconv": "*",
"api-platform/core": "^2.1", // v2.5.10
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/annotations": "^1.0", // 1.12.1
"doctrine/doctrine-bundle": "^2.0", // 2.1.2
"doctrine/doctrine-migrations-bundle": "^3.0", // 3.0.2
"doctrine/orm": "^2.4.5", // 2.8.2
"nelmio/cors-bundle": "^2.1", // 2.1.0
"nesbot/carbon": "^2.17", // 2.39.1
"phpdocumentor/reflection-docblock": "^3.0 || ^4.0 || ^5.0", // 5.2.2
"ramsey/uuid-doctrine": "^1.6", // 1.6.0
"symfony/asset": "5.1.*", // v5.1.5
"symfony/console": "5.1.*", // v5.1.5
"symfony/debug-bundle": "5.1.*", // v5.1.5
"symfony/dotenv": "5.1.*", // v5.1.5
"symfony/expression-language": "5.1.*", // v5.1.5
"symfony/flex": "^1.1", // v1.21.6
"symfony/framework-bundle": "5.1.*", // v5.1.5
"symfony/http-client": "5.1.*", // v5.1.5
"symfony/monolog-bundle": "^3.4", // v3.5.0
"symfony/security-bundle": "5.1.*", // v5.1.5
"symfony/twig-bundle": "5.1.*", // v5.1.5
"symfony/validator": "5.1.*", // v5.1.5
"symfony/webpack-encore-bundle": "^1.6", // v1.8.0
"symfony/yaml": "5.1.*" // v5.1.5
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3", // 3.3.2
"symfony/browser-kit": "5.1.*", // v5.1.5
"symfony/css-selector": "5.1.*", // v5.1.5
"symfony/maker-bundle": "^1.11", // v1.23.0
"symfony/phpunit-bridge": "5.1.*", // v5.1.5
"symfony/stopwatch": "5.1.*", // v5.1.5
"symfony/twig-bundle": "5.1.*", // v5.1.5
"symfony/web-profiler-bundle": "5.1.*", // v5.1.5
"zenstruck/foundry": "^1.1" // v1.8.0
}
}
5 Comments
Ryan, I think you forgot one constraint under
CheeseListing: the one calledValidIsPublished, which is applied to the class itself. Ironically, this constraint might be especially troublesome to move not only because we made it to specifically work withCheeseListingobjects only, but also because it involves digging into Doctrine internals, which would likely complicate moving the constraint to a non-entity even further. I wonder how you would have solved that. :)Hey Adeoweb!
Ah, nice catch! I can't remember now if I did this on purpose... or accident, but I do sometimes miss details like this :).
But let's think. For reference, here is the finished validation logic: https://symfonycasts.com/screencast/api-platform-extending/validator-logic#codeblock-f8b1e9b4ae
I think the key difference is that, if we added this constraint above our input class, the actual
CheeseListingentity will, I believe, NOT have been modified yet: only the input class will be modified at this point. And so, believe the logic actually becomes a bit simpler. To get the "original value", instead of using using Doctrine's UnitOfWork, we could just query for the CheeseListing entity itself inside the validator and read its value. Well, to do that, we would need the id, which we don't have. So intsead, I would inject the RequestStack and grab the current request. The CheeseListing object should then be accessible via$request->attributes->get('data'). I believe that would be null if this is a CREATE operation. And so, in that case, you would know that the "original published value" is "false".Does that help? I could be missing something - I'm just "coding by looking" here, but I think that would do it :).
Cheers!
"Does it help?" – I don't know, but I guess it would, if it was the problem I was facing. Although injecting stuff like UOW and RequestStack into validators seems somewhat dirty. I'm lucky I don't need to validate cases like this, or I'd be stuck trying to find better ways for a while. :D
Hi folks,
there is a small thing which confuses me and I would appriciate if someone could clear thigns for me. In
CheeseListingInputDataTransformer:25we call validator and validate user input but I feel thing might not be the optimal place for it. Shouldn't we do the validation after we merge user-provided input with whatever we potentialy have in DB or I'm missing something. Something like this:<b>tl;dr;</b>
Hey @s!
I think I can help with this :). The flow is a bit complex. Here is what's going on (by the time you've finished this chapter):
1) API Platform queries for the actual CheeseListing entity object from the database
2) Thanks to our custom denormalizer - https://symfonycasts.com/screencast/api-platform-extending/input-initializer and the next chapter - we create a CheeseListingInput DTO that is populated with data from the database
3) API Platform then deserializes the JSON from the user onto our CheeseListingInput. This is invisible to us - it happens between our custom normalizer (described above) and our data transformer (next step). This is when, effectively, the user's JSON is merged with the database daata
4) THEN, our data transformer is called. By the first line of transform(), the
$inputobject already represents the merged database and user input data.So when you say:
You re 100% correct! That "merge" happens on step 3, which means at step 4 (in our transform() method) it's the perfect place to validate.
As I mentioned, the flow is complex because API Platform does a few things in the background, and we do a few things throughout that process. Hopefully seeing it all listed helps.
Let me know!
Cheers!
"Houston: no signs of life"
Start the conversation!