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!
36.
Auto-set the Owner: Entity Listener
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
}
}
22 Comments
Hi,
i have an Entity with a constraint on 2 fields
`#[UniqueEntity(
)]`
It works well before ading the Doctrine Listener. Now my user is auto added to my entites but my constraint is not triggered.
Hi,
Do you have a
NotNullconstraint onUserfield? I think it's a little complex problem because Validation is called before any Doctrine Listener that changes data before persisting, so the easiest way to get it work, you should add aValidatorInterfaceservice to your Listener and call validate manually after setting the user. You should take validator service fromApiPlatformpackage. In this case you will have a proper validation exception 'causeValidatorInterface::validatein this case will do all work automaticaly!Cheers
Hi thank you for your reply
I am a completly noob can uou show how and where i can add this interface ? I tried inside the listener but could'nt manage to make it work :(
Heh, but you are on chapter 36 of ApiPlatform course ;) you should know how to autowire services ;)
This is a small example, it's hard to say how exactly you should use it, because I don't see your code, but it should be easy to inject anywhere!
Cheers!
Hi,
Thank you for your reply.
It was as simple as that. I tried the same thing but i probably messed it up somewhere.
it's working like a charm. Thank you.
Hey there :) amazing cast. Thank yu!
Small question, why when we update an entity ! The prePersist is not fired, instead it's the preUpdated callback who is called ?
Hey ahmedbhs
I'm glad to hear you are liking our content. About your question, the
prePersistevent is only fired when a new entity is saved in the database for the first time. And, thepreUpdateevent is fired every time an entity is updated in the database, that's just how Doctrine events works.Cheers!
Hi,
I'm a bit confused on this part. Doesn't this cuase validation errors? For example I want to check if the
userIDis unique. So I use the@UniqueEntity("userID")annotation. This doesn't seem to trigger at all. The same goes for the@IsValidOwner()annotation. It just throws a 500 error because of the unique SQL key. Is there a solution for this. For example add theuserIDin a earlier state (before the validation)?Hope you guys can help!
Thanks!
Hey Mees E.!
Yes, actually. The listener is running AFTER validation, so we're setting the owner and it's saving, without any validation. For our example, that works fine: we don't need to run IsValidOwner() because that checks to see if the owner === the current user, for when we're doing an edit. But for a create, it's always valid to set the owner to the currently-authenticated user. So no validation needed. And for UniqueEntity, it's just not something we need here.
> For example I want to check if the userID is unique
Let's see if we can figure this out :). One option would be to use the listener, and then execute validation yourself. Do that by injecting the ValidatorInterface service and calling validate. That will return a ConstraintViolationList. If that has any validations errors in it, throw the special ValidationException. You're basically repeating what Api Platform does for its own validation layer: https://github.com/api-plat...
Let me know if that helps!
Cheers!
Thank you for responding! That should work!
Just for extra information: Are there any best practices on validating
OneToOnerelations without throwing a 500 error? This solution should work but does sound like there's a better option.Thanks!
Hey Mees E.!
Hmm. In general, I think UniqueEntity should work for this... I can't think of a reason why it wouldn't. BUT, that's assuming that you aren't using a listener to set the owner like this: that assumes that you are just allowing the user to send data, and you want to guarantee that there isn't already a record in the database with the same user. I could be wrong, as I rarely use OneToOne, but I think that in the "normal data setting situation", UniqueEntity should catch it.
Cheers!
I have to add : private ?User $owner = null; into CheeseListing class to succeed the test.
Hey Stephane,
If you want to use that new PHP property types feature - yes, you have to :) It's tricky because we need to allow putting the entity into invalid state to validate it. Thanks for sharing your solution with others
Cheers!
I was thinking, in cases like this (when persisting data), is there a difference between using an Entity Listener or a Data Persister?
To my understanding, a Data Persister is only executed when using API Platform and an Entity Listener is always executed be it with API Platform or not. Am I thinking right?
So, if that was not an issue I could use a Data Persister to auto-set the owner the same way I could use an Entity Listener to encode a password?
Thanks!
Hey André P.
Yes, you're right. The DataPersister is a layer inside ApiPlatform, so, if you save an entity out of ApiPlatform, as far as I know, its DataPersister won't be called. My recomendation here is to use your API to create your entities, instead of having 2 different ways for creating those, it avoids code duplication and may simplify your logic
Cheers!
Hi,
if i want to set the owner of multiple entities, should i duplicate my listener for each entity or there is a smart way to do this?
Thanks for this great course.
Hey @adam!
Excellent question :). No need to duplicate the listener. What you can do is this:
A) Create an interface with a setOwner(User $user) method. Make all your entities that need this "set owner functionality" have this. Maybe the interface is called "SettableOwnerInterface".
B) Create an entity listener just like in this video, but use the SettableOwnerInterface type-hint on the prePersist() argument. Because, once you're done, this method will receive one of multiple different objects.
C) Register your entity listener on all the entity classes that you need.
That should do it! Btw, if you used a Doctrine event subscriber that's called on prePersist() of *all* entities, then you could skip step (C) and instead just check of the entity being passed to your method is an instance of SettableOwnerInterface.
Let me know if you have any questions!
Cheers!
This more advanced symfony content is great. The non-screencast visuals / presentation stuff for covering theory (even text) is a really helpful comprehension aide too.
Hi Cameron,
Thank you for your feedback!
Cheers!
Hi There!
I have A question about the entitylistener, but first: please tell me this is not the right place for this question since its maybe already a bit too much off-topic :)
Anyways lets give it a try!:
I use the doctrine entitylistener as I learned a whilo ago from this video. Only difference, I use postPersist.
I am now experimenting a bit with phpmailer since i want to sent a mail to the user right after he registered for the first time. Everything kind of works since my mail is received and data is persisted but somehow this also seems to mess with the response I'm getting from my API... as soon as I add 'mail->send()' the data in my response is no longer the one from my entity but is all info from my mailserver etc.. Probably I'm breaking some rules here of which I don't know but so far I havent found anything online
All the best,
wannes!
`
//function to be executed after persisting to the database
class AfterRegistrationEmailListener
{
}
`
Oh well I by now found out it was a problem related to phpmailer, not to my entitylistener so not so much related to the course!
In case anyone ever comes by this question with a similar issue:
The debug server of php mailer injected data in my response, malforming my entire response. simply removing the debugserver made everything work as expected :)
Hey Wannes,
Glad you were able to solve this problem yourself, well done! And thank you for sharing your solution with others!
Cheers!
"Houston: no signs of life"
Start the conversation!