Buy

Doctrine Listener: Encode the User's Password

In AppBundle, create a new directory called Doctrine and a new class called HashPasswordListener:

15 lines src/AppBundle/Doctrine/HashPasswordListener.php
... lines 1 - 2
namespace AppBundle\Doctrine;
... lines 4 - 7
class HashPasswordListener implements EventSubscriber
{
... lines 10 - 13
}

If this is your first Doctrine listener, welcome! They're pretty friendly. Here's the idea: we'll create a function that Doctrine will call whenever any entity is inserted or updated. That'll let us to do some work before that happens.

Implement an EventSubscriber interface and then use Command+N or the "Code"->"Generate" menu, select "Implement Methods" and choose the one method: getSubscribedEvents():

15 lines src/AppBundle/Doctrine/HashPasswordListener.php
... lines 1 - 4
use Doctrine\Common\EventSubscriber;
... lines 6 - 7
class HashPasswordListener implements EventSubscriber
{
public function getSubscribedEvents()
{
... line 12
}
}

In here, return an array with prePersist and preUpdate:

15 lines src/AppBundle/Doctrine/HashPasswordListener.php
... lines 1 - 7
class HashPasswordListener implements EventSubscriber
{
public function getSubscribedEvents()
{
return ['prePersist', 'preUpdate'];
}
}

These are two event names that Doctrine makes available. prePersist is called right before an entity is originally inserted. preUpdate is called right before an entity is updated.

Next, add public function prePersist():

38 lines src/AppBundle/Doctrine/HashPasswordListener.php
... lines 1 - 6
use Doctrine\ORM\Event\LifecycleEventArgs;
... lines 8 - 9
class HashPasswordListener implements EventSubscriber
{
... lines 12 - 18
public function prePersist(LifecycleEventArgs $args)
{
... lines 21 - 30
}
... lines 32 - 36
}

When Doctrine calls this, it will pass you an object called LifecycleEventArgs, from the ORM namespace.

This method will be called before any entity is inserted. How do we know what entity is being saved? With $entity = $args->getEntity(). Now, if this is not an instanceof User, just return and do nothing:

38 lines src/AppBundle/Doctrine/HashPasswordListener.php
... lines 1 - 4
use AppBundle\Entity\User;
... lines 6 - 9
class HashPasswordListener implements EventSubscriber
{
... lines 12 - 18
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if (!$entity instanceof User) {
return;
}
... lines 25 - 30
}
... lines 32 - 36
}

Encoding the Password

Now, on to encoding that password.

Symfony comes with a built-in service that's really good at encoding passwords. It's called security.password_encoder and if you looked it up on debug:container, its class is UserPasswordEncoder. We'll need that, so add a __construct() function and type-hint a single argument with UserPasswordEncoder $passwordEncoder. I'll hit Option+Enter and select "Initialize Fields" to save me some time:

38 lines src/AppBundle/Doctrine/HashPasswordListener.php
... lines 1 - 7
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder;
class HashPasswordListener implements EventSubscriber
{
private $passwordEncoder;
public function __construct(UserPasswordEncoder $passwordEncoder)
{
$this->passwordEncoder = $passwordEncoder;
}
... lines 18 - 36
}

In a minute, we'll register this as a service.

Down below, add $encoded = $this->passwordEncoder->encodePassword() and pass it the User - which is $entity - and the plain-text password: $entity->getPlainPassword(). Finish it with $entity->setPassword($encoded):

38 lines src/AppBundle/Doctrine/HashPasswordListener.php
... lines 1 - 9
class HashPasswordListener implements EventSubscriber
{
... lines 12 - 18
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if (!$entity instanceof User) {
return;
}
$encoded = $this->passwordEncoder->encodePassword(
$entity,
$entity->getPlainPassword()
);
$entity->setPassword($encoded);
}
... lines 32 - 36
}

That's it: we are encoded!

Encoding on Update

So now also handle update, in case a User's password is changed! The two lines that actually do the encoding can be re-used, so let's refactor those into a private method. To shortcut that, highlight them, press Command+T - or go to the "Refactor"->"Refactor this" menu - and select "Method". Call it encodePassword() with one argument that's a User object:

