Build and render a form with inheritance classes in Symfony 6 - php

I have three classes (User, Professionel and Simple, where Professionel and Simple are the children of User). I use inheritance mapping like this:
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\InheritanceType("JOINED")]
#[ORM\DiscriminatorColumn(name:"compte", type: "string")]
#[ORM\DiscriminatorMap(["professionnel"=>Professionel::class, "simple"=> Simple::class])]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 180, unique: true)]
#[Assert\NotBlank()]
#[Assert\Length(min:2, max:80)]
private $email;
#[ORM\Column(type: 'string', length: 50)]
private $typeCompte;
}
class Professionel extends User
{
private $nameProfessionel;
//getter setter
}
class Simple extends User
{
private $name;
//getter setter
}
I have generated formtype for each entity and extended SimpleType and ProfessionelType like this:
class SimpleType extends UserFormType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name')
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Simple::class,
]);
}
}
class ProfessionelType extends UserFormType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('nameProfessionel');
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Professionel::class,
]);
}
}
What I want to do is whenever the user clicks on a radio button (Simple or Professionel), to only show the form that fits.
I have tried to build the form in the controller:
$types = [
'parentForm' => UserFormType::class,
'partForm' => SimpleType::class,
'proForm' => ProfessionelType::class,
];
$forms = [];
foreach ($types as $type1) {
$forms[] = $this->createForm($type1);
}
but I have this error :
Could not load type "App\Form\ChoiceType": class does not exist.

Related

Symfony3 FOSuserbundle register form one to one entity

i neeed in my symfony base aplication 4 types of user,one of them is super user and can be just typical fosuserbundle user, but need to admin can add 3 different type of user. So i do a new Client class for one type of user which has 1:1 entity with my base user class
class Client{
my client class fields
...
/**
* #ORM\OneToOne(targetEntity="AppBundle\Entity\User")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
...
}
And my User class
class User extends BaseUser
{......}
The Question is how to do a register form for this ? Please help me
Now i have this UserType form
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => User::class,
));
}
public function getParent()
{
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
}
public function getBlockPrefix()
{
return 'app_user_registration';
}
}
And my clientForm class
class ClientForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('address', TextareaType::class)
->add('phone', TextType::class)
->add('deliveryAddress', TextareaType::class)
->add('user', UserType::class);
}
public function getParent()
{
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
}
public function getBlockPrefix()
{
return 'app_user_registration';
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Client::class,
]);
}
}
But this give me a error
Neither the property "email" nor one of the methods "getEmail()", "email()", "isEmail()", "hasEmail()", "__get()" exist and have public access in class "AppBundle\Entity\Client".
Oki, becuse earlier i've try a lot of different way to do this, i just delete getParent and getBlockPrefix from my ClientForm class and all look fine :)

Symfony - form - many to many relation - collection type

Here is the situation:
I have an entity Property
class Property
{
/**
* #ORM\Id
* #ORM\Column(type="string")
* #ORM\GeneratedValue(strategy="UUID")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="PropertyEquipment", inversedBy="properties")
*/
protected $propertyEquipments;
public function __construct()
{
$this->propertyEquipments = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function addPropertyEquipment(\AppBundle\Entity\PropertyEquipment $propertyEquipment)
{
$this->propertyEquipments[] = $propertyEquipment;
return $this;
}
public function removePropertyEquipment(\AppBundle\Entity\PropertyEquipment $propertyEquipment)
{
$this->propertyEquipments->removeElement($propertyEquipment);
}
public function getPropertyEquipments()
{
return $this->propertyEquipments;
}
}
And the entity PropertyEquipment:
class PropertyEquipment
{
/**
* #ORM\Id
* #ORM\Column(type="string")
* #ORM\GeneratedValue(strategy="UUID")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="Property", mappedBy="propertyEquipments")
*/
protected $properties;
/**
* #ORM\Column(type="string", length=100)
* #Gedmo\Translatable
*/
protected $equipmentName;
public function __construct()
{
$this->properties = new ArrayCollection();
}
/**
* Get id
*
* #return string
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getEquipmentName()
{
return $this->equipmentName;
}
/**
* #param mixed $equipmentName
*/
public function setEquipmentName($equipmentName)
{
$this->equipmentName = $equipmentName;
}
public function addProperty(Property $property)
{
$this->properties[] = $property;
return $this;
}
public function removeProperty(Property $property)
{
$this->properties->removeElement($property);
}
public function getProperties()
{
return $this->properties;
}
}
The form PropertyCreation
class PropertyCreation extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
//with this I see the values coming from DB in the template
->add("propertyEquipments", PropertyEquipmentCreation::class)
//with this it's empty :/
/*->add("propertyEquipments", CollectionType::class, array(
"entry_type" => PropertyEquipmentCreation::class,
))*/
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Property::class
));
}
}
Here is the form PropertyEquipmentCreation:
class PropertyEquipmentCreation extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('propertyEquipment', EntityType::class, [
'class' => 'AppBundle\Entity\PropertyEquipment',
'choice_label' => 'equipmentName',
'expanded' => true,
'multiple' => true
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => PropertyEquipment::class,
]);
}
}
And the controller
public function createPropertyAction(Request $request)
{
$property = new Property();
$form = $this->createForm(PropertyCreation::class, $property);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($property);
$entityManager->flush();
return $this->redirectToRoute('homepage');
}
return $this->render('form/owner_create_property.html.twig', ["form" => $form->createView()]);
}
My error:
Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "AppBundle\Entity\Property#$propertyEquipments", got "Doctrine\Common\Collections\ArrayCollection" instead.
Must I transform these with something like class PropertyEquipmentTransformer implements DataTransformerInterface?
I think you should use getParent() function in PropertyEquipmentCreation and inherit from EntityType::class then put all your field configs in the configureOptions() function (remove the buildForm function) and it should work.
You are having this problem because it is a compound form in your implementation and no simple form and symfony is unable to resolve which field created inside the subform needs to be used as source for the entity field
First !
Big thanks to Nickolaus !
Here is the solution (PropertyEquipmentCreation):
namespace AppBundle\Form\Type;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PropertyEquipmentCreation extends AbstractType
{
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'class' => 'AppBundle\Entity\PropertyEquipment',
'choice_label' => 'equipmentName',
'expanded' => true,
'multiple' => true,
]);
}
public function getParent()
{
return EntityType::class;
}
}
And for (PropertyCreation)
<?php
namespace AppBundle\Form;
use AppBundle\Form\Type\PropertyEquipmentCreation;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PropertyCreation extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('description', TextareaType::class)
->add('name', TextType::class)
->add("propertyEquipments", PropertyEquipmentCreation::class)
->add('save', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Property::class
));
}
}
Many thanks !
Try using Doctrine ArrayCollection instead of ArrayCollections:
$this->propertyEquipments = new \Doctrine\Common\Collections\ArrayCollection();
This should work!

