I have got a problem with lazy loading a one to many relation. I have got an User
object, which can have many Permission
Objects. As soon as I load a User, the Permissions are not initialized. To my understanding they should be loaded automatically as soon as I call getPermissions()
but they are not loaded and the Permissions
Object still does not get initialized. When I fetch: "EAGER"
it is working fine. Optionally it is also working if I call
$permissions=$user->getPermissions();
$this->getDoctrine()->getManager()->initializeObject($permissions);
What am I misunderstanding? Shouldnt Doctrine fetch my relations automatically? Thanks
Permissions Entity
<?php
namespace App\Entity;
use App\Repository\PermissionsRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: PermissionsRepository::class)]
class Permissions
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\ManyToOne(targetEntity: Company::class)]
#[ORM\JoinColumn(nullable: false)]
private $Company;
#[ORM\Column(type: 'string', length: 255)]
private $identifier;
#[ORM\Column(type: 'integer')]
private $value;
#[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'permissions')]
#[ORM\JoinColumn(nullable: false)]
private $user;
public function getId(): ?int
{
return $this->id;
}
public function getCompany(): ?Company
{
return $this->Company;
}
public function setCompany(?Company $Company): self
{
$this->Company = $Company;
return $this;
}
public function getIdentifier(): ?string
{
return $this->identifier;
}
public function setIdentifier(string $identifier): self
{
$this->identifier = $identifier;
return $this;
}
public function getValue(): ?int
{
return $this->value;
}
public function setValue(int $value): self
{
$this->value = $value;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
}
User Entity
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[UniqueEntity(fields: ['email'], message: 'There is already an account with this email')]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 180, unique: true)]
private $email;
#[ORM\Column(type: 'json')]
private $roles = [];
#[ORM\Column(type: 'string')]
private $password;
#[ORM\OneToOne(inversedBy: 'user', targetEntity: Customer::class, cascade: ['persist', 'remove'])]
private $customer_id;
#[ORM\Column(type: 'boolean')]
private $isVerified = false;
#[ORM\OneToMany(mappedBy: 'user', targetEntity: Permissions::class , cascade: ['persist', 'remove'], orphanRemoval: true)]
private $permissions;
public function __construct()
{
$this->permissions = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->email;
}
/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* @see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* @see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getCustomerId(): ?Customer
{
return $this->customer_id;
}
public function setCustomerId(?Customer $customer_id): self
{
$this->customer_id = $customer_id;
return $this;
}
public function isVerified(): bool
{
return $this->isVerified;
}
public function setIsVerified(bool $isVerified): self
{
$this->isVerified = $isVerified;
return $this;
}
/**
* @return Collection<int, Permissions>
*/
public function getPermissions(): Collection
{
return $this->permissions;
}
public function addPermission(Permissions $permission): self
{
if (!$this->permissions->contains($permission)) {
$this->permissions[] = $permission;
$permission->setUser($this);
}
return $this;
}
public function removePermission(Permissions $permission): self
{
if ($this->permissions->removeElement($permission)) {
// set the owning side to null (unless already changed)
if ($permission->getUser() === $this) {
$permission->setUser(null);
}
}
return $this;
}
}
EDIT:
I read that the get only works on the original object. But even using the original object doesn`t work for me.
$user_repository = $entityManager->getRepository(User::class);
$user=$user_repository->findOneBy(array('id' => 1));
$user->getPermissions();
dump($user);
EDIT2
This is the DUMP of User
App\Entity\User {#705 ▼
-id: 1
-email: "[email protected]"
-roles: []
-password: "*********************"
-customer_id: null
-isVerified: true
-permissions: Doctrine\ORM\PersistentCollection {#710 ▼
#collection: Doctrine\Common\Collections\ArrayCollection {#712 ▼
-elements: []
}
#initialized: false
-snapshot: []
-owner: App\Entity\User {#705}
-association: array:15 [ …15]
-em: Doctrine\ORM\EntityManager {#517 …11}
-backRefFieldName: "user"
-typeClass: Doctrine\ORM\Mapping\ClassMetadata {#707 …}
-isDirty: false
}
}
CodePudding user response:
The best solution is to make your own function in the UserRpository.php
to fetch the permissions. You can do it with something like that :
public function getUserWithPermissions(int $idUser)
{
$query = $this->createQueryBuilder('u')
->addSelect('p') // to make Doctrine actually use the join
->leftJoin('u.permissions', 'p')
->where('u.id= :idUser')
->setParameter('idUser', $idUser)
->getQuery();
return $query->getOneOrNullResult();
}
And after that you can access it in your Controller for example, with :
$User = $entityManager->getRepository(User::class)->getUserWithPermissions($idUser);
$permissions = $User->getPermissions();
NB : with this solution, you can remove the EAGER
option and it's best to do it as I say for performance reasons.
CodePudding user response:
The user object only includes a doctrine collection. So $User->getPermissions();
is returning a doctrine collection. It seems like this is not enough to just call $User->getPermissions();
to initialize the collection, but really access a permissions object and its data inside the collection by $user->getPermissions()->current();
afterwards the user object.
$user = $user_repository->findOneBy(array('id' => 1));
dump($user);
$permissions = $user->getPermissions()->current();
dump($permissions);
dump($user);
Looping over the collection as proposed by Monnomcjo also allows to access the data:
foreach ($user->getPermissions() as $permission) {
dump($permission);
}