I would like to share with you a common error which can happen when using doctrine and it can be a nightmare until you notice why it's happening.
Let's see it using an example. Imagine we are working with these entities:
#[ORM\Entity(repositoryClass: UserRepository::class)]
class User {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 150)]
private string $name;
#[ORM\Column(length: 255)]
private string $pass;
#[ORM\Column(length: 100)]
private string $email;
// getters & setters
}
#[ORM\Entity(repositoryClass: UserAddressRepository::class)]
class UserAddress {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne]
private User $user;
#[ORM\Column(length: 150)]
private string $address;
// getters & setters
}
Now, when you get a request for a new register you do something like this:
$user = new User();
$user->setName('Peter');
$user->setPass('xxxxx');
$user->setEmail('peter.morris@gmail.com');
$address = new UserAddress();
$address->setUser($user);
$address->setAddress('Down street 25B');
$em->persist($user);
$em->persist($address);
$em->flush();
$eventDispatcher->dispatch(new UserRegisteredEvent($user));
So far, this code seems right. It creates a user and an Address, persists them on the database and dispatches and event. Let's see now the subscriber:
class UserSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents(): array
{
return [
UserRegisteredEvent::class => 'onUserRegistered'
];
}
public function onUserRegistered(UserRegisteredEvent $e){
$user = $e->getUser();
$address = $user->getAddress();
$addrName = $address->getAddress();
}
}
After executing line $addrName = $address->getAddress() you will get the following error:
Call to a member function getAddress() on null
That happens because we've persisted both entities but we have not refreshed user entity.
To avoid this kind of errors, we can follow the next steps (among others of course):
Refresh user object before passing it to the event
$em->persist($user);
$em->persist($address);
$em->flush();
$em->refresh($user);
$eventDispatcher->dispatch(new UserRegisteredEvent($user))
This allows us to having user re-hydrated before using in the subscriber.
Passing user identifier to the event
$em->persist($user);
$em->persist($address);
$em->flush();
$eventDispatcher->dispatch(new UserRegisteredEvent($user->getId()));
// -------------------------------------------------------
public function onUserRegistered(UserRegisteredEvent $e){
$user = $em->getRepository(User::class)->find($e->getId());
$user = $e->getUser();
$address = $user->getAddress();
$addrName = $address->getAddress();
}
This way pass the user identifier to the event so it's the subscriber who gets a fresh user from the database
Conclusion
This can seem a really simple error but it can be a nightmare when you have a project working on production and you start getting this kind of errors and your code is syntactically correct but not semantically.