Symfony4 form - preselection in Collection of Entities - php

I have two entities with a Many->Many relation , appli and service
I want to create a form that would allow me to edit, add, or remove relations .
Those entities are linked as follow ..
Appli.php :
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Service", mappedBy="applis")
*/
private $services;
public function getService() {
return $this->services;
}
Service.php :
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Appli", inversedBy="services")
* #ORM\JoinTable(name="service_to_app",
* joinColumns={#ORM\JoinColumn(name="services", referencedColumnName="id_service")},
* inverseJoinColumns={#ORM\JoinColumn(name="applis", referencedColumnName="id_apps")}
* )
*/
private $applis;
Notice that $services and $applis are arrayCollections .
Here is my formType :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$item = $builder->getData();
$builder /* ... */
->add('serviceCol', CollectionType::class, array(
'entry_type' => ServiceType::class,
'entry_options' => array('label' => false,),
'property_path' => 'service',
'allow_add' => true, 'allow_delete' => true,
));
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => Appli::class,
));
}
and the serviceType :
class ServiceType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('service', EntityType::class, array(
'class' => Service::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('s')
->orderBy('s.id_service', 'ASC');},
'choice_label' => 'nom', 'property_path' => 'nom', 'label' => false));
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => Service::class,
));
}
}
My issue so far is to preselect relations, I do have the correct amount of fields , but it's only default value and does not show the existing relations .
I could get to it without using the CollectionType field , but then , I would not be able to add / remove fields .
I cant fill the 'data' option since $item->getService() gives an arrayCollection , and I could not find a way to iterate through it during the Collection definition .
Is there any simple enough solution to this ?

So the fix was quite simple , the solution was on Symfony form - Access Entity inside child entry Type in a CollectionType
It also made me read How to Dynamically Modify Forms Using Form Events with a renewed attention !
here s is my ServiceType formBuilder :
$builder->addEventListener(FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($builder) {
$form = $event->getForm();
$service = $event->getData();
if($service instanceof Service) {
$form->add('service', EntityType::class, array(
'class' => Service::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('s')
->orderBy('s.id_service', 'ASC');},
'choice_label' => 'nom',
'property_path' => 'nom',
'label' => false,
'data' => $service ));
}
})
;

Related

Add a child field for each Product Field - Symfony Forms

I'm trying to build a Order functionality with Symfony 4.4 Forms
I have this, so far
class OrderProductForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('customer', EntityType::class, [
'class' => 'App\Entity\Customers',
'choice_label' => 'name',
'label' =>'order_form.name'
])
->add('products', CollectionType::class, [
'entry_type' => ProductQuantityType::class,
'label' => '',
])
->getForm();
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'App\Entity\Order',
));
}
}
and
class ProductQuantityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('products', EntityType::class, [
'class' => 'App\Entity\Products',
'expanded' => true,
'multiple' => true,
'choice_label' => 'name',
'label' =>''
])
->add('qty', IntegerType::class, [
'label' =>'order_form.qty'
])
->getForm();
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => null,
));
}
}
That won't render Product Quantity because it has no data. Symfony manual does not say how to do it ... CollectionType
What I really want is to add a Quantity Field for each product and at the same time to add as many products as I want.
This is the doctrine structure
Only the relevant code is included
class Order
{
/**
* #ORM\ManyToOne(targetEntity=Customers::class, inversedBy="orders")
* #ORM\JoinColumn(nullable=false)
*/
private $customer;
}
class Customers
{
/**
* #ORM\OneToMany(targetEntity=Order::class, mappedBy="customer", orphanRemoval=true)
*/
private $orders;
}
Any thoughts on how I can move forward with this ?
Thanks !

Symfony form validation error with customType ManyToMany

