I have a very simple form (ModuleType.php) calling a collection (ModuleControleType.php).
This code works perfectly (add, modify and delete : no problem), but I want to customize the query for find Description (I just want to add a Where) in ModuleControleType.php.
I can do a query_builder with a field of type " Entity ", but not with a field " Collection " type.
ModuleType.php :
class ModuleType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('point', 'textarea', array(
'label' => 'Point(s) à observer',
'required' => false,
'attr' => array(
'rows' => 5
)
))
->add('controles', 'collection', array(
'label' => false,
'type' => new ModuleControleType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false
))
;
}
ModuleControleType.php :
class ModuleControleType extends AbstractType
{
use Description;
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('description', 'textarea', array(
'label' => 'Description',
'required' => false,
'attr' => array(
'rows' => $rows
)
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AdministrationBundle\Entity\ModuleControle',
'intention' => $this->getName() . '_token'
));
}
/**
* #return string
*/
public function getName()
{
return 'administration_module_controle_form';
}
}
Related
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 ?
I have two entities that are connected to each other by doctrine association many-to-one. I created a form collection but when i try to save something it hits me with an error.
The error that i'm getting:
Expected argument of type "Zenith\SurveyBundle\Entity\SurveyOption", "array" given
This is my first form the one that loads the collection.
class TestType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('option', CollectionType::class, [
'entry_type' => SurveyOptionType::class,
'allow_add' => true,
'allow_delete' => true,
'entry_options' => [
'label' => false,
],
])
->add('submit', SubmitType::class, [
'label' => 'Salveaza',
])
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SurveyManager::class
]);
}
}
This is the form loaded by collection:
class SurveyOptionType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('isEnabled', CheckboxType::class, [
'label' => 'Chestionar Activ',
])
->add('headquarter', EntityType::class, [
'class' => HeadQuarterManager::class,
'multiple' => false,
'expanded' => false,
])
->add('userNumber', IntegerType::class, [
'attr' => [
'min' => '1',
'type' => 'number',
],
'label' => 'Numar Utilizatori',
])
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => SurveyOption::class
));
}
}
My Controller action:
public function newAction($surveyId, Request $request)
{
$surveyOption = new SurveyOption();
$em = $this->getDoctrine()->getManager();
$surveyRepository = $em->getRepository(SurveyManager::class);
$survey = $surveyRepository->findOneBy(['id' => $surveyId]);
$form = $this->createForm(TestType::class, $survey);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
}
return [
'surveyOption' => $surveyOption,
'form' => $form->createView(),
];
}
The question is useless.. because i'm a hurry and overburned .. i didn't notice few mistakes.. the forms where fine .. :-)
I have two pages and would like to use one uploadType. When I edit, show the name in the textfield. In NewsController I give the data.
$ EditForm ['upload'] ['filename'] -> setData ($ upload);
This is working. But when i will show the uploadPage and set the data in the uploadType.
'data' => $ build-> getData ()
so is the news textfield empty.
UploadType:
class UploadType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$data = $builder->getData();
if($this->isFieldEnabled('directory',$options)) {
$builder ->add('directory', null, array(
'label' => 'upload.directory',
'required' => true,
)
);
}
$builder->add('name', FileType::class,array(
'label' => 'upload.choice',
'data_class' => null,
'attr' => array(
'class' => 'hidden'
)
));
if($this->isFieldEnabled('filename',$options)) {
$builder->add('filename', 'text',array(
'label' => 'upload.upload',
'data' => $data, //If set, don't show in the news textfield
'mapped' => false,
));
}
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'Maiskolben\UploadBundle\Entity\Upload',
'translation_domain' => 'Maiskolben',
'disableFields' => [],
)
);
$resolver->setOptional([
'disableFields',
]);
}
private function isFieldEnabled($fieldName, $options)
{
if (isset($options['disableFields']) && !empty($options['disableFields'])) {
if (in_array($fieldName, $options['disableFields'])) {
return false;
}
}
return true;
}
}
NewsType:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class,array(
'label' => 'news.newsTitle',
))
->add('content','textarea',array(
'label' => 'news.content',
))
->add('upload', new UploadType(), array(
'label' => false,
'required' => false,
'disableFields' => array(
'directory',
)
));
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Maiskolben\NewsBundle\Entity\News',
'translation_domain' => 'Maiskolben',
));
}
}
NewsController:
public function editAction(Request $request, News $news)
{
$upload = $news->getUpload();
$editForm = $this->createForm(new NewsType, $news);
$editForm['upload']['filename']->setData($upload);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em = $this->getDoctrine()->getManager();
//....
I tryed with "addEventListener" in both site. All the same result.
I hope anyone can help me. :)
i got weird error that when i submit my form with time type i got null values.
This is my FormType class:
class DailyTimeRecordType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('clockIn', 'time', array(
'attr' => array(
'class' => 'form-control',
),
'input' => 'timestamp',
'widget' => 'single_text',
'html5' => false,
))
->add('clockOut', 'time', array(
'attr' => array(
'class' => 'form-control',
),
'widget' => 'single_text',
'html5' => false,
))
->add('memo', 'textarea', array(
'attr' => array(
'class' => 'form-control',
'placeholder' => 'Memo details...',
),
));
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\DailyTimeRecord',
'validation_groups' => array('DTR'),
));
}
/**
* #return string
*/
public function getName()
{
return 'appbundle_dailytimerecord';
}
}
However when I set clockIn to DateTime i got values on submission, why is that so?
What I inputted are values like this: 10:30 AM, 12:00 AM etc.
Thanks!
I have 2 entities with form types :
User2Type.php is a sub form of Pca2Type.php.
If the form is not filled out properly, the errors from User2Type.php keep bubbling up to the "users" property of Pca2Type.php.
How can I get the errors so they appear next to their appropriate fields instead. I want the username, email, and password errors to appear next to their fields.
I will post the form types.
Pca2Type.php
namespace Homecare\HomecareBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class Pca2Type extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstName')
->add('lastName')
->add('umpi')
->add('agency', 'entity', array( 'class' => 'HomecareHomecareBundle:Agency', 'property' => 'agencyName', 'placeholder' => 'N/A' ) )
->add('create','submit', array(
'label' => 'Create Pca'
) )
;
$builder->add('users', 'collection', array('type' => new User4Type(), 'allow_add' => true, 'by_reference' => false, 'error_bubbling' => false ));
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Homecare\HomecareBundle\Entity\Pca',
'validation_groups' => array( 'adminCreatePca' ),
));
}
/**
* #return string
*/
public function getName()
{
return 'homecare_homecarebundle_pca';
}
}
User2Type.php
namespace Homecare\HomecareBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\ORM\EntityRepository;
use Homecare\HomecareBundle\Entity\Recipient;
use Homecare\HomecareBundle\Entity\Pca;
use Homecare\HomecareBundle\Entity\User;
class User2Type extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstName')
->add('lastName')
->add('username')
->add('email', 'email')
->add('password', 'password')
->add('agency', 'entity', array( 'class' => 'HomecareHomecareBundle:Agency', 'property' => 'agencyName', 'placeholder' => 'N/A' ) )
->add('save','submit')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Homecare\HomecareBundle\Entity\User',
'validation_groups' => array( 'adminCreateAdmin', 'adminCreateAgency' )
));
}
/**
* #return string
*/
public function getName()
{
return 'homecare_homecarebundle_user2';
}
}