I need language on change of A2LiX Translation tabs. If I click on "FR" then need to get Fr and on click of "NL" need Nl language in form builder ('Need Language Here').
For querybuilder in one field.
Is it possible to get the language in form type when tab change ?
My Form class MixCampaignTranslationType :
<?php
namespace BackEndBundle\Form;
use BackEndBundle\Validator\Constraints\CampaignSlugDuplicate;
use BackEndBundle\Validator\Constraints\MaxMixCampaign;
use Doctrine\ORM\EntityRepository;
use SurveyBundle\Entity\Survey;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Type;
class MixCampaignTranslationType extends AbstractType
{
public $campaignId = null;
public function buildForm(FormBuilderInterface $builder, array $options)
{
if (null !== $options['campaignId']) {
$this->campaignId = $options['campaignId'];
}
$builder
->add('surveys', EntityType::class, [
'class' => Survey::class,
'required' => false,
'multiple' => true,
'mapped' => true,
'attr' => [
'class' => 'select2',
],
'constraints' => new MaxMixCampaign(),
])
->add('startDate', DateTimeType::class, [
'widget' => 'single_text',
'format' => 'yyyy-MM-dd',
'constraints' => new NotBlank(),
'attr' => [
'class' => 'datepicker',
],
])
->add('endDate', DateTimeType::class, [
'widget' => 'single_text',
'format' => 'yyyy-MM-dd',
'constraints' => new NotBlank(),
'attr' => [
'class' => 'datepicker',
],
])
->add('slug', TextType::class, [
'constraints' => [new NotBlank(), new Type('string'), new CampaignSlugDuplicate($this->campaignId, $options['locale'], 'mix')],
])
->add('isClosed');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'SurveyBundle\Entity\MixCampaignTranslation',
'locale' => 'fr',
'campaignId' => null
]);
}
public function getName()
{
return 'back_end_bundle_mix_campaign_translation_type';
}
}
You can create your custom TranslationFormType and then pass the language of loop as current language, Please check as below.
/**
* #param \Symfony\Component\Form\FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventSubscriber($this->translationsListener);
$formsOptions = $this->translationForm->getFormsOptions($options);
foreach ($options['locales'] as $locale) {
if (isset($formsOptions[$locale])) {
$builder->add($locale, $options['form_type'], [
'currentLanguage' => $locale,
'campaignId' => $formsOptions[$locale]['campaignId']
]
);
}
}
}
and you can use currentLanguage to filter data in query_builder in your form_type.
Hope it will help you.
Related
I'm using Symfony 4.4 and I have form like this:
<?php
declare(strict_types=1);
namespace App;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class MainForm extends AbstractType
{
public const GROUP_CREATE = 'create';
public const GROUP_UPDATE = 'update';
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('zone', TargetingZoneForm::class, [
'targeting_validation_groups' => [
MainForm::GROUP_CREATE,
MainForm::GROUP_UPDATE,
],
])
;
}
}
class TargetingZoneForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('is_excluded', CheckboxType::class, [
'constraints' => [
new \Symfony\Component\Validator\Constraints\Type([
'type' => 'bool',
'groups' => $options['targeting_validation_groups'],
]),
],
])
->add('list', CollectionType::class, [
'entry_type' => IntegerType::class,
'entry_options' => [
'constraints' => [
new \Symfony\Component\Validator\Constraints\Range([
'min' => 1,
'max' => 2147483647,
'groups' => $options['targeting_validation_groups'],
]),
],
],
'allow_add' => true,
'allow_delete' => true,
'error_bubbling' => false,
'constraints' => [
new \Symfony\Component\Validator\Constraints\Count([
'max' => \App\Model\Zone::ZONE_LIMITATIONS_MAX,
'maxMessage' => 'You can use up to {{ limit }} zones',
'groups' => $options['targeting_validation_groups'],
]),
new \App\Constraints\Zone([
'groups' => $options['targeting_validation_groups'],
]),
],
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
parent::configureOptions($resolver);
$resolver->setRequired(['targeting_validation_groups']);
$resolver->setAllowedTypes('targeting_validation_groups', 'array');
}
}
My payload is like this:
{
"zone": {
"list": [
"1",
"26606653111701",
"3"
],
"is_excluded": false
}
}
I want to validate all of the elements of the collection for Range constraint, and only if it is valid - fire my custom \App\Constraints\Zone constraint after if (because inside I will have all valid IDs and send a single DB query). I failed to do so with GroupSequence.
I can't use Sequentially because I'm using Symfony 4.4.
It seems like you need the All Constraint. You can apply it on arrays with same values, like your array of integers in the list property of your form. You can then pass any constraints you need, and they will be applied to each element of the array.
Judging by the documentation, it should be available in Symfony 4.4.
I'm trying to make a form with Symfony to be able to adding new row by clicking button. I can't use SubmitType class because after submit symfony can't add new row. ButtonType class doesn't have a basic function isClicked(). Here is my Form:
<?php
namespace App\Form;
use App\Entity\Team;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
final class TeamFormType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('teamName', TextType::class, [
'label' => false,
'attr' => array(
'placeholder' => 'Wpisz Nazwe Drużyny'
)
])
->add('teamCity', TextType::class, [
'label' => false,
'attr' => array(
'placeholder' => 'Wpisz Nazwe Miasta'
)
])
->add('teamCoach', TextType::class, [
'label' => false,
'attr' => array(
'placeholder' => 'Wpisz Imie i Nazwisko Trenera'
)
]);
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event){
$form = $event->getForm();
for ($i = 1; $i <= 6; $i++) {
$name = 'zawodnik' . $i .'';
$form->add($name, TextType::class, [
'mapped' => false,
'label' => false,
'attr' => array(
'placeholder' => 'Imie i Nazwisko Zawodnika'
)
]);
}
$form->add('agreeTerms', CheckBoxType::class, [
'mapped' => false,
'required' => true,
'label' => 'Zaakceptuj regulamin'
])
->add('Submit', SubmitType::class, [
'label' => 'Wyślij Zgłoszenie'
])
->add('addRow', ButtonType::class, [
'label' => 'Dodaj zawodnika'
]);
});
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Team::class,
'csrf_protection' => true,
'csrf_field_name'=> '_token',
'csrf_token_id' => 'team_item'
]);
}
}
And Controller :
<?php
namespace App\Controller;
use App\Form\TeamFormType;
use Doctrine\DBAL\Types\TextType;
use Doctrine\ORM\EntityManagerInterface;
use http\Env\Request;
use http\Env\Response;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class FormsController extends AbstractController
{
public function createTeamForm(Request $request, EntityManagerInterface $entityManager) :Response
{
$teamEntity = new Team();
$form = $this->createForm(TeamFormType::class);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
if($form->get('addRow')->isClicked()) {
$form->add('something', TextType::class, [
'label' => false,
'attr' => array(
'placeholder' => 'Imie i Nazwisko zawodnika.'
)
]);
}
$entityManager->persist($teamEntity);
$entityManager->flush();
$this->addFlash('success', 'Pomyślnie Utworzono Drużyne');
return $this->redirectToRoute('index');
}
}
}
Anyone have a idea to create button like that ?
I suggest you using Javascript and css, create your row and just hide it, then, onClick, let em appear with a JS function that modify the css sheet
I've Symfony 4.4 and the Symfony/translation component.
I'm trying to extract the translations, and everything works fine, except that the form labels are not extracted.
This is a example of one of the Form Types.
namespace App\Form;
use App\Entity\ClassifiedAd;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ClassifiedAdType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('description')
->add('price', MoneyType::class, [
'required' => false,
'currency' => "EUR"
])
->add('type', ChoiceType::class, [
'required' => true,
'expanded' => true,
'multiple' => false,
'label' => "classifiedad.form.type",
'choices' => [
'classifiedad.form.type_'.ClassifiedAd::SELLING => ClassifiedAd::SELLING,
'classifiedad.form.type_'.ClassifiedAd::SEARCHING => ClassifiedAd::SEARCHING,
'classifiedad.form.type_'.ClassifiedAd::SWAPPING => ClassifiedAd::SWAPPING,
]
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => ClassifiedAd::class,
]);
}
}
I would like to use my UserType form class to register a user and to edit the user profile. As I want to the admins to register a user while setting up their roles, I don't want the user to modify that options. So I would like to use 2 different forms but for the same type User.
Can I create 2 different buildForm() functions in one FormType class ?
Or do I need to create another type ?
Here is my UserType class (main purpose was to register a user) :
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\BirthdayType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstname', TextType::class)
->add('lastname', TextType::class)
->add('birthdate', BirthdayType::class, array(
'placeholder' => '-'))
->add('email', EmailType::class)
->add('username', TextType::class)
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'first_options' => array('label' => 'password'),
'second_options' => array('label' => 'repeat-password'),
))
->add('roles', ChoiceType::class, [
'multiple' => true,
'expanded' => true, // render check-boxes
'choices' => [
'Administrateur' => 'ROLE_ADMIN',
'Direction' => 'ROLE_MANAGER',
'Comptabilite' => 'ROLE_ACCOUNTING',
'Commercial' => 'ROLE_MARKETING',
'Docteur' => 'ROLE_DOCTOR',
'Client' => 'ROLE_CLIENT',
],
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => User::class,
));
}
}
You can use a variable to dynamically add the roles to the formType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->is_admin = $options['is_admin'];
// [...]
if ($this->is_admin)
$builder
->add('roles', ChoiceType::class, [
'multiple' => true,
'expanded' => true,
'choices' => [
'Administrateur' => 'ROLE_ADMIN',
'Direction' => 'ROLE_MANAGER',
'Comptabilite' => 'ROLE_ACCOUNTING',
'Commercial' => 'ROLE_MARKETING',
'Docteur' => 'ROLE_DOCTOR',
'Client' => 'ROLE_CLIENT',
],
])
// [...]
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
// [...]
'is_admin' => false,
]);
}
Then pass the variable like
$form = $this->createForm(UserType::class, $user, array(
'is_admin' => $this->isGranted('ROLE_ADMIN'),
);
you can easily handle this using array $optionsin controller set choices of roles and add them to $optionand in userType do something like this:
->add('roles', ChoiceType::class, [
'multiple' => true,
'expanded' => true, // render check-boxes
'choices' => $options['choices'],
])
Quick explanation of the subject:
A "Project" can have several "Activities"
I had to create simultaneously as many "Activities" as needed (You click on a button "Add an activity" and you got a new line which means a new activity). I managed to do that by doing Collections on my fields.
Here's the code of the formBuilder :
namespace CoreBundle\Form\Type;
use CoreBundle\Entity\Activities;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class CreateNewActivitiesType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'aeAmount',
CollectionType::class,
array(
'entry_type' => TextType::class,
'allow_add' => true,
'allow_delete' => true,
'label' => 'Montant de l\'AE',
'label_attr' => array(
'class' => 'aeAmount visuallyhidden'
),
'entry_options' => array(
'attr' => array(
'placeholder' => '80 000 €'
),
),
'required' => true
)
)
->add(
'amountSpent',
CollectionType::class,
array(
'entry_type' => TextType::class,
'allow_add' => true,
'allow_delete' => true,
'label' => 'RDP : ',
'label_attr' => array(
'class' => 'amountSpent visuallyhidden'
),
'entry_options' => array(
'attr' => array(
'placeholder' => '35 000 €'
)
),
'required' => true,
)
)
->add(
'afName',
CollectionType::class,
array(
'entry_type' => TextType::class,
'allow_add' => true,
'allow_delete' => true,
'required' => true,
'label' => 'AF : ',
'label_attr' => array(
'class' => 'afName visuallyhidden',
),
'entry_options' => array(
'attr' => array(
'placeholder' => 'AERT-496'
)
)
)
)
->add(
'year',
CollectionType::class,
array(
'entry_type' => TextType::class,
'allow_delete' => true,
'allow_add' => true,
'required' => true,
'entry_options' => array(
'attr' => array(
'readonly' => true
)
),
'label' => 'Année : ',
'label_attr' => array(
'class' => 'year visuallyhidden'
)
)
)
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'CoreBundle\Entity\Activities'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'corebundle_collection_activities';
}
}
(I know i can do this much shorter, but refactorisation will be later.)
So, this is currently working, with his associated controller to add my activities, here it is :
/**
* #param integer $projectId
* #param string $projectType
* #param integer $rdId
*
* #return \Symfony\Component\HttpFoundation\Response
*/
public function createNewActivityAction($projectId, $projectType, $rdId)
{
$activity = new Activities();
$request = $this->get('request_stack')->getCurrentRequest();
$form = $this->createForm(CreateNewActivitiesType::class, $activity);
$form->handleRequest($request);
$em = $this->getDoctrine()->getManager();
if ($form->isSubmitted() && $form->isValid()) {
$rdAffiliated = $em->getRepository('CoreBundle:DecisionStatement')->findOneBy(['id' => $rdId]);
$formData = $form->getData();
$yearList = $formData->getYear();
$aeAmountList = $formData->getAeAmount();
$afNameList = $formData->getAfName();
$amountSpentList = $formData->getAmountSpent();
for ($i = 0; $i < count($yearList); $i++) {
$yearDatetime = new DateTime($yearList[$i] . '-01-01 00:00:00');
$existingActivity = $em->getRepository('CoreBundle:Activities')->getExistingActivityWithoutIdentifier(
$yearDatetime,
$rdAffiliated
);
if ($existingActivity) {
/**
* #var Activities $currentActivity
*/
$currentActivity = $existingActivity[0];
$currentActivity->setAeAmount(
$currentActivity->getAeAmount() + $aeAmountList[$i]
);
$currentActivity->setAmountSpent(
$currentActivity->getAmountSpent() + $amountSpentList[$i]
);
$em->persist($currentActivity);
} else {
$newActivity = new Activities();
$newActivity->setYear($yearDatetime)
->setAeAmount($aeAmountList[$i])
->setAfName($afNameList[$i])
->setAmountSpent($amountSpentList[$i])
->setAfReception(false)
->setDecisionStatement($rdAffiliated)
->setPeopleByMonth(0);
$em->persist($newActivity);
}
}
$em->flush();
return $this->redirectToRoute('rd_show_activities', array(
'rdId' => $rdId,
'projectType' => $projectType,
'projectId' => $projectId
));
}
return $this->render('#Core/html/13-edit-activite.html.twig', array(
'page' => 'activities_creation',
'createActivitiesForm' => $form->createView(),
'projectParentId' => $projectId,
'projectParentType' => $projectType,
'rdId' => $rdId
));
}
Here is also a screenshot from the var_dump when activities form is submitted :
But where's my problem begins, it is when I want to edit because my form is based on the entity "Activities". But I want to edit all the existing "Activities" for a given project, I'll have an array containing my "Activities" objects (found by the findBy method), so I can't pass my array into my form, which results in an error.
How to transform this array of many "Activities" objects into only one "Activities" object?
I don't understand why you have only one object "Activities" that contains several collection.
You should create an object activities that contains several activities.
Each activity has only one attribute "year", "amountSpent"...
When you edit your object "ActivitieS" with the appropriate form you will be able to edit each activity linked with this object.
Embed a collection of forms
To correctly manage this kind of problem you need to build a Collection of forms in Symfony.
A "Project" can have several "Activities"
I had to create simultaneously as many "Activities" as needed [...]
At first you need to build a Project entity that will contain all the Activities as a Doctrine ArrayCollection as you can see below:
src/AppBundle/Entity/Project.php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
class Project
{
protected $activities;
public function __construct()
{
$this->activities = new ArrayCollection();
}
public function getTags()
{
return $this->tags;
}
}
src/AppBundle/Form/Type/ProjectType.php
namespace AppBundle\Form\Type;
use AppBundle\Entity\Project;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
class ProjectType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('tags', CollectionType::class, array(
'entry_type' => ActivitiesType::class,
'allow_add' => true,
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Project::class,
));
}
}
Then you need to enable the user to dynamically add an Activity to a Project you need to make advantage of the prototype variable, using Twig and JavaScript.
I've taken into consideration all the comments that you made and decided to review the whole thing.
DecisionStatement.php :
Added in the __construct() method a new ArrayCollection for the Activities affiliated for a given DecisionStatement.
The method to return the Activities affiliated was already here.
documents = new ArrayCollection();
$this->activitiesAffiliated = new ArrayCollection();
}
/**
* Get activitiesAffiliated
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getActivitiesAffiliated()
{
return $this->activitiesAffiliated;
}
I've created a new FormType (DecisionStatementType.php), to have my Collection of forms :
use CoreBundle\Entity\DecisionStatement;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class DecisionStatementType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'activitiesAffiliated',
CollectionType::class,
array(
'entry_type' => NewActivityType::class,
'allow_add' => true,
'allow_delete' => true
)
)
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => DecisionStatement::class
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'corebundle_collection_decisionstatement';
}
}
And here is the "subForm", newActivityType.php :
use CoreBundle\Entity\Activities;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class NewActivityType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'aeAmount',
TextType::class,
array(
'label' => 'Montant de l\'AE',
'label_attr' => array(
'class' => 'aeAmount visuallyhidden'
),
'attr' => array(
'placeholder' => '80 000 €'
),
'required' => true
)
)
->add(
'amountSpent',
TextType::class,
array(
'label' => 'RDP : ',
'label_attr' => array(
'class' => 'amountSpent visuallyhidden'
),
'attr' => array(
'placeholder' => '35 000 €'
),
'required' => true,
)
)
->add(
'afName',
TextType::class,
array(
'required' => true,
'label' => 'AF : ',
'label_attr' => array(
'class' => 'afName visuallyhidden',
),
'attr' => array(
'placeholder' => 'AERT-496'
)
)
)
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'CoreBundle\Entity\Activities'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'corebundle_collection_activities';
}
}
In my view's DOM, i have now this :
FormCollection Data-Prototype in DOM
So, is this a bit cleaner ?