I'm running Symfony 3.3 with a form type configuration like this.
class UserFinancialTransactionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('billingAddresses', AddressType::class, array(
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => User::class,
'translation_domain' => 'forms',
));
}
}
class AddressType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$countryChoices = $options['country_choices'];
$builder
->add('firstName', TextType::class, array(
'label' => 'address.first_name',
'required' => false,
))
->add('lastName', TextType::class, array(
'label' => 'address.last_name',
'required' => false,
))
->add('line1', TextType::class, array(
'label' => 'address.line1',
))
->add('line2', TextType::class, array(
'label' => 'address.line2'
))
->add('city', TextType::class, array(
'label' => 'address.city'
))
->add('postalCode', TextType::class, array(
'label' => 'address.postal_code'
))
->add('country', EntityType::class, array(
'label' => 'address.country',
'class' => 'AppBundle:Country',
'choices' => $countryChoices,
'choice_label' => 'shortDe',
'query_builder' => function (EntityRepository $entityRepository) {
return $entityRepository->createQueryBuilder('c')
->orderBy('c.shortDe', 'ASC');
},
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Address::class,
'translation_domain' => 'forms',
'country_choices' => null,
));
}
}
BillingAddresses is a child of user (many-to-many-relation). In my form I want to have only one BillingAddress and want to have it without javascript, this is the reason why I did not use CollectionType.
To get this working I added a setter Method to Entity\User for "billingAddresses":
/**
* Set billingAddresses
*
* #param \AppBundle\Entity\Address $billingAddress
*
* #return User
*
*/
public function setBillingAddresses(\AppBundle\Entity\Address $billingAddress)
{
if($this->billingAddresses !== NULL and $this->billingAddresses->contains($billingAddress)){
return false;
}
$this->addBillingAddress($billingAddress);
return $this;
}
In twig I render the fields like this:
{{ form_row(form.payment.user.billingAddresses) }}
When I submit the form, I got errors for all address form fields (e. g. for user.billingAddresses[0].line1) in symfony profiler and the also in {{ form_errors(form) }}, but my form fields of billingAddress are not labeled as having an error (normally error message of each field is returned directly by form field and not in {{ form_errors(form) }} and in twig form field template form.vars.valid is not set to "false").
So how could I got correct form handling with many-to-many property with my custom form type (AddressType)?
Thank you and best!

Using entity as parameter for EntityType input filter in symfony

What I want to do is use the Entity passed to the createForm method in my controller to filter an EntityType field.
Entities are related:
Employee('1')----('1')User('M')-----('1')TicketQueue('M')----('1')Ticket
user('M')----('1')ticket
what I want to do is use the ticket relation to ticketqueue, in order to be able to filter the user field to only users that have an employee profile and are assigned to the queue which the ticket is assigned to.
so far this is where I am:
I'm sucessfully filling some of my conditions which are:
-only users with employee profile
-only users with an assigned queue
but I haven't gotten to how to filter out with a where statement where TicketQueue = "whatever the ticket is assigned to"
now this form will only be used while editing the ticket status and assignee(user).
Form class:
class TicketUpdateType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('assignedto', EntityType::class, array('multiple' => false,
'class' => 'AuthBundle\Entity\User', 'placeholder' => 'Select Personel',
'query_builder' => function (UserRepository $er) {
return $er->createQueryBuilder('u')
->select('u')
->join('u.employee','e')
->join('u.ticketQueues','tq')
->orderBy('u.username', 'ASC');
},
'label' => "Assigned To:",
'choice_label' => function
($q) {
return $q->getEmployee()->getFirstName()." ".$q->getEmployee()->getLastName();
}, 'attr' => array('class' => 'form-control')))
->add('ticketstatus', EntityType::class, array('multiple' => false,
'class' => 'TicketBundle\Entity\TicketStatus', 'placeholder' => 'Select Status','label' => "Ticket Status", 'choice_label' => function
($q) {
return $q->getName();
}, 'attr' => array('class' => 'form-control')));
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => Ticket::class,
));
}
}
EDIT:
Form:
class TicketUpdateType extends AbstractType{
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->addEventSubscriber(new AddQueueFieldSubscriber())
->add('ticketstatus', EntityType::class, array('multiple' => false,
'class' => 'TicketBundle\Entity\TicketStatus', 'placeholder' => 'Select Status','label' => "Ticket Status", 'choice_label' => function
($q) {
return $q->getName();
}, 'attr' => array('class' => 'form-control')));
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => Ticket::class,
));
}
}
Event subscriber:
class AddQueueFieldSubscriber implements EventSubscriberInterface{
public static function getSubscribedEvents()
{
// Tells the dispatcher that you want to listen on the form.pre_set_data
// event and that the preSetData method should be called.
return array(FormEvents::PRE_SET_DATA => 'preSetData');
}
public function preSetData(FormEvent $event)
{
$ticket = $event->getData();
$form = $event->getForm();
$queue = $event->getData()->getTicketQueue()->getName();
if ($ticket) {
$form->add('assignedto', EntityType::class, array('multiple' => false,
'class' => 'AuthBundle\Entity\User', 'placeholder' => 'Select Personel',
'query_builder' => function (UserRepository $er) use ($queue){
return $er->createQueryBuilder('u')
->select('u')
->join('u.employee','e')
->join('u.ticketQueues','tq')
->where('tq.name = :queue')
->orderBy('u.username', 'ASC')
->setParameter('queue', $queue);
},
'label' => "Assigned To:",
'choice_label' => function
($q) {
return $q->getEmployee()->getFirstName()." ".$q->getEmployee()->getLastName();
}, 'attr' => array('class' => 'form-control')));
}
}
}
"What I want to do is use the Entity passed to the createForm method in my controller to filter an EntityType field."
You need to use a PRE_SET_DATA function in your form :
symfony doc
This function allow you to prepare your form.
exemple :
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) {
$form = $event->getForm();
$entity = $event->getData();
if (!$entity->getEnabled()) {
$form->remove('validity');
}
}
);
Don't forget to init your form with the entity in your controller :
$form = $this->createForm(TicketUpdateType::class, $yourEntity);
Good luck !

