Symfony 5.4, customize form to display year only - php

The current buildForm with the year added
->add('year', DateType::class, array(
'label' => 'Select Year',
'widget' => 'choice',
'years' => range(Date('Y'), 2000)
The twig file to customize the month and day attributes, (this used to work)
{{ form_widget(form.year['day'], {'attr':{'style':'display:none'}}) }}
{{ form_widget(form.year['month'], {'attr':{'style':'display:none'}}) }}
It currently displays this image, would like it to display the year only
I've tried changing the twig file, adding a class to the form builder, nothing seems to work so far.

If your field is just an integer, you can just use the ChoiceType directly. Set the 'choices' option to your range of years and configure the 'choice_label' option to use the value.
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
class YearType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('year', ChoiceType::class, [
'label' => 'Select Year',
'choices' => range(Date('Y'), 2000),
'choice_label' => function ($value) {
return $value;
},
])
;
}
}
The above YearType renders as below:
<form name="year" method="post"><div id="year"><div><label for="year_year" class="required">Select Year</label><select id="year_year" name="year[year]"><option value="2023">2023</option><option value="2022">2022</option><option value="2021">2021</option><option value="2020">2020</option><option value="2019">2019</option><option value="2018">2018</option><option value="2017">2017</option><option value="2016">2016</option><option value="2015">2015</option><option value="2014">2014</option><option value="2013">2013</option><option value="2012">2012</option><option value="2011">2011</option><option value="2010">2010</option><option value="2009">2009</option><option value="2008">2008</option><option value="2007">2007</option><option value="2006">2006</option><option value="2005">2005</option><option value="2004">2004</option><option value="2003">2003</option><option value="2002">2002</option><option value="2001">2001</option><option value="2000">2000</option></select></div><input type="hidden" id="year__token" name="year[_token]" value="9f14.o4haGhHOQ5-4CrGAkoeXWo94meRfttZPMpyzdrCmQdw.ydIdciiXJsngeuvs98b6Frkwrrdo05sOW-TwDuXVKu2O3jUjcpw0xsJsiQ"></div></form>
If you need it to be a DateTime Build an array of choices filled with DateTimes
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
class YearType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$range = range(Date('Y'), 2000);
$choices = [];
foreach ($range as $Y) {
$choices[$Y] = new \DateTime("Jan 1st $Y");
}
$builder
->add('year', ChoiceType::class, [
'label' => 'Select Year',
'choices' => $choices,
'choice_value' => function (?\DateTime $year) {
return $year ? $year->format('Y') : '';
},
])
;
}
}

Related

How to show a different value for a field in a Symfony FormType

I have a Symfony FormType like this
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
class MyType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, [
'label' => 'Name',
'required' => true,
'disabled' => false,
]);
}
}
when editing, this text type will be populated with a value from the DB. I would like to change the value in the field depending on some other condition, I tried this with no luck:
if($someValue !== null) {
$builder
->get('name')->setData($someValue);
}
how can I show a different value in the form?
In your controller where you render the form, try to do in this way:
my exemple here is a form for entity named category
$form = $this->createForm(CategoryType::class, $category);
if($someValue !== null) {
$form->get('name')->setData($someValue);
}

Symfony forms | Dynamic collection type values