Symfony custom many-to-many with checkbox

Having Session, Person, Participant, I'm trying to create a Session, and a list with all Persons to check the participants.
The PersonInSession table keeps participants_ids and the session_id.
I'm getting the following Error:
Unable to transform value for property path "person": Expected a Doctrine\Common\Collections\Collection object.
I want to create a Session and directly check the participants in the same form.
PersonEntityType
class PersonEntityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name');
}
}
SessionEntityType
class SessionEntityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('description')
->add('participant', CollectionType::class,
array(
'entry_type' => ParticipantEntityType::class,
'by_reference' => false,
'allow_add' => true,
'allow_delete' => true,
)
);
}
}
ParticipantEntityType
class ParticipantEntityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('person', EntityType::class,
array(
'class' => 'AppBundle:PersonEntity',
'choice_label' => 'name',
'expanded' =>'true',
'multiple' => 'true'
)
);
}
}
PersonEntity
class PersonEntity
{
private $id;
private $name;
/**
* #ORM\OneToMany(targetEntity="ParticipantEntity", mappedBy="person")
*/
private $participant;
public function __construct()
{
$this->participant = new ArrayCollection();
}
}
SessionEntity
class SessionEntity
{
private $id;
private $description;
/**
* #ORM\OneToMany(targetEntity="ParticipantEntity", mappedBy="session")
*/
private $participant;
public function __construct()
{
$this->participant = new ArrayCollection();
}
}
ParticipantEntity
class ParticipantEntity
{
private $id;
/**
* #ORM\ManyToOne(targetEntity="PersonEntity", inversedBy="participant")
*/
private $person;
/**
* #ORM\ManyToOne(targetEntity="SessionEntity", inversedBy="participant")
*/
private $session;
public function __construct()
{
$this->person = new ArrayCollection();
}
}
You should have:
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection
class ParticipantEntity
{
// ...
/**
* #var PersonEntity[]|ArrayCollection
*/
private $person;
public function __construct()
{
$this->person = new ArrayCollection();
}
// ...
Otherwise it cannot work as is.
in SessionEntityType, the participant field (you would better rename it to participants as that would be more meaningful) is a CollectionType field. So in its entry_type (ParticipantEntityType) you need to add a field which maps to a Collection property of the target Entity (ParticipantEntity).
In that entity you have person which has wrongly a ManyToOne relationship with your participant. So It should be defined as:
ParticipantEntity
class ParticipantEntity
{
/**
* #ORM\OneToMany(targetEntity="PersonEntity")
*/
private $persons;
public function __construct()
{
$this->person = new ArrayCollection();
}
}
I strongly recommend you to revise your entities, as they are clearly the source of problem.

Symfony 2.3 FormType $options inheritance

tl;dr:
Is there any way to have the 'context' option auto-cascade through all form types ? It would NOT be leasable to pass it along manually in the form types like this:
$builder->add('organizer', 'organizer_type', array('context' => $options['context']));
Hello everybody,
I have written an extension to ALL Symfony form types:
class ContextExtension extends AbstractTypeExtension
{
protected $context;
public function __construct(SecurityContextInterface $context)
{
$this->context = $context->getToken()->getUser()->getContext();
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults([
'context' => $this->context
]);
$resolver->setAllowedTypes(['context' => 'GOC\Bundle\FrameworkBundle\Model\ContextInterface']);
}
/**
* Returns the name of the type being extended.
*
* #return string The name of the type being extended
*/
public function getExtendedType()
{
return 'form';
}
}
I have validated that it is registered and working (for the most part). I only face one problem. When I set my new option ('context') in a form type like this:
class EventType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(
'eventgroup', 'eventgroup_type', array(
'context' => $context,
)
)
}
public function getName()
{
return 'event';
}
}
I receive the option in the event group form type and can work with it there:
class EventGroupType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->log($options['context']);
$builder
->add('name', 'text')
->add('comment', 'text')
->add('organizer', 'organizer_type')
;
}
public function getName()
{
return 'eventgroup_type';
}
}
However, it appears that the options does not cascade any further and is therefore not usable in the text or organizer type. Is there any way to have the 'context' option auto-cascade through all form types ? It would NOT be leasable to pass it along manually in the form types like this:
$builder->add('organizer', 'organizer_type', array('context' => $options['context']));

