Home > Software design >  ORM\OneToMany not loading Objects automatically with get method
ORM\OneToMany not loading Objects automatically with get method

Time:06-27

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);
        }
  • Related