I'm building a small tool on symfony 3.4, I'm experiencing two issue with a form that I cannot find a solution for.
For the context, the form that is causing me some difficulties is based on a doctrine entity : Event.
This event reference another entity : a Doctrine (nothing do to with the ORM). A doctrine references multiples Fittings.
For a given Event with a given Doctrine, I want to display a collectiontype built from the doctrine fittings that expose a number meant to be the required number of this fitting for this event.
This lead to 3 entities in my form : the event itself, the doctrine, and a collectiontype of fittingRequirements built on my end.
The right panel content is meant to change each time the doctrine change.
Here is the EventType :
<?php
namespace AppBundle\Form;
use AppBundle\Entity\Doctrine;
use AppBundle\Entity\Event;
use AppBundle\Form\DataTransformer\FittingRequirementTransformer;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class EventType extends AbstractType
{
protected $requirementTransformer;
public function __construct(FittingRequirementTransformer $transformer)
{
$this->requirementTransformer = $transformer;
}
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setMethod('POST')
->add('name')
->add(
'date',
DateTimeType::class,
[
'widget' => 'single_text',
'format' => 'yyyy-MM-dd HH:mm',
]
)
->add('startLocation')
->add(
'eventType',
ChoiceType::class,
[
'choices' => [
'PvE' => 'PvE',
'PvP' => 'PvP',
'Other' => 'Other',
],
]
)
->add('target')
->add('description')
->add(
'doctrine',
EntityType::class,
[
'class' => 'AppBundle\Entity\Doctrine',
'choice_label' => 'name',
'query_builder' => function (EntityRepository $repository) {
return $repository->createQueryBuilder('d')->orderBy('d.name', 'ASC');
},
'required' => false,
]
);
$formModifier = function (FormInterface $form, Doctrine $doctrine = null, Event $event) {
$eventRequirements = [];
if ($doctrine) {
$doctrineFittings = $doctrine->getFittings();
$doctrineRequirements = $event->getDoctrineFittingRequirements($doctrine);
$eventRequirements = $this->requirementTransformer->dataToForm(
$doctrineFittings,
$doctrineRequirements,
$event
);
}
$form->add(
'eventRequirements',
CollectionType::class,
[
'entry_type' => FittingRequirementType::class,
'label' => false,
'entry_options' => ['label' => false],
'data' => $eventRequirements,
'mapped' => false,
]
);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
$formupEvent = $event->getData();
$formModifier($event->getForm(), $formupEvent->getDoctrine(), $formupEvent);
}
);
$builder->get('doctrine')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$eventForm = $event->getForm()->getParent();
$doctrine = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $doctrine, $eventForm->getData());
}
);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'data_class' => 'AppBundle\Entity\Event',
]
);
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'event';
}
}
I'm building the list of eventFittingRequirements and adding it on PRE_SET_DATA and POST_SUBMIT
As you can see, I use a CollectionType of FittingRequirementType you can see bellow :
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class FittingRequirementType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('number', NumberType::class);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\FittingRequirement'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_fittingrequirement';
}
}
This is only used to expose the number of required fittings.
All this works well when I display the form, however when I submit the form using javascript to refresh the requirement part, the field are indeed replaced, but the returned form has no value in the inputs.
The $eventRequirements variable from my $formModifier contains a proper set of data, with the number value. However, when I check the XHR with the symfony profiler, the form has no values, even if I select the original doctrine again. I don't understand what is going on and how to fix this.
Thanks for reading
I just found out what was going on and fixed my issue.
My forms are fine, however the handleRequest method clears the unmapped fields I set with my custom fittingRequirement list.
I had to manually submit my form with the clearmissing parameter to false like bellow :
$form->submit($request->request->get($form->getName()), false);

Generate dynamic amount of CollectionType

I want to create a few CollectionType fields.
I have moderators, I load all moderators from db and want to assign sub-users to each modeator in form
Try to do something like that:
public function buildForm(FormBuilderInterface $builder, array $options)
{
foreach ($options['moderators'] as $mod) {
$builder
->add('users['.$mod->getId().']', CollectionType::class, array(
'entry_type' => UserFields::class,
'allow_add' => true,
'label' => false,
'entry_options' => array(
),
));
}
}
In $options['moderators'] there is array of moderators entities.
I want to create the same amount of CollectionType fields as moderators entities (example above - does not work because I cant pass [] as field name)
Each moderator have unique id, so I can use it and pass this id to each CollectionType but how?
How to get back this id of CollectionType in Controller function?
How to display this form on page?
I think you need to dive a little deeper into what collections are ;)
Your collection is invalid, because the entry_type should be a formType. I'm guessing in your case this should be EntityType (if you want the user to choose from a list of users).
So assuming you want a Collection of moderators with each moderator a Collection of sub-users (btw you'd normally make a CRUD and edit each moderator separately, so you don't need a collection with a sub-collection), you'd need something like this:
ModeratorCollectionType.php
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
class ModeratorCollectionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('moderators', CollectionType::class, [
'entry_type' => ModeratorType::class,
'prototype' => true,
'allow_add' => true
]);
}
}
ModeratorType.php
<?php
namespace AppBundle\Form;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ModeratorType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('users', CollectionType::class, [
'entry_type' => EntityType::class,
'entry_options' => [
'class' => 'AppBundle:User',
],
'prototype' => true,
'allow_add' => true
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Moderator',
]);
}
}
In your Controller you'd make a form with the ModeratorCollection
$form = $this->createForm(ModeratorCollectionType::class, $data = []);

symfony3 form, can't get options in buildForm method