symfony2 service array parameters. How to get value based on key

I'm creating new invoice form, which includes select input with some constant values.
I've made it through services:
services.yml
parameters:
stawki_vat:
0: 0%
5: 5%
8: 8%
23: 23%
zw: zw.
services:
acme.form.type.stawki_vat:
class: Acme\FakturyBundle\Form\Type\StawkiVatType
arguments:
- "%stawki_vat%"
tags:
- { name: form.type, alias: stawki_vat }
StawkiVatType.php
class StawkiVatType extends AbstractType
{
private $stawkiVatChoices;
public function __construct(array $stawkiVatChoices) {
$this->stawkiVatChoices = $stawkiVatChoices;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'choices' => $this->stawkiVatChoices,
));
}
public function getParent()
{
return 'choice';
}
public function getName()
{
return 'stawki_vat';
}
}
TowarType.php
class TowarType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nazwa_towar', null, array('label' => 'Nazwa towaru/usługi'))
->add('cena_netto', null, array(
'label' => 'Cena netto'
))
->add('vat', 'stawki_vat', array(
'attr' => array('class' => 'styled'),
'label' => 'Stawka VAT',
))
;
}
In form, everything works perfect.
But now, I want to get a value stored in database (key of stawki_vat) and show a value of stawki_vat array.
How to achieve this in simple way?
You need to pass your entity manager to your custom form type service. Presuming you are using Doctrine:
services.yml
services:
acme.form.type.stawki_vat:
class: Acme\FakturyBundle\Form\Type\StawkiVatType
arguments: ["#doctrine.orm.entity_manager","%stawki_vat%"]
StawkiVatType.php
class StawkiVatType extends AbstractType
{
private $stawkiVatChoices;
private $em;
public function __construct(EntityManager $em, array $stawkiVatChoices) {
$this->em = $em;
$this->stawkiVatChoices = $stawkiVatChoices;
}
// ...

Categories