65 lines src/AppBundle/Doctrine/HashPasswordListener.php
... lines 1 - 9
class HashPasswordListener implements EventSubscriber
{
... lines 12 - 18
public function prePersist(LifecycleEventArgs $args)
{
... lines 21 - 25
$this->encodePassword($entity);
}
... lines 28 - 48
/**
* @param User $entity
*/
private function encodePassword(User $entity)
{
if (!$entity->getPlainPassword()) {
return;
}
$encoded = $this->passwordEncoder->encodePassword(
$entity,
$entity->getPlainPassword()
);
$entity->setPassword($encoded);
}
}

Tip

I didn't mention it, but you also need to prevent the user's password from being encoded if plainPassword is blank. This would mean that the User is being updated, but their password isn't being changed.

Super nice!

Now that we have that, copy prePersist, paste it, and call it preUpdate. You might think that these methods would be identical... but not quite. Due to a quirk in Doctrine, you have to tell it that you just updated the password field, or it won't save.

The way you do this is a little nuts, and not that important: so I'll paste it in:

65 lines src/AppBundle/Doctrine/HashPasswordListener.php
... lines 1 - 6
use Doctrine\ORM\Event\LifecycleEventArgs;
... lines 8 - 9
class HashPasswordListener implements EventSubscriber
{
... lines 12 - 28
public function preUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if (!$entity instanceof User) {
return;
}
$this->encodePassword($entity);
// necessary to force the update to see the change
$em = $args->getEntityManager();
$meta = $em->getClassMetadata(get_class($entity));
$em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $entity);
}
... lines 43 - 63
}

Registering the Subscriber as a Service

Ok, the event subscriber is perfect! To hook it up - you guessed it - we'll register it as a service. Open app/config/services.yml and add a new service called app.doctrine.hash_password_listener. Set the class. And you guys know by now that I love to autowire things. It doesn't always work, but it's great when it does:

27 lines app/config/services.yml
... lines 1 - 5
services:
... lines 7 - 21
app.doctrine.hash_password_listener:
class: AppBundle\Doctrine\HashPasswordListener
autowire: true
... lines 25 - 27

Finally, to tell Doctrine about our event subscriber, we'll add a tag. This is something we talked about in our services course: it's a way to tell the system that your service should be used for some special purpose. Set the tag to doctrine.event_subscriber:

27 lines app/config/services.yml
... lines 1 - 5
services:
... lines 7 - 21
app.doctrine.hash_password_listener:
class: AppBundle\Doctrine\HashPasswordListener
autowire: true
tags:
- { name: doctrine.event_subscriber }

The system is complete. Before creating or updating any entities, Doctrine will call our listener.

Let's update our fixtures to try it.

