i want to develop a doctrine-powered database application which should portray the following schema.
There are users, companies and relation-roles which describes the relation between a user to a company like [USER X] is [ROLE X] in [COMPANY X].
I'm using the symfony maker-bundle to create the entities I need. I'll attach every code at the end of this post.
To test the code, I persisted a company, a user, a role and a relation between them to the database. I expected that I could get all related users with their roles using a Company-Entity-Object with the generated getter getRelatedUsers() but I get an empty ArrayCollection.
This is how I tested to fetch the data in a TestController
#[Route('/test', name: 'test_page')]
public function index(CompanyRepository $companyRepository): Response
{
$companies = $companyRepository->findAll();
dd($companies[0]->getRelatedUsers());
}
Thanks for your help!
User.php
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=UserRepository::class)
* #ORM\Table(name="`user`")
*/
class User
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $fullName;
/**
* #ORM\OneToMany(targetEntity=UserCompanyRelation::class, mappedBy="user")
*/
private $relatedCompanies;
public function __construct()
{
$this->relatedCompanies = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getFullName(): ?string
{
return $this->fullName;
}
public function setFullName(string $fullName): self
{
$this->fullName = $fullName;
return $this;
}
/**
* #return Collection|UserCompanyRelation[]
*/
public function getRelatedCompanies(): Collection
{
return $this->relatedCompanies;
}
public function addRelatedCompany(UserCompanyRelation $relatedCompany): self
{
if (!$this->relatedCompanies->contains($relatedCompany)) {
$this->relatedCompanies[] = $relatedCompany;
$relatedCompany->setUser($this);
}
return $this;
}
public function removeRelatedCompany(UserCompanyRelation $relatedCompany): self
{
if ($this->relatedCompanies->removeElement($relatedCompany)) {
// set the owning side to null (unless already changed)
if ($relatedCompany->getUser() === $this) {
$relatedCompany->setUser(null);
}
}
return $this;
}
}
Company.php
<?php
namespace App\Entity;
use App\Repository\CompanyRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=CompanyRepository::class)
*/
class Company
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity=UserCompanyRelation::class, mappedBy="company")
*/
private $relatedUsers;
public function __construct()
{
$this->relatedUsers = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection|UserCompanyRelation[]
*/
public function getRelatedUsers(): Collection
{
return $this->relatedUsers;
}
public function addRelatedUser(UserCompanyRelation $relatedUser): self
{
if (!$this->relatedUsers->contains($relatedUser)) {
$this->relatedUsers[] = $relatedUser;
$relatedUser->setCompany($this);
}
return $this;
}
public function removeRelatedUser(UserCompanyRelation $relatedUser): self
{
if ($this->relatedUsers->removeElement($relatedUser)) {
// set the owning side to null (unless already changed)
if ($relatedUser->getCompany() === $this) {
$relatedUser->setCompany(null);
}
}
return $this;
}
}
UserCompanyRelation.php
<?php
namespace App\Entity;
use App\Repository\UserCompanyRelationRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=UserCompanyRelationRepository::class)
*/
class UserCompanyRelation
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=UserCompanyRelationRole::class, inversedBy="userCompanyRelations")
* #ORM\JoinColumn(nullable=false)
*/
private $role;
/**
* #ORM\ManyToOne(targetEntity=Company::class, inversedBy="relatedUsers")
* #ORM\JoinColumn(nullable=false)
*/
private $company;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="relatedCompanies")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
public function __construct()
{
}
public function getId(): ?int
{
return $this->id;
}
public function getRole(): ?UserCompanyRelationRole
{
return $this->role;
}
public function setRole(?UserCompanyRelationRole $role): self
{
$this->role = $role;
return $this;
}
public function getCompany(): ?Company
{
return $this->company;
}
public function setCompany(?Company $company): self
{
$this->company = $company;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
}
UserCompanyRelationRole.php
<?php
namespace App\Entity;
use App\Repository\UserCompanyRelationRoleRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=UserCompanyRelationRoleRepository::class)
*/
class UserCompanyRelationRole
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity=UserCompanyRelation::class, mappedBy="role")
*/
private $userCompanyRelations;
public function __construct()
{
$this->userCompanyRelations = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection|UserCompanyRelation[]
*/
public function getUserCompanyRelations(): Collection
{
return $this->userCompanyRelations;
}
public function addUserCompanyRelation(UserCompanyRelation $userCompanyRelation): self
{
if (!$this->userCompanyRelations->contains($userCompanyRelation)) {
$this->userCompanyRelations[] = $userCompanyRelation;
$userCompanyRelation->setRole($this);
}
return $this;
}
public function removeUserCompanyRelation(UserCompanyRelation $userCompanyRelation): self
{
if ($this->userCompanyRelations->removeElement($userCompanyRelation)) {
// set the owning side to null (unless already changed)
if ($userCompanyRelation->getRole() === $this) {
$userCompanyRelation->setRole(null);
}
}
return $this;
}
}
I found an solution. I had to modify the fetch-strategy to EAGER in a annotation attribute. I modified the relatedUsers annotation property in company.php
<?php
namespace App\Entity;
use App\Repository\CompanyRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=CompanyRepository::class)
*/
class Company
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity=UserCompanyRelation::class, mappedBy="company", fetch="EAGER")
*/
private $relatedUsers;
public function __construct()
{
$this->relatedUsers = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection|UserCompanyRelation[]
*/
public function getRelatedUsers(): Collection
{
return $this->relatedUsers;
}
public function addRelatedUser(UserCompanyRelation $relatedUser): self
{
if (!$this->relatedUsers->contains($relatedUser)) {
$this->relatedUsers[] = $relatedUser;
$relatedUser->setCompany($this);
}
return $this;
}
public function removeRelatedUser(UserCompanyRelation $relatedUser): self
{
if ($this->relatedUsers->removeElement($relatedUser)) {
// set the owning side to null (unless already changed)
if ($relatedUser->getCompany() === $this) {
$relatedUser->setCompany(null);
}
}
return $this;
}
}
You should use inversed by for bidirectional mapping between entities. See: https://www.doctrine-project.org/projects/doctrine-orm/en/2.9/reference/association-mapping.html#many-to-many-bidirectional
Related
how can i get doctrine in entity to get permission list?
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Security\Core\Role\Role;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass=UserRepository::class)
* #ORM\HasLifecycleCallbacks()
* #ORM\Table(name="`user`")
*/
class User implements UserInterface
{
private $entityManager;
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
private $discordId;
/**
* #ORM\Column(type="string", length=180)
*/
private $username;
/**
* #ORM\Column(type="string", length=255, unique=true)
*/
private $email;
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #ORM\OneToMany(targetEntity=Sites::class, mappedBy="owner", orphanRemoval=true)
*/
private $sites;
/**
* #ORM\PostLoad
* #ORM\PostPersist
*/
public function fetchEntityManager(LifecycleEventArgs $args)
{
$this->entityManager = $args->getEntityManager();
}
public function __construct()
{
$this->sites = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getdiscordId(): ?int
{
return $this->discordId;
}
public function setdiscordId(string $discordId): self
{
$this->discordId = $discordId;
return $this;
}
public function getUsername(): ?string
{
return $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
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->username;
}
public function getRoles() :array
{
// get permissions from group
$groupPermissionsRepo = $this->entityManager->getRepository(GroupPermissions::class);
$groupAccessList = $groupPermissionsRepo->getPermissionsForGroup($this->getGroupId());
// create access
$accessList = [];
foreach ($groupAccessList as $access) {
$accessList[] = new Role('ROLE_'.$access);
}
// set default role when group didn't have any access granted
if(count($groupAccessList) == 0)
$accessList[] = new Role('ROLE_USER');
// return access
return $accessList;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
/**
* #return Collection<int, Sites>
*/
public function getSites(): Collection
{
return $this->sites;
}
public function addSite(Sites $site): self
{
if (!$this->sites->contains($site)) {
$this->sites[] = $site;
$site->setOwner($this);
}
return $this;
}
public function removeSite(Sites $site): self
{
if ($this->sites->removeElement($site)) {
// set the owning side to null (unless already changed)
if ($site->getOwner() === $this) {
$site->setOwner(null);
}
}
return $this;
}
}
While using this code i get only error:
App\Entity\User::fetchEntityManager(): Argument #1 ($args) must be of type Doctrine\Common\Persistence\Event\LifecycleEventArgs, Doctrine\ORM\Event\LifecycleEventArgs given, called in C:\xampp\htdocs\dbshop2\vendor\doctrine\orm\lib\Doctrine\ORM\Event\ListenersInvoker.php on line 84
I there,
I'm trying to use api platform with symfony 4 but I'm unable to load related entities of an object even using normalizationContext and Groups.
For example, I have 2 classes FieldType and Field. Each Field have a single one FieldType. To load the Field with the fieldtype I created the "field" group which I use in Field to load all the propeties and also in FieldType to also load the field type properties. But at the end I still get the IRI in the returned JSON.
Can someone help me to figure out what is wrong?
Here is the code for the 2 classes:
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\AUDFieldRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use ApiPlatform\Core\Annotation\ApiSubresource;
/**
* #ApiResource(
* normalizationContext={"groups"={"field:read"}},
* )
* #ORM\Entity(repositoryClass=AUDFieldRepository::class)
* #ORM\Table(name="aud_field")
*/
class AUDField
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups("field:read")
*/
private $id;
/**
* #ORM\Column(type="string", length=255, unique=true)
* #Groups({"field:read"})
*/
private $name;
/**
* #ORM\Column(type="boolean")
*/
private $optional = false;
/**
* #ORM\Column(type="string", length=40, nullable=true)
* #Groups({"field:read"})
*/
private $inputType;
/**
* #ORM\Column(type="text", nullable=true)
* #Groups({"field:read"})
*/
private $inputValues;
/**
* #var array
* #ORM\Column(type="json_array", nullable=true)
* #Groups({"field:read"})
*/
private $specifications;
/**
* #ORM\ManyToOne(targetEntity=AUDFieldType::class)
* #ORM\JoinColumn(nullable=false)
* #Groups({"field:read"})
*/
private $type;
/**
* #ORM\ManyToMany(targetEntity=AUDAttributeType::class, mappedBy="fields")
* #Groups({"field:read"})
*/
private $attributesTypes;
public function __construct()
{
$this->attributesTypes = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getOptional(): bool
{
return $this->optional;
}
public function isOptional(): bool
{
return $this->optional;
}
public function setOptional(bool $bool): self
{
$this->optional = $bool;
return $this;
}
public function getSpecifications(): ?array
{
return $this->specifications;
}
public function getSpecification($key): ?array
{
return json_decode($this->specifications[$key]);
}
public function setSpecifications(?array $specs): self
{
$this->specifications = $specs;
return $this;
}
public function setSpecification($key, $value): ?array
{
return $this->specifications[$key] = $value;
}
public function hasSpecification($key): bool
{
return array_key_exists($key, $this->specifications);
}
/*public function addSpecification($key, $value): self
{
$this->specifications[$key] = $value;
return $this;
}
public function removeSpecification($key): self
{
unset($this->specifications[$key]);
return $this;
}*/
public function getInputValues(): ?string
{
return $this->inputValues;
}
public function setInputValues(?string $inputValues): self
{
$this->inputValues = $inputValues;
return $this;
}
public function getInputType(): ?string
{
return $this->inputType;
}
public function setInputType(?string $inputType): self
{
$this->inputType = $inputType;
return $this;
}
public function getType(): ?AUDFieldType
{
return $this->type;
}
public function setType(?AUDFieldType $type): self
{
$this->type = $type;
return $this;
}
/**
* #return Collection|AUDAttributeType[]
*/
public function getAttributesTypes(): Collection
{
return $this->attributesTypes;
}
public function addAttributesType(AUDAttributeType $attributesType): self
{
if (!$this->attributesTypes->contains($attributesType)) {
$this->attributesTypes[] = $attributesType;
$attributesType->addField($this);
}
return $this;
}
public function removeAttributesType(AUDAttributeType $attributesType): self
{
if ($this->attributesTypes->removeElement($attributesType)) {
$attributesType->removeField($this);
}
return $this;
}
}
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use App\Repository\AUDFieldTypeRepository;
/**
* #ApiResource(normalizationContext={"groups"={"field:read"}})
* #ORM\Entity(repositoryClass=AUDFieldTypeRepository::class)
* #ORM\Table(name="aud_field_type")
*/
class AUDFieldType
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"field:read"})
*/
private $id;
/**
* #ORM\Column(type="string", length=100)
* #Groups({"field:read"})
*/
private $name;
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
}
End the Postman output for http://127.0.0.1:8000/api/field/1:
{
"#context": "/api/contexts/AUDField",
"#id": "/api/field/1",
"#type": "AUDField",
"id": 1,
"name": "Identifiant",
"specifications": {
"minlength": 4
},
"type": "/api/fieldtype/1",
"attributesTypes": [
"/api/attributetype/1"
]
}
And for http://127.0.0.1:8000/api/fieldtype/1:
{
"#context": "/api/contexts/AUDFieldType",
"#id": "/api/fieldtype/1",
"#type": "AUDFieldType"
}
Try to remove normalizationContext in AUDFieldType class or rename it with field-type:read.
I have a problem about the flush on 2 linked entities (ManyToOne, OneToMany).
I'm on Symfony 5.1.
I just want to persist one "UserSavedCard" with many "UserCartSavedProducts" entities.
But I have an error when I flushed my entities and this error come to this file "in vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php (line 3013)"
My function that throw the error :
/**
* Save current user cart in database for later
* #param string|null $title
*/
public function saveCart(?string $title)
{
$cart = $this->getCart();
$cartSaved = new UserCartSaved();
$cartSaved->setUser($this->security->getUser());
$this->em->persist($cartSaved);
foreach ($cart as $item) {
$savedProduct = new UserCartSavedProducts();
$savedProduct->setProduct($item['product']);
$savedProduct->setUserCartSaved($cartSaved);
$this->em->persist($savedProduct);
}
$this->em->flush();
}
When I execute this code above But I have this error :
Notice: Undefined index: 000000007e86ae93000000003f3a2fbb
There is my entites :
UserCartSaved:
<?php
namespace App\Entity;
use App\Repository\UserCartSavedRepository;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints\Date;
/**
* #ORM\Entity(repositoryClass=UserCartSavedRepository::class)
*/
class UserCartSaved
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="userCartSaveds")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\OneToMany(targetEntity=UserCartSavedProducts::class, mappedBy="userCartSaved")
*/
private $userCartSavedProducts;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
public function __construct()
{
$this->userCartSavedProducts = new ArrayCollection();
$this->createdAt = new DateTime();
}
public function getId(): ?int
{
return $this->id;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
/**
* #return Collection|UserCartSavedProducts[]
*/
public function getUserCartSavedProducts(): Collection
{
return $this->userCartSavedProducts;
}
public function addUserCartSavedProduct(UserCartSavedProducts $userCartSavedProduct): self
{
if (!$this->userCartSavedProducts->contains($userCartSavedProduct)) {
$this->userCartSavedProducts[] = $userCartSavedProduct;
$userCartSavedProduct->setUserCartSaved($this);
}
return $this;
}
public function removeUserCartSavedProduct(UserCartSavedProducts $userCartSavedProduct): self
{
if ($this->userCartSavedProducts->contains($userCartSavedProduct)) {
$this->userCartSavedProducts->removeElement($userCartSavedProduct);
// set the owning side to null (unless already changed)
if ($userCartSavedProduct->getUserCartSaved() === $this) {
$userCartSavedProduct->setUserCartSaved(null);
}
}
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
}
UserCartSavedProducts :
<?php
namespace App\Entity;
use App\Repository\UserCartSavedProductsRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=UserCartSavedProductsRepository::class)
*/
class UserCartSavedProducts
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=UserCartSaved::class, inversedBy="userCartSavedProducts")
* #ORM\JoinColumn(nullable=false)
*/
private $userCartSaved;
/**
* #ORM\ManyToOne(targetEntity=Product::class, inversedBy="userCartSavedProducts", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*/
private $product;
public function getId(): ?int
{
return $this->id;
}
public function getUserCartSaved(): ?UserCartSaved
{
return $this->userCartSaved;
}
public function setUserCartSaved(?UserCartSaved $userCartSaved): self
{
$this->userCartSaved = $userCartSaved;
return $this;
}
public function getProduct(): ?Product
{
return $this->product;
}
public function setProduct(?Product $product): self
{
$this->product = $product;
return $this;
}
}
Product
<?php
namespace App\Entity;
use App\Repository\ProductRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=ProductRepository::class)
*/
class Product
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity=UserCartSavedProducts::class, mappedBy="product")
*/
private $userCartSavedProducts;
public function __construct()
{
$this->userCartSavedProducts = new ArrayCollection();
}
/**
* #return Collection|UserCartSavedProducts[]
*/
public function getUserCartSavedProducts(): Collection
{
return $this->userCartSavedProducts;
}
public function addUserCartSavedProduct(UserCartSavedProducts $userCartSavedProduct): self
{
if (!$this->userCartSavedProducts->contains($userCartSavedProduct)) {
$this->userCartSavedProducts[] = $userCartSavedProduct;
$userCartSavedProduct->setProduct($this);
}
return $this;
}
public function removeUserCartSavedProduct(UserCartSavedProducts $userCartSavedProduct): self
{
if ($this->userCartSavedProducts->contains($userCartSavedProduct)) {
$this->userCartSavedProducts->removeElement($userCartSavedProduct);
// set the owning side to null (unless already changed)
if ($userCartSavedProduct->getProduct() === $this) {
$userCartSavedProduct->setProduct(null);
}
}
return $this;
}
}
I've run into an issue getting the same error, and in my case is that I was storing the entity object into a session and trying to flush it "as is" after retrieval. Regardless I could retrieve and dump the object and everything seemed fine (until flush), I was told that the EM lost management of the contents. So, to get management back, I had to retrieve every single id of every component of the stored object via its own getter, and then via find, retrieve them all and set them again into a new entity instance.
I have a user entity that has a many-to-many relationship with another entity Roles.
I have a form that allows me to create a new user and for that, I use an EntityType for the manyToMany relationship 'roles'.
Here is my form
$builder
->add('username',null,['label' => 'Email'])
->add('password',null,['label'=>'Password'])
->add('roles',EntityType::class, [
'multiple' => true,
'class' => Role::class,
'choice_label' => 'role_name',
]
)
;
Everything works fine but when I submit the form I got this error:
Expected argument of type "App\Entity\Role", "string" given at
property path "roles".
EDIT:
here is my User entity :
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity(repositoryClass=UserRepository::class)
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $username;
/**
* #ORM\Column(type="string", length=255)
*/
private $password;
/**
* #ORM\ManyToMany(targetEntity=Role::class, inversedBy="users")
*/
private $roles;
public function __construct()
{
$this->roles = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getUsername(): ?string
{
return $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
public function getRoles()
{
return ['ROLE_ADMIN'];
}
public function getSalt()
{
return null;
}
public function eraseCredentials()
{
}
public function addRole(Role $role): self
{
if (!$this->roles->contains($role)) {
$this->roles[] = $role;
$role->addUser($this);
}
return $this;
}
public function removeRole(Role $role): self
{
if ($this->roles->contains($role)) {
$this->roles->removeElement($role);
$role->removeUser($this);
}
return $this;
}
}
Here is my Role entity :
<?php
namespace App\Entity;
use App\Repository\RoleRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=RoleRepository::class)
*/
class Role
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $role_name;
/**
* #ORM\ManyToMany(targetEntity=User::class, mappedBy="roles")
*/
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getRoleName(): ?string
{
return $this->role_name;
}
public function setRoleName(string $role_name): self
{
$this->role_name = $role_name;
return $this;
}
/**
* #return Collection|User[]
*/
public function getUsers(): Collection
{
return $this->users;
}
public function addUser(User $user): self
{
if (!$this->users->contains($user)) {
$this->users[] = $user;
}
return $this;
}
public function removeUser(User $user): self
{
if ($this->users->contains($user)) {
$this->users->removeElement($user);
}
return $this;
}
}
I guess the problem is here
public function getRoles()
{
return ['ROLE_ADMIN'];
}
you should return an array or Role entity not string, it should be something like this
public function getRoles()
{
return $this->roles;
}
what if i'm using ArrayCollection in User entity?
/**
* #var Collection|Role[]
* #ORM\ManyToMany(targetEntity="App\Entity\Role")
* #ORM\JoinTable(
* name="user_roles",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="role_id", referencedColumnName="id")}
* )
*/
private $roles;
public function __construct()
{
$this->roles = new ArrayCollection();
}
public function getRoles()
{
return $this->roles;
}
I have two entities, Classe and students, the class has a OneToMany relation with Student as one class can have many students.
I'm creating a page where I show the names of all the students related to a chosen classes and I can't access the data in students from Classe.
entity class
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\ClasseRepository")
*/
class Classe
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=200)
*/
private $label;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Student", mappedBy="classe", orphanRemoval=true)
*/
private $Students;
/**
* #ORM\OneToMany(targetEntity="App\Entity\WorkDays", mappedBy="class")
*/
private $schedule;
public function __construct()
{
$this->Students = new ArrayCollection();
$this->schedule = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getLabel(): ?string
{
return $this->label;
}
public function setLabel(string $label): self
{
$this->label = $label;
return $this;
}
/**
* #return Collection|Student[]
*/
public function getStudents(): Collection
{
return $this->Students;
}
public function addStudent(Student $student): self
{
if (!$this->Students->contains($student)) {
$this->Students[] = $student;
$student->setClasse($this);
}
return $this;
}
public function removeStudent(Student $student): self
{
if ($this->Students->contains($student)) {
$this->Students->removeElement($student);
// set the owning side to null (unless already changed)
if ($student->getClasse() === $this) {
$student->setClasse(null);
}
}
return $this;
}
/**
* #return Collection|WorkDays[]
*/
public function getSchedule(): Collection
{
return $this->schedule;
}
public function addSchedule(WorkDays $schedule): self
{
if (!$this->schedule->contains($schedule)) {
$this->schedule[] = $schedule;
$schedule->setClass($this);
}
return $this;
}
public function removeSchedule(WorkDays $schedule): self
{
if ($this->schedule->contains($schedule)) {
$this->schedule->removeElement($schedule);
// set the owning side to null (unless already changed)
if ($schedule->getClass() === $this) {
$schedule->setClass(null);
}
}
return $this;
}
}
Entity Student
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\StudentRepository")
*/
class Student
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=200)
*/
private $firstname;
/**
* #ORM\Column(type="string", length=200)
*/
private $lastname;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\classe", inversedBy="Students")
* #ORM\JoinColumn(nullable=false)
*/
private $classe;
public function getId(): ?int
{
return $this->id;
}
public function getFirstname(): ?string
{
return $this->firstname;
}
public function setFirstname(string $firstname): self
{
$this->firstname = $firstname;
return $this;
}
public function getLastname(): ?string
{
return $this->lastname;
}
public function setLastname(string $lastname): self
{
$this->lastname = $lastname;
return $this;
}
public function getClasse(): ?classe
{
return $this->classe;
}
public function setClasse(?classe $classe): self
{
$this->classe = $classe;
return $this;
}
}
As I said before, a class can have many students and I need to get the names of the students that a related to that class and put them in a list.
Because the entities are related to eachother you can access the student info of a class through the Entity Classe or the other way arround. For example:
Controller:
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class ClasseController extends AbstractController {
/**
* #Route("/{id}", name="index", methods={"GET"})
*/
public function index(ClasseRepository $repository, $id) {
return $this->render('index.html.twig', [
'students' => $repository->getStudents($id),
]);
}
Repository:
namespace App\Repository;
use App\Entity\Classe;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;
class ClasseRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry)
{
parent::__construct($registry, Classe::class);
}
public function getStudents($id) {
return $this->createQueryBuilder('c')
->select('c.Students')
->andWhere('c.id = :id')
->setParameter('id', $id)
->getQuery()
->getResult()
;
}
}