I'm building sort of an accounting web app and I have, among others, an entity for credit cards and an entity for bank accounts. The app is intended to document my expenses, so the plan is to enter all my movements and link them to the account/credit card they were paid by; thus, the Movement
entity has a ManyToOne
relationship to the Account
and/or the CreditCard
entity.
The EntityType
would allow me to select the appropriate account and credit card by writing:
$form = $this->createFormBuilder($movement)
->add('Account', EntityType::class, array(
'class' => Account::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.user = :user_id')
->setParameter(':user_id', $this->getUser());
},
'label' => 'Account name',
'required' => true
))
->add('Credit_Card', EntityType::class, array(
'class' => CreditCard::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.user = :user_id')
->setParameter(':user_id', $this->getUser());
},
'label' => 'Credit card',
'required' => true
))
->add('Save', SubmitType::class)
->getForm();
This would give me two select form fields in the HTML. This is, of course, not ideal as a movement is not linked to both, an account and a credit card but only to one of both. Thus the required
property should in fact be a requiredIfTheOtherFieldIsEmpty
property. Ideally, I would have a segmented select field, that first displays the group of accounts and then the group of credit cards, thus something as the pseudo-code below:
$form = $this->createFormBuilder($movement)
->add(array('Account', 'Credit_Card'), array(EntityType::class, CreditCard::class), array(
'Account' => array(
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.user = :user_id')
->setParameter(':user_id', $this->getUser());
},
'label' => 'Account name',
),
'Credit_Card' => array(
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.user = :user_id')
->setParameter(':user_id', $this->getUser());
}
),
'required' => true
))
->add('Save', SubmitType::class)
->getForm();
Unfortunately, I cannot find a form type that matches these requirements. What is the correct way to handle this?
I guess that the ChoiceType
is a good starting point as it allows grouping, but I have no idea how I should retrieve the accounts and credit cards.
Any help is highly appreciated. If more information is needed, please ask, I'll be happy to provide as much as you need.
My Answer:
One way to handle this situation is to create a custom form type that combines the functionality of the EntityType and ChoiceType form types. You can create a custom form type that retrieves the accounts and credit cards separately and then combines them into a single select field with groups.
Here's an example of how you can create a custom form type for this scenario:
1. Create a new form type class that extends AbstractType:
php
// src/Form/Type/AccountCreditCardType.php
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use App\Repository\AccountRepository;
use App\Repository\CreditCardRepository;
class AccountCreditCardType extends AbstractType
{
private $accountRepository;
private $creditCardRepository;
public function __construct(AccountRepository $accountRepository, CreditCardRepository $creditCardRepository)
{
$this->accountRepository = $accountRepository;
$this->creditCardRepository = $creditCardRepository;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'choices' => [
'Accounts' => $this->accountRepository->findBy(['user' => $this->getUser()]),
'Credit Cards' => $this->creditCardRepository->findBy(['user' => $this->getUser()])
],
'group_by' => function($value, $key, $index) {
return $index < count($this->accountRepository->findBy(['user' => $this->getUser()])) ? 'Accounts' : 'Credit Cards';
},
'choice_label' => function($value, $key, $index) {
return $value->getName(); // Assuming there is a getName() method on Account and CreditCard entities
},
'required' => true
]);
}
public function getParent()
{
return ChoiceType::class;
}
}
2. Update your form builder to use the custom form type:
php
$form = $this->createFormBuilder($movement)
->add('AccountCreditCard', AccountCreditCardType::class)
->add('Save', SubmitType::class)
->getForm();
This custom form type will retrieve the accounts and credit cards separately from their respective repositories and combine them into a single select field with groups. The `group_by` option is used to group the choices by 'Accounts' and 'Credit Cards', and the `choice_label` option is used to display the name of each account and credit card in the select field.
Make sure to adjust the code according to your entity and repository names and methods. This should give you a segmented select field with the accounts and credit cards grouped together.
Rate this post
3 of 5 based on 6699 votesComments