Leave a comment!

  • 2017-11-02 Alexandru Lazar

    I sort it out. Before finding this tutorial i tried to pass * @ORM\PreUpdate in the user entity on the password field. Working great now! Thanks for the support you got me i`ll buy this tutorial!

    Cheers!

  • 2017-11-02 weaverryan

    Ah, cool! That answers the first question perfectly then :).

    Try this:

    1) dump($this->passwordEncoder);die;. It should be an instance of UserPasswordEncoder

    2) dump($entity->getPlainPassword());die;. This should be a string... but your error seems to make me think this is an object!

    Cheers!

  • 2017-11-02 Alexandru Lazar

    Heyweaverryan !
    Thanks for the fast answer. I am using 4.0 Beta and will go with the encoder interface. A for the second one I am clueless at the moment. I even tried to force it to string and it says it can`t be transformed. It`s clearly not autowiring since i got passed that using UserPassswordEncoderInterface. I`ll keep trying and let you know.
    Thanks for the fast answer!
    Cheers!

  • 2017-11-02 weaverryan

    Hey Alexandru Lazar!

    Ok, really interesting things are happening here! We've changed & improved autowiring in Symfony 3.3 and 3.4, so that's part of what you're seeing. First, what version of Symfony are you on? I would not expect the first error, though I would expect a deprecation notice. Here are some details:

    1) In Symfony 3, autowiring is quite magic: if you type-hint UserPasswordEncoder, it tries to find any service with that class and pass it in. But, that magical way is now deprecated. And in Symfony 4, it's much simpler: your type-hint must exactly match a service id in the container. Here are all the details about that: https://knpuniversity.com/s.... This is why - unless you're trying Symfony 4.0 BETA - I am surprised by your first error. On Symfony 4, you *must* use UserPasswordEncoderInterface (that's the service id in the container). But in Symfony 3, both should work (but using UserPasswordEncoder will trigger a deprecation notice).

    2) About the strlen() error, that's interesting. Basically, if you look deep enough, the second argument to encodePassword() - $entity->getPlainPassword() - is ultimately passed to a class that checks its length: https://github.com/symfony/.... Is $user->getPlainPassword() somehow an object? I don't think this has anything to do with autowiring, I think there's another issue.

    Let me know what you find out! Cheers!

  • 2017-11-02 Alexandru Lazar

    Hello,
    I did this for practice and got :
    Cannot autowire service "app.doctrine.hash_password_listener": argument "$passwordEncoder" of method "App\Doctrine\HashPasswordListener::__construct()" references class "Symfony\Component\Security\Core\Encoder\UserPasswordEncoder" but no such service exists. Try changing the type-hint to "Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface" instead.

    I changed to UserPasswordEncoderInterface and when updating the user details i get:
    Warning: strlen() expects parameter 1 to be string, object given

    pointing to encodeUserPassword ---

    $encoded = $this->passwordEncoder->encodePassword($entity,$entity->getPlainPassword());

  • 2017-07-17 weaverryan

    Yo maxii123!

    Weeeeeeeeell, mine only "kinda" auto completed :). It auto-completed doctrine.event_listener, but as soon as I typed doctrine.event_subscriber... the auto-complete did *not* work! If you got no auto-completion, I'm not sure - just make sure you have the latest version of the plugin! That magic definitely comes from the plugin :).

    Cheers!

  • 2017-07-14 maxii123

    Your service tag auto completed towards the end there. Mine didn't! Why? Yes. I have symfony plugin... ;)

  • 2017-05-08 Terry Caliendo

    Got it. Thanks.

  • 2017-05-06 Victor Bocharsky

    Hey Terry,

    Actually, Doctrine Lifecycle Callbacks are fine and you can use it as well, but they have some limitation like you can't inject other services into the entity. That's why listeners help with it - we can do the same with event listeners and we also can inject other service into it since listener is just a service.

    Cheers!

  • 2017-05-05 Terry Caliendo

    Just curious... why does Doctrine give ORM annotation to allow manipulation within the Entity file? Is it still bad practice to do it as part of the Entity file as opposed to a separate listener initiated in the services.yml file?

    http://symfony.com/doc/curr...

  • 2017-05-01 sokphea chea (ជា សុខភា)

    I forget use use statement for user too, thank you Xav you save my time.

  • 2017-04-28 Diego Aguiar

    Hey Xav!

    First: let's be 100% sure that your $entity is not an User object by dumping it and checking its type

    Second: Check at the top of your file (in the use statement section) what type of User was declared, maybe if you used autocompletion, you might chose a wrong one, it should be your "User Class"

    If neither of these cases, please let me see your listener code

    Cheers!

  • 2017-04-27 Xav

    Hi,
    When I register a new user (chapter 18), I have an error "Integrity constraint violation: 1048 [password field cant be null] (translated from french ^^'". So after a few tests, I've determined that the problem must come from the listener.
    Inside the prePersist function, if I put a "dump($entity );die;" right after "$entity = $args->getEntity();", I see that the prepersist event is detected. and obtain this :

    User {#5368 ▼
    -id: null
    -name: "Test"
    -mail: "test"
    -password: null
    -plainPassword: "test"
    }
    But ! If I put the dump/die after the "if (!$entity instanceof User)", I have the Integrity Constraint Violation". That shoud mean that my $entity isn't an instance of User, therefore the encodedPassword function isn't executed, right ? Yet, the previous test has shown that it really is an instance of User. So I don't understand.
    Could someone please explain me what I've missed ?
    Thank you.

    Edit : if I comment the "if !$entity part" in the prePersist function, no more errors and the user is registered.

    Edit 2 : found my mistake. A namespace error, again. Mfff.

  • 2017-04-20 Dominik

    PHPStorm just offered me the listener tag in auto completion only. Not a subscriber. Little bit confusing. Perhaps that was the problem :)

  • 2017-04-05 Terry Caliendo

    Got it. Thanks.

  • 2017-04-05 Victor Bocharsky

    Hey Terry,

    Good question! Because in this case you have to inject the password encoder service into the entity, which is a really bad practice, since entity is just a *data* not a service.

    Cheers!

  • 2017-04-04 Terry Caliendo

    While I enjoyed learning about the Doctrine listener, it seems like a lot of extra work to put in the Doctrine listener to encode the password. Why not just encode the password whenever setPassword() is called? Something like the following?

    User Entity:

        public function setPassword($password)
    {

    $encoded = $this->passwordEncoder->encodePassword(
    $this,
    $password
    );
    $this->password = $encoded ;

    }

  • 2017-03-24 Victor Bocharsky

    Hm, then make sure your User class has the "public function getPlainPassword()" method - please, double check it, probably you did some kind of misprint in its name. If you're really sure you have exactly the User:: getPlainPassword() public method - then try to clear the cache (both prod/dev) and run again.

    Cheers!

  • 2017-03-24 phphozpter

    But I still get the same error with this

    public function prePersist(LifecycleEventArgs $args)
    {
    $entity = $args->getEntity();

    if (!$entity instanceof User) {
    return;
    }

    if (!$entity->getPlainPassword()) {
    return;
    }

    $this->encodePassword($entity);
    }

    public function preUpdate(LifecycleEventArgs $args)
    {
    $entity = $args->getEntity();

    if (!$entity instanceof User) {
    return;
    }

    if (!$entity->getPlainPassword()) {
    return;
    }

    $this->encodePassword($entity);
    $em = $args->getEntityManager();
    $meta = $em->getClassMetadata(get_class($entity));
    $em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $entity);
    }

  • 2017-03-24 Victor Bocharsky

    Hey phphozpter ,

    It makes sense, since this event are called for *each* entity. So you need to do an extra check to ensure you're working exactly with User entity, and - the good news - you already have this extra check when do "if (!$entity instanceof User)", so just move your getPlainPassword() method call below, after the check. The correct example is:


    if (!$entity instanceof User) {
    return;
    }

    if (!$entity->getPlainPassword()) {
    return;
    }

    Then you will be 100% sure you always call the method on User entity ;)

    Cheers!

  • 2017-03-24 phphozpter

    If I do like this.


    if (!$entity->getPlainPassword()) {
    return;
    }

    if (!$entity instanceof User) {
    return;
    }

    I always get `Attempted to call an undefined method named "getPlainPassword"` when I edit any entity?

  • 2017-02-13 Victor Bocharsky

    Ah, then it makes sense. You're welcome!

    Cheers!

  • 2017-02-13 Sander de Wijs

    That was the issue, I added the event_listener as a tag instead of the event_subscriber. And looking back, that was exactly what the error message was trying to tell me :) Thanks for your help!

  • 2017-02-13 Victor Bocharsky

    Hey Sander,

    Do you implement EventSubscriber interface and tag your service with "doctrine.event_subscriber"? Because event listeners and event subscribers have a bit different configuration

    Cheers!

  • 2017-02-12 Sander de Wijs

    I got this error after adding the hash_password_listener to services.yml:
    Doctrine event listener "app.doctrine.hash_password_listener" must specify the "event" attribute.

    Adding ', event: prePersist' to the tag fixes this, but perhaps I missed something when creating the Doctrine event listener?

  • 2017-01-06 Hakim Ch

    Perfect, that what i was looking for! thank you guys for your explanations <3

  • 2017-01-06 weaverryan

    Hey Hakim Ch!

    Are you possibly referring to this weird code that appears in the event listener?


    $em = $args->getEntityManager();
    $meta = $em->getClassMetadata(get_class($entity));
    $em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $entity);

    If so, YES! This is something that's super-specific to an event listener. In fact, in a perfect world, we should NOT need this code! In theory, our event listener should be able to update a field (e.g. password), and as Doctrine finishes saving, it would send that update to the database. But for internal, low-level reasons, this does not happen on the preUpdate event in Doctrine. What I mean is, if you have an event listener on preUpdate, and you change a value on your entity, Doctrine doesn't see this change and doesn't make the update. These 3 weird lines say: "Hey Doctrine! I made some new changes to the entity - I need you to see them". So, these lines are 100% specific to the preUpdate listener... and I wish they weren't needed, but they are!

    I hope that helps! Or maybe you were asking about something else entirely :).

    Cheers!

  • 2017-01-06 Hakim Ch

    thing that confused me, is i never meet a call like that to update a field... :(
    even i watched "go pro with doctrine"

  • 2017-01-06 Victor Bocharsky

    Hey Hakim,

    What exactly are confusing you? The event listener encodes plain user password on certain events, i.e. when you save (persist or update) User entity to the DB. But how the event listener do it - you should write yourself in that event listener. Does it makes sense for you?

    Cheers!

  • 2017-01-05 Hakim Ch

    Im just confused about how you called a different way to update the password :(
    it is special to Event listener ?

  • 2016-11-30 Christophe Lablancherie

    Thanks for your answer ! I will do some research for EncoderAwareInterface and if it's easy to implement i will do this to do on bcrypt :)

  • 2016-11-29 weaverryan

    Hey Christophe Lablancherie!

    You can definitely do this (sort of) and you have 2 options:

    1) Continue using sha with salt - this is just a setting in security.yml, and you can keep the same hashing algorithm even if you're changing from FOSUserBundle to something else. Of course, bcrypt is more secure than just using sha.

    2) Allow your 100 old accounts to use sha, but use bcrypt for the new accounts. You would do this by adding some flag on the old accounts so that you know they use the old method. Then, implement the EncoderAwareInterface in your User class to tell Symfony which to use in which case.

    Cheers!

  • 2016-11-29 Christophe Lablancherie

    Hi,

    I was using FOSUserBundle on my project, but i would to change and take a solution coded by myself. I found your solution very usefull but i've one question. I have 100 account registered and also 100 password, registered with sha and salt...But in the screencast we used bcrypt, so what could i migrate the account member ?

    Thanks for your answer

  • 2016-10-10 Dennis Biedermann

    Thanks for this helpful answer!

    Cheers!

  • 2016-10-07 Victor Bocharsky

    Hey Dennis,

    Ha, that's a good question! Actually, it depends... and btw, we use it in other tutorial: https://knpuniversity.com/s... . So, here's a point: use `HasLifecycleCallbacks()` if in your even handlers occur only entity-related stuffs, e.g. you update DateTime object, do some simple manipulation with entity data within the entity, etc. But in our case we should use other services, i.e. use password encoder to encode user password properly - that's why we use event listeners / subscribers. Because you can't inject service into your entity, but you can inject it into other service. That's it.

    TL;DR So if things are pretty simple - prefer to use HasLifecycleCallbacks(), otherwise use listeners when things become more complex.

    Cheers!

  • 2016-10-07 Dennis Biedermann

    Hi Ryan,

    why don't you use the lifecycle callbacks right on the entity?
    F.e.:

    namespace AppBundle\Entity;

    use Doctrine\ORM\Mapping as ORM;
    use Symfony\Component\Validator\Constraints as Assert;

    /**
    * User
    *
    * @ORM\Table(name="user")
    * @ORM\Entity()
    * @ORM\HasLifecycleCallbacks()
    */
    class User
    {

    ... fields and functions

    /**
    * @ORM\PrePersist()
    */
    public function prePersist()
    {
    // ... stuff to do before persist to database
    }

    /**
    * @ORM\PrePersist()
    */
    public function preUpdate()
    {
    // ... stuff to do before persist to database
    }
    }

  • 2016-09-16 weaverryan

    Hey Yang!

    That's a GREAT question - I hate when things are just magically shown - so shame on me :).

    Of course, you can read the documentation. And, at the very least, you would probably need to read a little bit of documentation to *at least* know that there *is* an argument to this method. But, to know *what* class this is (i.e. that it is a LifecycleEventArgs), I like to simply dump() the variable to find out what it is:


    public function prePersist($event)
    {
    dump($event);die; // what are you!?
    }

    When in doubt, this is what I like to do!

    Cheers!

  • 2016-09-16 Yang Liu

    Hi,

    when you implemented the prePersist/preUpdate methods, they take a LifecycleEventArgs parameter. But for someone new to symfony like me, how do I know this is need here or even what this is called?

  • 2016-09-14 Victor Bocharsky

    Hey James,

    Yes! Actually, it depends on how you are you doing it, but the most common way is using the secondary $plainPassword property instead of the same ($password property) for it.

  • 2016-09-14 james

    Just understood now the whole process from the code above, I need to not try to add directly the password but send it to setplainPassword and then the listener will encode and setPassword for me so:

    not: $user->setPassword($userPassword["plainPassword"]);
    but: $user->setplainPassword($userPassword["plainPassword"]);

    Thanks again for your help!

  • 2016-09-14 Victor Bocharsky

    Hey James,

    I'm glad you handled with setting a password! Well, first of all you should achieve this event listener to work. Please, double check you defined event listener as service correctly, it should has a tag - { name: doctrine.event_subscriber }.
    Then ensure you have all necessary methods inside event listener class: prePersist() and preUpdate() - where I'd recommend you to simply add a die('listener works'); statement at the beginning of each method to see it works. Also your event listener must have `getSubscribedEvents()` method:


    public function getSubscribedEvents()
    {
    return ['prePersist', 'preUpdate'];
    }

    Please note, you must use exactly 'prePersist' and 'preUpdate' strings as it's a reserved event names. And the same names you should use for methods.

    BTW, please, ensure you clear the cache, I'd recommend you manually remove "dev" and "prod" folders in the cache directory.
    Let me know if it helps

    Cheers!

  • 2016-09-13 james

    Hi,

    I put a post previously in form. I have now got as a result of my code:

    $userPassword = $formForgotPassword->getData();

    var_dump($userPassword["plainPassword"]);

    $user->setPassword($userPassword["plainPassword"]);
    $em = $this->getDoctrine()->getManager();
    $em->persist($user);
    $em->flush();

    var_dump($user->getPassword());

    die();

    this:

    string(3) "123" string(3) "123"

    So now the password is here but it is not encoded... I have looked twice the video and just can not understand why and how to get this listener to listen to my controller which is normally called before any entity is inserted. I have tried to put a var_dump() but it just show me that when updating the password the listener is not called at all. How can I get this listener to listen when the user reset his/her password?

    Thanks

  • 2016-09-08 weaverryan

    Sweeeet! No worries, things *do* become deprecated (or I make other mistakes), and it's nice that there are people who take the time to let me know :).

    Cheers!

  • 2016-09-08 Mike Milano

    My mistake, I had used `Doctrine\Common\Persistence\Event\LifecycleEventArgs` instead of `Doctrine\ORM\Event\LifecycleEventArgs`. Sorry for the noise.

  • 2016-09-08 weaverryan

    Yo Mike!

    Hmm, are you sure about the deprecation? It actually makes sense - changes like this have been made in the past - but I don't see it marked as deprecated in the code yet: https://github.com/doctrine.... Is there another spot you were seeing it? I'd definitely be interested to know :).

    And quite likely about Ctrl+T - Command+T is a Mac thing, Windows & Linux are different (i.e. swap Command for Ctrl). And actually, unfortunately, *some* keyboard shortcuts are *totally* different between operating systems... and that's no fun at all :p.

    For those reading this later, PHPStorm's docs on shortcuts have a little OS selector in the upper right, so you can see what a shortcut is in your OS: https://www.jetbrains.com/h...

    Cheers!

  • 2016-09-08 Mike Milano

    It seems LifecycleEventArgs->getEntity() and LifecycleEventArgs->getEntityManager() are deprecated, replaced by LifecycleEventArgs->getObject() and LifecycleEventArgs->getObjectManager().

    Also, the for the refactor shortcut (super cool btw) the video mentions to use Command+T, when on my PHPStrom, it is Ctrl+T.

  • 2016-08-10 Victor Bocharsky

    Hey,

    What about LifecycleEventArgs class - it depends on do you use Doctrine ORM or no. Most probably you do in Symfony projects, so choose the second one which "Doctrine\ORM\LifecycleEventArgs", otherwise - choose the "Doctrine\Common\LifecycleEventArgs". But this things is hard to guess, that's why you should always based on its docs! In docs take to account what FQCN is used in the use statement. In popular third party libraries it always well documented, otherwise you just need to guess or inspect their code.

    Cheers!

  • 2016-08-10 3amprogrammer

    How do I know what class to include in use statement when phpstorm suggests multiple one? For instance of LifecycleEventArgs I have Doctrine\Common and Doctrine\ORM.

  • 2016-08-06 weaverryan

    Ah, great question :). Unfortunately, it's just one of those things that's up to the docs of the different libraries to support. *Ideally*, it would be on the php doc of the interface. For Doctrine, that's true (it simply says that this method returns an array of event names). For JMS, it should be in its docs, or you can go digging for it (https://github.com/schmittj.... In short, it's up to the implementation of the event system, so they can kinda do whatever they want. tl;dr: it can only really be found in the docs, or, buried in the code.

    Cheers!

  • 2016-08-05 Vlad

    Hi Ryan,
    How do I find out what options an event subscriber supports?
    Thank you!

  • 2016-08-04 weaverryan

    Yo Vlad!

    There is a slight difference between the Doctrine event subscriber and the event subscriber used by the JMSSerializerBundle. They philosophically do the same thing, but under the hood, Doctrine uses *its* event library, and JMS uses Symfony's event library. So the way that you wire them up is slightly different. So I don't think adding the "class" option works with Doctrine. BUT, Doctrine does support something slightly different, that does exactly this - it's called an entity listener. They're great, but the config is a little weird for me so I stick to subscribers. Check them out http://docs.doctrine-projec... and http://symfony.com/doc/curr...

    And yes to your code block - checking for !$entity->getPlainPassword() should do it - that's the bit I forgot!

    Cheers!

  • 2016-08-04 Vlad

    Hi Victor,
    That code refers to 3:24 of this video.
    Thanks,

  • 2016-08-04 Victor Bocharsky

    Hey Vlad,

    What do you mean here? What's your question and what are you trying to do?

  • 2016-08-03 Vlad

    Hi Ryan,
    In your REST tutorial, your getSubscribedEvents() method is slightly different:


    public static function getSubscribedEvents()
    {
    # Only call onPostSerialize for Programmer classes!
    return array(
    array(
    'event' => 'serializer.post_serialize',
    'method' => 'onPostSerialize',
    'format' => 'json',
    'class' => 'AppBundle\Entity\Programmer'
    )
    );
    }

    Instead of doing the if (!$entity instanceof User) check, can the getSubscribedEvents be refactored to the following?


    public static function getSubscribedEvents()
    {
    # Only call onPostSerialize for Programmer classes!
    return array(
    array(
    'event' => 'prePersist',
    'method' => 'prePersist',
    'class' => 'AppBundle\Entity\User'
    ),
    array(
    'event' => 'preUpdate',
    'method' => 'preUpdate',
    'class' => 'AppBundle\Entity\User'
    )
    );
    }
  • 2016-08-03 Vlad

    Would that do it?


    # Prevent the user's password from being encoded if plainPassword is blank.
    # This would mean that the User is being updated, but their password isn't being changed.
    if (!$entity->getPlainPassword()) {
    return;
    }

    $this->encodePassword($entity);
  • 2016-07-27 weaverryan

    Ah, this *probably* means that you don't have a setPlainPassword() method - so Alice doesn't know how to set the plainPassword field. Can you check to see if you have that?

    Cheers!

  • 2016-07-27 Andjii

    I got an error in a console, while tried to load fixtures:
    > purging database
    > loading AppBundle\DataFixtures\ORM\LoadFixtures

    [UnexpectedValueException]
    Could not determine how to assign plainPassword to a AppBundle\Entity\User object

    what should I do in this case?...