I am using an EntityType in a formType in Symfony 4. When I am displayed it in a new form all works great, the choicetype liste contain all the values from my database. But when I load existing data from my database the choice list don't load the value from the database, it display the placeholer. I have checked in the database and the value is saved. My form looks like this
->add('operatingSystem', EntityType::class, [
'class' => "App\Entity\TicketOperatingSystem",
'choice_label' => 'name',
'label' => 'Système d\'exploitation',
'required' => false,
])
The entity :
/**
* #ORM\ManyToOne(targetEntity="Maps_red\TicketingBundle\Entity\TicketOperatingSystem")
* #ORM\JoinColumn(name="operating_system", referencedColumnName="id", nullable=true)
*/
private $operatingSystem;
public function getOperatingSystem(): ?TicketOperatingSystemInterface
{
return $this->operatingSystem;
}
public function setOperatingSystem(?TicketOperatingSystemInterface $ticketOperatingSystem): UserInstance
{
$this->operatingSystem = $ticketOperatingSystem;
return $this;
}
OperatingSystem Entity :
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
public function getId() : ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): TicketOperatingSystemInterface
{
$this->name = $name;
return $this;
}
For testing I try to change the EntityType to a ChoiceType with a list of 2, it works perfectly. I don't know what the problem with EntityType is.
Try adding 'choice_value' => 'name',
->add('operatingSystem', EntityType::class, [
'class' => "App\Entity\TicketOperatingSystem",
'choice_label' => 'name',
'choice_value' => 'name',
'label' => 'Système d\'exploitation',
'required' => false,
])
Related
I develop with symfony a form for create tickets but when i try i got this error:
my function buildform from the file tickettype.php:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('Titre', TextType::class)
->add('Message', TextType::class)
->add('Date', DateTimeType::class, ['data' => new \DateTime()] )
->add('Demandeur', EntityType::class, [
'class' => Client::class,
'choice_label' => 'Nom',
])
->add('Agent', EntityType::class, [
'class' => Dealer::class,
'choice_label' => 'Nom',
])
->add('Etat_Ticket', EntityType::class, [
'class' => Etat::class,
'choice_label' => 'Statut',
]);
}
and in the controller :
/**
* #Route("/add/", name="add_ticket")
*
* #param Request $request
* #return \Symfony\Component\HttpFoundation\Response
*/
public function addTicketAction(Request $request)
{
$ticket = new Ticket();
$form = $this->createForm(TicketType::class, $ticket);
$form->add('send', SubmitType::class, ['label' => 'créé un nouveau ticket']);
$form->handleRequest($request);
if($form->isSubmitted()){
$ticket->setDate(new \DateTime());
$em = $this->getDoctrine()->getManager();
$em->persist($ticket);
$em->flush();
return $this->redirectToRoute('List_ticket');
}
return $this->render("add.html.twig", array('form' => $form->createView()));
}
and my entity Ticket have this property:
/**
* #ORM\ManyToOne(targetEntity=Etat::class, inversedBy="Etat_Ticket")
* #ORM\JoinColumn(nullable=false)
*/
private $Etat_Ticket;
link to the entity Etat which look like this :
/**
* Etat
*
* #ORM\Table(name="etat")
* #ORM\Entity
*/
class Etat
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="statut", type="string", length=255, nullable=false)
*/
private $statut;
public function getId(): ?int
{
return $this->id;
}
public function getStatut(): ?string
{
return $this->statut;
}
public function setStatut(string $statut): self
{
$this->statut = $statut;
return $this;
}
}
You don't have a getter for Etat_Ticket property, so symfony (and that's a PHP common rule, when the property is not public) can't read its value from outside class scope. Symfony Form component, here, is trying by default to use the getter or access the property directly if it were public, and as neither getter nor public property are found, it symply doesn't know how to retrieve the value.
You can feed the form by yourself (docs) or use property_path.
Remember also that binding entities (or in general the domain model) directly to a form is good for RAD (rapid application development) but not so good in the long term. I would suggest to use a sort of DTO in order to read and write from and to the model (take a look here in order to get an idea about this concept)
I made a page on symfony to administrate some products with a dropdown menu linked to a category table.
The categoryid is well saved on the product, but the dropdown never select the good option, I don't find why.
Here is the category part in my class Dproduct :
/**
* #ORM\ManyToOne(targetEntity=RCategory::class, inversedBy="products")
* #ORM\JoinColumn(name="categoryId", referencedColumnName="categoryId", onDelete="CASCADE")
*/
private $categoryId;
public function getCategoryId(): ?RCategory
{
return $this->categoryId;
}
public function setCategoryId(?RCategory $categoryId): self
{
$this->categoryId = $categoryId;
return $this;
}
here is my class RCategory:
/**
* RCategory
*
* #ORM\Table(name="r_category")
* #ORM\Entity
*/
class RCategory
{
/**
* #var int
*
* #ORM\Column(name="categoryId", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $categoryId;
/**
* #var string
*
* #ORM\Column(name="categoryLib", type="string", length=50, nullable=false)
*/
private $categoryLib;
/**
* #Gedmo\Slug(fields={"categoryLib"})
* #ORM\Column(type="string", length=128, unique=true)
*/
public function getCategoryId(): ?int
{
return $this->categoryId;
}
public function getCategoryLib(): ?string
{
return $this->categoryLib;
}
public function setCategoryLib(string $categoryLib): self
{
$this->categoryLib = $categoryLib;
return $this;
}
here is my buildForm function :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('categoryId', ChoiceType::class, [
'mapped' => false,
'choices' => $options['choiceCategory'],
'label' => 'Catégorie'
])
->add('productLib', TextType::class, [
'label' => 'Libellé',
])
->add('Enregister', SubmitType::class)
;
}
And my choiceCategory function :
public function choiceCategory(){
$choices = [];
$categories = $this->getCategory();
foreach ($categories as $categorie) {
$choices[$categorie->getCategorylib()] = $categorie->getCategoryid();
}
return $choices;
}
And finaly my controller :
$product = $this->getDoctrine()
->getRepository('App:DProduct')
->findOneBy(array('slug' => $slug));
...
$form = $this->createForm(productType::class, $product, array('choiceCategory'=>$categoryController->choiceCategory()));
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
... Data is well saved ...
}
return $this->render(
'form/meuble.html.twig',
array(
'form' => $form->createView(),
'titlePage' => $titlePage,
'product' => $product
)
);
Does anyone see where is the problem ?
Thanks,
Best regards,
Matthieu
You are using an unmapped field, therefore it won't set any data based on the product you are editing.
You can set the data with de data option
$product = $builder->getData();
...
->add('categoryId', ChoiceType::class, [
'mapped' => false,
'choices' => $options['choiceCategory'],
'label' => 'Catégorie'
'data' => $product->getCategory(),
])
U might be able to use a mapped field and use the EntityType instead of the ChoiceType to build the choices. Notice the choice_label and query_builder options.
It's simple im doing a permission table from Companies and Users where i have to store the user id and the company id right now i have this and i get this error
Expected value of type "App\Entity\CompanyUserPermissionMap" for association field "App\Entity\User#$companyUserPermissionMaps", got "App\Entity\Company" instead.
User Entity
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $userId;
/**
* #ORM\Column(type="string", length=12)
*/
private $code;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
private $email;
/**
* #var CompanyUserPermissionMap[]
* #ORM\OneToMany(targetEntity="App\Entity\CompanyUserPermissionMap", mappedBy="user", orphanRemoval=true)
*/
private $companyUserPermissionMaps;
public function __construct()
{
$this->companyUserPermissionMaps = new ArrayCollection();
}
public function getCompanyUserPermissionMaps(): Collection
{
return $this->companyUserPermissionMaps;
}
public function addCompanyUserPermissionMaps(CompanyUserPermissionMaps $permission): self
{
if (!$this->companyUserPermissionMaps->contains($permission)) {
$this->companyUserPermissionMaps[] = $permission;
$permission->setUser($this);
}
return $this;
}
Company Entity
#########################
## PROPERTIES ##
#########################
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $companyId;
/**
* #ORM\Column(type="string", length=6, unique=true)
*/
private $code;
/**
* #var CompanyUserPermissionMap[]
* #ORM\OneToMany(targetEntity="App\Entity\CompanyUserPermissionMap", mappedBy="company", orphanRemoval=true)
*/
private $companyUserPermissionMaps;
public function __construct()
{
$this->companyUserPermissionMaps = new ArrayCollection();
}
/**
* #return Collection|CompanyUserPermissionMaps[]
*/
public function getCompanyUserPermissionMaps(): Collection
{
return $this->companyUserPermissionMaps;
}
public function addCompanyUserPermissionMaps(AccountingBankPermission $permission): self
{
if (!$this->companyUserPermissionMaps->contains($permission)) {
$this->companyUserPermissionMaps[] = $permission;
$permission->setAccount($this);
}
return $this;
}
public function removeCompanyUserPermissionMaps(AccountingBankPermission $permission): self
{
if ($this->companyUserPermissionMaps->contains($permission)) {
$this->companyUserPermissionMaps->removeElement($permission);
// set the owning side to null (unless already changed)
if ($permission->getAccount() === $this) {
$permission->setAccount(null);
}
}
return $this;
}
relation table
Column(type="integer")
private $companyUserPermissionId;
/**
* #var User
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="companyUserPermissionMaps")
* #ORM\JoinColumn(referencedColumnName="user_id", nullable=false)
*/
private $user;
/**
* #var Company
* #ORM\ManyToOne(targetEntity="App\Entity\Company", inversedBy="companyUserPermissionMaps")
* #ORM\JoinColumn(referencedColumnName="company_id", nullable=true)
*/
private $company;
/**
* #return int|null
*/
public function getCompanyUserPermissionId(): ?int
{
return $this->companyUserPermisionId;
}
/**
* #return User
*/
public function getUser(): ?User
{
return $this->user;
}
/**
* #param User $user
* #return AccountingBankPermission
*/
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
/**
* #return Company
*/
public function getCompany(): ?Company
{
return $this->company;
}
/**
* #param array $company
* #return CompanyUserPermissionMap
*/
public function setCompany(?array $company): self
{
$this->company = $company;
return $this;
}
Form type
$builder
->add('roles' ,ChoiceType::class ,[
'required' => true,
'choices' => $this->roles,
'multiple' => true,
'expanded' => true,
'label_attr' => [
'class' => 'custom-control-label',
],
'choice_attr' => function($val, $key, $index) {
return ['class' => 'custom-control-input'];
},
'attr'=>['class' =>'custom-checkbox custom-control']
])
->add('email', EmailType::class, [
'label' => "E-Mail"
])
->add('firstName', TextType::class, [
'label' => "First Name"
])
->add('lastName', TextType::class, [
'label' => "Last Name"
])
->add('companyUserPermissionMaps' ,EntityType::class ,[
'required' => true,
'class' => Company::class,
'label' => 'Compañia',
'multiple' => true,
'expanded' => false,
'choice_label' => 'legalName',
'mapped'=>false
])
->add('save', SubmitType::class, [
'label' => "Save"
])
and my controller function looks like this
$user = new User();
$originalRoles = $this->getParameter('security.role_hierarchy.roles');
$options=['roles' => $originalRoles ];
$form = $this->createForm(UserType::class, $user, $options);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$tempPassword = "some pass";
$user->setPassword($encoder->encodePassword(
$user,
$tempPassword
));
$companies=[];
$companiesForm=$form->get('companyUserPermissionMaps')->getData();
foreach ($companiesForm as $value) {
$companies[] = $value->getCompanyId();
}
// Save object to database
$entityManager = $this->getDoctrine()->getManager();
/** #var CompanyRepository $companyRepository */
$companyRepository = $this->getDoctrine()->getRepository(Company::class);
$companiesArr =$companyRepository->findCompanyByArray($companies);
$companyUserPermissionMap = new CompanyUserPermissionMap();
$companyUserPermissionMap->setUser($user);
$companyUserPermissionMap->setCompany($companiesArr);
$entityManager->persist($user);
$entityManager->persist($companyUserPermissionMap);
update
Okay, so I neglected to mention the problem you're actually facing. The form component, smart as it is, will try to call getters and setters when loading/modifying the form data. Since your form contains ->add('companyUserPermissionMaps',..., the form component will call getCompanyUserPermissionMaps on your entity (which will return a currently probably empty collection) and will try to write back to the entity via either setCompanyUserPermissionMaps or add/remove instead of set, if they are present.
Since your field actually behaves as if it holds a collection of Company objects, the setting of those on your User object will obviously fail with the error message you encountered:
Expected value of type "App\Entity\CompanyUserPermissionMap" for association field "App\Entity\User#$companyUserPermissionMaps", got "App\Entity\Company" instead.
which absolutely makes sense. So, this problem can be fixed in different ways. The one way which you apparently already tried was setting mapped to false, but instead of false you used 'false' (notice the quotes), which evaluates to true ... ironically. So to use your approach, you would have to remove the quotes. However, I propose a different approach, which I would much prefer!
end update
My general advice would be to hide stuff you don't want to show. So, in your form builder instead of
->add('companyUserPermissionMaps', EntityType::class, [
'required' => true,
'class' => Company::class,
'label' => 'Compañia',
'multiple' => true,
'expanded' => false,
'choice_label' => 'legalName',
'mapped'=>'false'
])
which obviously already is not a field that handles CompanyUserPermissionMaps but companies instead - so apparently you suspected this is semantically something different, you should go back to the User entity and give it a function getCompanies instead
public function getCompanies() {
return array_map(function ($map) {
return $map->getCompany();
}, $this->getCompanyUserPermissionsMaps());
}
public function addCompany(Company $company) {
foreach($this->companyUserPermissionMaps->toArray() as $map) {
if($map->getCompany() === $company) {
return;
}
}
$new = new CompanyUserPermissionMap();
$new->setCompany($company);
$new->setUser($this);
$this->companyUserPermissionMaps->add($new);
}
public function removeCompany(Company $company) {
foreach($this->companyUserPermissionMaps as $map) {
if($map->getCompany() == $company) {
$this->companyUserPermissionMaps->removeElement($map);
}
}
}
you would then call the field companies (so ->add('companies', ...)) and act on Company entities instead of those pesky maps. (I also don't really like exposing ArrayCollection and other internals to the outside. But hey, that's your decision.)
however, if your Maps are going to hold more values at some point, you actually have to work with the maps in your form, and not just with Company entities.
I have three entity (Activite, CabinetEquipe and ActiviteEquipe). When I add Activite, I want to add many CabinetEquipe in ActiviteEquipe.
CabinetEquipe entity
/**
* #var string
*
* #ORM\Column(name="libelle", type="string", nullable=false, length=125)
*/
private $libelle;
Activite entity
/**
* #ORM\OneToMany(targetEntity="LogicielBundle\Entity\ActiviteEquipe", mappedBy="activite", cascade={"persist", "remove"})
*/
private $equipes;
public function addEquipe(\LogicielBundle\Entity\ActiviteEquipe $equipe)
{
$this->equipes[] = $equipe;
$equipe->setActivite($this);
return $this;
}
public function removeEquipe(\LogicielBundle\Entity\ActiviteEquipe $equipe)
{
$this->equipes->removeElement($equipe);
}
public function getEquipes()
{
return $this->equipes;
}
ActiviteEquipe entity
/**
* #ORM\ManyToOne(targetEntity="UtilisateurBundle\Entity\CabinetEquipe")
* #ORM\JoinColumn(nullable=false)
*/
private $equipe;
public function setEquipe(\UtilisateurBundle\Entity\CabinetEquipe $equipe)
{
$this->equipe = $equipe;
return $this;
}
public function getEquipe()
{
return $this->equipe;
}
But, after my form for add Activite, I have this error : Found entity of type UtilisateurBundle\Entity\CabinetEquipe on association LogicielBundle\Entity\Activite#equipes, but expecting LogicielBundle\Entity\ActiviteEquipe
My Form :
$builder->add('equipes', 'entity', array(
'label' => 'Équipe(s)',
'class' => 'UtilisateurBundle\Entity\CabinetEquipe',
'expanded' => true,
'multiple' => true,
'choices' => $equipes,
'property' => function($equipe) {
return $equipe->getLibelle();
}
));
Can you help me pleases ? I'm newbie in Symfony
You expect \LogicielBundle\Entity\ActiviteEquipe in your entity.
public function addEquipe(\LogicielBundle\Entity\ActiviteEquipe $equipe)
{
But in form you work on \UtilisateurBundle\Entity\CabinetEquipe.
$builder->add('equipes', 'entity', array(
'label' => 'Équipe(s)',
'class' => 'UtilisateurBundle\Entity\CabinetEquipe',
In form you need to choose entity which is expected in addEquipe and removeEquipe.
Looks like you expect some kind of proxy (ActiviteEquipe) which is imo not needed here.
I have this form with manyTomany relation working perfectly like this:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('manifestations', 'entity', array(
'class' => 'PrifProtocoleBundle:Manifestation',
'multiple' => true,
'expanded' => false,
'property' => 'libelle',
'empty_value' => 'Choississez',
'required' => false,));
}
but i want to set the'multiple' parameter to 'false', this way, i just have a select box with the option 'Choississez', so when i click on it, it displays all the other values. Unfortunately i get an error message: nor of the methods _set()" or "_call()" exist and have public access in class. i've been searching for some solutions on the web and tried this one:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('manifestations', 'collection', array(
'type' => 'entity',
'options' => array(
'class' => 'AcmeProtoBundle:Manifestation',
'multiple' => false,
'expanded' => false,
'property' => 'libelle',
'empty_value' => 'Choisissez',
'required' => false,)));
}
i have no error message! but the select form doesn't display even when i set the 'multiple' to 'true, i only have the submit button, when clicked shows me the results, so i think i miss something in the parameters to display the form!
can anyone help? Thanks
Manifestation.php
/**
* #ORM\Entity
* #ORM\Entity(repositoryClass="ManifestationRepository")
*/
class Manifestation {
public function __construct() {
$this->dateCreation = new \DateTime;
$this->dateModif = new \DateTime;
}
public function __toString() {
return $this->getLibelle();
}
/**
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Id
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="integer")
* #Assert\GreaterThan(
* value = 0,
* message = "La valeur doit être positive"
* )
*/
private $numOrdre;
/**
* #ORM\Column(type="string",length=50)
* #Assert\Length(
* min = "5",
* minMessage = "Le libellé doit faire au moins {{ limit }} caractères"
* )
*/
private $libelle;
/**
* #ORM\Column(type="datetime")
*/
private $dateCreation;
/**
* #ORM\Column(type="datetime")
*/
private $dateModif;
/**
* #ORM\Column(type="boolean")
* #Assert\NotBlank( message=" ")
*/
private $etat;
//getters and setters
invite.php
/**
* #ORM\Entity
* #ORM\Entity(repositoryClass="InviteRepository")
*
*/
class Invite {
/**
* #var boolean
*
* #ORM\ManyToMany(targetEntity="Acme\ProtoBundle\Entity\Manifestation", cascade={"persist"})
* #Assert\NotBlank(message=" ")
*/
private $manifestations;
Can you show your manifestation.php file (your entity)?
Multiple doesn't have the behaviour you are looking for : Multiple is used to allow a user to check multiple checkboxes (true) or only one (false) of a given form (symfony doc : multiple).
In your case a common solution is to use javascript on a parent field that would disable/enable the children fields. Make sure to add server-side validation on these fields if you go for this.
This is the solution working for me now with 'multiple' => false':
i've added this function in the other entity in relation with Manifestation, to consider manifestations as an array
public function setManifestations($manifestations){
if(!is_array($manifestations)){
$manifestations = array($manifestations);
}
$this->manifestations = $manifestations;
}