Symfony2: Form Collection of entities with one-to-one relationship

I have 2 entities:
TransactionDefaultParametersEntity
Parameter: widgetType
Parameter: defaultParameters (Has one to one relationship with DefaultParametersEntity)
DefaultParametersEntity
Parameter: checkIn
Parameter: checkOut
Parameter: currency
Parameter: adults
The controller provides a collection of TransactionDefaultParametersEntities, and for each I want a form displayed with all DefaultParameterEntity parameters as well as the widgetType parameter provided by the TransactionDefaultParametersEntity.
Currently I have a form type for each entity:
TransactionDefaultParametersFormType
class DefaultSettingsFormType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('defaultParameters', 'collection', ['type' => default_parameters', 'allow_add' => true]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\TransactionDefaultParametersEntity',
'create' => false
));
}
public function getName()
{
return 'transaction_default_parameters';
}
}
DefaultParametersFormType
class DefaultParametersFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('widgetType', 'choice', [
'choices' => [
'Default',
'Booking Engine',
'Informative'
],
'label' => 'Widget Type'
])
->add('checkIn', 'text', ['label' => 'Check In'])
->add('checkOut', 'text', ['label' => 'Check Out'])
->add('currency', 'text', ['label' => 'Currency'])
->add('adults', 'text', ['label' => 'adults']);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\DefaultParametersEntity',
));
}
public function getName()
{
return 'default_parameters';
}
}
And lastly in the controller:
// $transactionDefaultParametersCollection is a collection of TransactionDefaultParameterEntities
$form = $this->createFormBuilder($transactionDefaultParametersCollection)
->add('transactionDefaultParameters', 'transaction_default_settings')
->add('save', 'submit', array('label' => 'Save'))
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
// do something
}
return $this->render(
'settings/editDefaults.html.twig',
[
'form' => $form->createView()
]
);
If this wasn't clear please let me know :). Also if I could be pointed to an article that would be very helpful. I looked at how to embed a collection of forms but this was a different case than the example provided.

Symfony form type display ManyToOne entity information

I'm creating a form type to update options for accounts the user have.
I have 3 entities User, UserHasAccount and Account.
Here are the relations between them:
User (OTM) <-> (MTO) UserHasAccount (MTO) <-> (OTM) Account
In UserHasAccount I have 2 options options1 and options2 (boolean)
An account can be link to multiple user. I have a page where I want to be able to change the options foreach Users linked to this Account (/account/{id}/manage-user)
Here is the first form type to map the fields from User
class AccounUserOptionFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user', 'entity', array(
'class' => 'AcmeUserBundle:User',
'property' => 'fullname',
))
->add('option1', 'checkbox', array(
'label' => 'account.form.option.one',
'required' => false,
'error_bubbling' => true,
))
->add('option2', 'checkbox', array(
'label' => 'account.form.option.two',
'required' => false,
'error_bubbling' => true,
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\UserBundle\Entity\UserHasAccount',
'translation_domain' => 'AcmeAccountBundle'
));
}
public function getName()
{
return 'acme_update_account_option_type';
}
}
And the AccountUserOption form type:
class AccountUserOptionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// Display the collection of users
->add('accountUser', 'collection', array(
'type' => new AccountUserOptionFormType($options),
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\AccountBundle\Entity\Account',
'cascade_validation' => true,
'validation_groups' => array('AcmeUpdateOption'),
));
}
public function getName()
{
return 'acme_account_user_option_type';
}
}
The problem I have is with:
->add('user', 'entity', array(
'class' => 'AcmeUserBundle:User',
'property' => 'fullname',
))
I just want to display (label or similar) the name of the user. Using entity is displaying a dropdown :/ Is there any core field type I could use or do I have to create a custom one? I would have think that this could be core.
Cheers,
Maxime

Categories