I don't really know which title to give this thread but, i'm working on a Symfony project in v3.1.6 and using the select2 plugin in field of my form with ajax.
I want to use the event (submit and pre_set_data) of the form component for creation and editing to change the field dynamically like this part of the symfony doc. Everything work fine until when i submit the form and give an error Notice: Undefined variable: options
My form type code here
namespace Xxx\ArticleBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Xxx\ArtistBundle\Repository\ArtistRepository;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Ivory\CKEditorBundle\Form\Type\CKEditorType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Doctrine\ORM\EntityManager;
class ArticleType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
//$em = $options['em']; //this give me same error
$builder
->add('artist', EntityType::class, array(
'class' => 'XxxArtistBundle:Artist',
'choice_label' => 'artist_name',
'multiple' => false,
'expanded' => false))
->add('title', TextType::class)
->add('categories', EntityType::class, array(
'class' => 'XxxArticleBundle:Category',
'choice_label' => 'name',
'multiple' => true,
'expanded' => true))
->add('image', ChoiceType::class, array(
'expanded' => false,
'multiple' => false))
->add('intro', CKEditorType::class)
->add('content', CKEditorType::class);
$formModifier = function (FormInterface $form, $image) {
$listImages = $options['em']->getRepository('XxxAppBundle:Image')->find($image)); //this line give me the error
if (!$listImages) {
return; //i will add a FormError in the field
}
$listImages = array();
die(var_dump($listImages));
$form->add('image', EntityType::class, array(
'class' => 'XxxAppBundle:Image',
'choices' => $listImages,
));
};
$builder->get('image')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$image = $event->getForm()->getData();
//die(var_dump($image)); //select2 field, returned null (but it's not the question
//die(var_dump($options)); returned error 500 Notice: Undefined variable: options
$formModifier($event->getForm()->getParent(), $image);
}
);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Xxx\ArticleBundle\Entity\Article',
));
$resolver->setRequired('em');
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'xxx_articlebundle_article';
}
}
And i also want to say the goal is to have a UI similar w/ Wordpress when create a post from the dashboard, for set an image in an article and when image is selected throw it in a tag to the user and without entityType because it will have thousand so i choose an choiceType to use with the select2 plugin but if someone got a better solution i'm on it.
Thx in advance for the help
When you're using a variable from a parent scope inside a closure, you should pass it to 'use' language construct.
$formModifier = function (FormInterface $form, $image) use ($options) {
$listImages = $options['em']->getRepository('XxxAppBundle:Image')->find($image)); //this line give me the error
if (!$listImages) {
return; //i will add a FormError in the field
}
$listImages = array();
die(var_dump($listImages));
$form->add('image', EntityType::class, array(
'class' => 'XxxAppBundle:Image',
'choices' => $listImages,
));
};

Symfony2 Form get data value in form type

I want modify a form with a content value :
I try with "PRE_BIND" event but this doesn't work if the form isn't send a first time.
I have this :
<?php
namespace YOU\CommercantBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class LivraisonChoixType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::PRE_BIND, function (FormEvent $event) use ($builder)
{
$form = $event->getForm();
$data = $event->getData();
if ((int)$data['pays'] > 0) {
$form->remove('livreur');
$pays = $data['pays'];
$form->add('livreur','entity',array(
'property' =>'name',
'class' => 'YOUAdminBundle:Livreur',
'label' => 'Livreur :',
'query_builder' => function($er) use ($pays){
return $er->createQueryBuilder('c')
->join('c.pays', 'p')
->andWhere('p.id= :pays')
->addOrderBy('c.name', 'ASC')
->setParameter('pays', $pays);
},
)
);
}
});
$builder
->add('pays','pays',array('label'=>'Destination :'))
->add('livreur','entity',array(
'property' =>'name',
'class' => 'YOUAdminBundle:Livreur',
'label' => 'Livreur :',
'query_builder' => function($er) {
return $er->createQueryBuilder('c')
->join('c.pays', 'p')
->andWhere('p.id= :pays')
->addOrderBy('c.name', 'ASC')
->setParameter('pays', 0);
},
)
)
->add('prix','number',array('required'=>true,'label' => 'Frais :'))
->add('prix2','number',array('required'=>false,'label' => 'Frais en second article :'))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'YOU\CommercantBundle\Entity\LivraisonChoix',
));
}
public function getName()
{
return 'you_commercantbundle_livraisonchoixtype';
}
}
Called by this form type :
<?php
namespace YOU\CommercantBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class LivraisonType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name','text',array('required'=>true,'label'=>'Nom :'))
->add('choix','collection',array(
'type'=>new LivraisonChoixType(),
'options'=>array('attr'=>array('class'=>'livreur-collection')),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'YOU\CommercantBundle\Entity\Livraison'
));
}
public function getName()
{
return 'you_commercantbundle_livraisontype';
}
}
Anyone know how I can get the value ?
This may not be the best answer but this is what I do when I want to assign values to forms before they are rendered. Basically I would bind the form to an entity as such:
// First create entity that will bind with form
$someEntityInstance = new myEntity();
$someEntityInstance->setPropertyOne(5);
$someEntityInstance->setPropertyTwo('another value');
// Then bind entity to form
$myForm = $this->createForm(new myFormType, $someEntityInstance);
Any properties that are mapped from the form to the entity will have the same value and when rendered in the view, this will show up. Though if I have multiple entities represented in a form, I then create a new thing class called a processor that will have properties mapped and bound to the form that can also change the form field values.
This is the easiest way that I know for changing form field values before rendering them.

Categories