Synfony 2.8 one of the two fields must be filled - forms

I have this form and I want to checked if one of the two fields (numberPlate or expirationDate) is filled.
This is my buildForm:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('type', ChoiceType::class, array(
'choices_as_values' => true,
'required' => true,
'label' => 'Tipo Veicolo',
'empty_data' => '',
'empty_value' => '',
'attr' => array('class'=> 'form-control select2'),
'choices' => array('Auto' => 'Auto', 'Moto' => 'Moto', 'Camper' => 'Camper' ,'Barca' => 'Barca')
))
->add('numberPlate', TextType::class, array(
'label' => 'Targa',
'required' => false,
'attr' => array(
'class'=> 'form-control',
'minlength' => 5,
'maxlength' => 7
)
))
->add('expirationDate', DateTimeType::class, array(
'label' => 'Scadenza',
'widget' => 'single_text',
'input' => 'datetime',
'format' => 'dd/MM/yyyy',
'attr' => array('class'=> 'form-control')
))
;
}

You can ensure one of the fields are not empty, by adding a callback constraint to your entity.
namespace App\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
class YourModel
{
/**
* #Assert\Callback
*/
public function validate(ExecutionContextInterface $context)
{
if (!$this->numberPlate && !$this->expirationDate) {
$context->buildViolation('Targa or Scadenza is required')
//optionally display the error at the numberPlate field, omit to display at the top of the form errors
->atPath('numberPlate')
->addViolation()
;
}
}
}
Then update your Scadenza field as not required.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
//...
->add('expirationDate', DateTimeType::class, array(
'label' => 'Scadenza',
'required' => false,
'widget' => 'single_text',
'input' => 'datetime',
'format' => 'dd/MM/yyyy',
'attr' => array('class'=> 'form-control')
))
;
}
When the form is submitted it will execute the YourModel::validate method, and if the numberPlate and expirationDate are empty, it will fail $form->isValid().
Be sure to clear your cache after making the changes, to refresh the annotations.
NOTE: This will apply to any/all forms this entity model is used in,
to separate the validation you will need to implement validation groups

Related

Symfony 3 callback validation not being called

I have a form in Symfony 3 where I want the user to upload a file or fill in a comment (or both).
$form = $this->createFormBuilder(
$approvalRequest,
array(
'validation_groups' => array('revocation_proposal')
)
)
->add(
'deletionComment',
TextareaType::class,
array(
'attr' => array(
'cols' => 70,
'rows' => 10
),
'required' => false //otherwise html will force it to be required
)
)
->add('deletionTemplate',
ResearchTemplateType::class,
array('label' => 'Deletion Form',
'required' => false)) //otherwise html will force it to be required
->add(
'cancel',
SubmitType::class,
array(
'label' => 'Cancel'
)
)
->add('revoke', SubmitType::class, array(
'label' => 'Revoke Approval',
'attr' => array('class' => 'btn-danger')
))
->getForm();
$form->handleRequest($request);
I am trying to enforce that at least one of the two (the comment or the file) must be present in order for the form to be valid using callbacks.
In my entity I have:
/**
* #Assert\Callback
*/
public function validate(ExecutionContextInterface $context)
{
if (is_null($this->getDeletionComment()) && is_null($this->getDeletionTemplate())) {
$context->buildViolation('You must write a deletion comment or upload a deletion template')
->atPath('deletionTemplate')
->addViolation();
}
}
But it is not being called. Am I missing something?
Thanks

Symfony 3 : Build forms for entities based on Traits

I'm having a hard time building forms for my entities that are themselves built with traits.
For example my "Article" entity only contains the link to the category and 2 pics, the rest of its properties is in the SeoTrait (title, meta_title, meta_desc, content, etc...), ValidTrait (isValid true/false)... which I want to use for other entities.
It all works fine for doctrine, that generates my schema with all the fields from the Traits in each entity that use them. The problem is for the forms :
I've created the SeoTraitType for the "SEO" properties :
class SeoTraitType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, array(
'label' => 'Nom'
))
->add('metaTitle', TextType::class, array(
'label' => 'Meta Title'
))
->add('metaDescription', TextareaType::class, array(
'label' => 'Meta Description'
))
->add('metaKeywords', TextType::class, array(
'label' => 'Keywords'
))
->add('content', TextareaType::class, array(
'label' => 'Content'
))
;
}
}
And then I'm using it in my ArticleType :
class ArticleType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('seo', SeoTraitType::class, array(
'label' => 'Seo',
'mapped' => false
))
->add('isValid', ValidTraitType::class, array(
'label' => 'Valid',
'mapped' => false
))
->add('save', SubmitType::class, array(
'label' => 'form_save',
'translation_domain' => 'back_default'
));
;
}
}
The two problems I have here is that I must set mapped => false for the 2 TraitTypes when I want to embed them in my main entity's form
And then in my form I get article[seo][name] for the SeoTrait's fields, so I can't really use the $form->handleRequest() methods and all... to handle the submission of my form
I was wondering if there is a special way to do this within the provided methods of the form component, or if I just have to handle the request myself and parse the trait arrays myself to build my entity before saving it ? I couldn't really find anything on the internet :(
One way to solve your problem is to transform your class SeoTraitType into a Trait.
like:
trait SeoTraitType
{
public function buildSEOForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, array(
'label' => 'Nom'
))
->add('metaTitle', TextType::class, array(
'label' => 'Meta Title'
))
->add('metaDescription', TextareaType::class, array(
'label' => 'Meta Description'
))
->add('metaKeywords', TextType::class, array(
'label' => 'Keywords'
))
->add('content', TextareaType::class, array(
'label' => 'Content'
))
;
}
}
Then:
class ArticleType extends AbstractType
{
use SeoTraitType;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->buildSEOForm($builder, $options);
$builder
->add('isValid', ValidTraitType::class, array(
'label' => 'Valid',
'mapped' => false
))
->add('save', SubmitType::class, array(
'label' => 'form_save',
'translation_domain' => 'back_default'
));
;
}
}
You can also do this with static method. Not a big fan of Trait.
Ok so the Trait solution works fine, but I chose to go for this method here :
https://symfony.com/doc/current/form/inherit_data_option.html
Thanks so much guys, I was pretty sure that the solution would be somewhere in the documentation but couldn't find it !

How Can I get autofocus on the first element (buildform) symfony

In my TopicType class, I used :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', 'text')
->add('content', 'ckeditor', array(
'label' => 'Contenu',
'config_name' => 'my_custom_config',
'config' => array('language' => 'fr'),))
->add('save', 'submit')
;
}
How can I get autofocus on my first field "title", when i display the form?
$builder->add('title', 'text', array(
'attr' => array(
'autofocus' => true
)
);
The realy crossbrowser way is to type
$builder->add('title', 'text', array(
'attr' => array(
'autofocus' => null
)
);
This code produces just autofocus attribute without = sign and any value.

remove entity from entity form field if custom condition is matched

I have an unsubscribe form, where the first field is a entity field, fetching my UnsubscribeType entities :
$builder
->add('type', 'entity', array(
'class' => 'Evo\SubscriptionBundle\Entity\UnsubscribeType',
'property' => 'name',
'label' => 'Choose process type',
'empty_value' => 'Choose an option',
'empty_data' => null,
'label_attr' => array(
'class' => 'control-label',
),
'attr' => array(
'class' => 'form-control',
),
))
this collection contains 2 entities, #1 and #2. I would like to remove entity #2 from the select field if a custom test about the authed user fails.
Let's say I'd to test $this->getUser()->getCustomField(). If it is false, then i would like to remove entity #2 from the select field containing my UnsubscribeType entities.
Any idea how to do that ?
Perhaps you could pass to the form builder the result of the test from you controller and then, either use 'property' => 'name' or 'choices => // fetch entity #1 here.
So, something like this
public function buildForm(FormBuilderInterface $builder, array $options)
{
if($options['customField'] === TRUE)
{
$builder
->add('type', 'entity', array(
'class' => 'Evo\SubscriptionBundle\Entity\UnsubscribeType',
'property' => 'name',
// ..
;
}
else
{
$builder
->add('type', 'entity', array(
'class' => 'Evo\SubscriptionBundle\Entity\UnsubscribeType',
'choices' => $options['customField'],
// ..
;
}
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
// ..
$resolver->setRequired(array(
'customField'
));
}
And in your controller:
$form = $this->get('form.factory')->create(new EntryType(), $entry, array(
'customField' => $this->getUser()->getCustomField()
));
with getCustomField() returning either true or the entity (or collection of entities) that you want populating your select field. My solution would involve changing the getCustomField method though, don't know if that would suit.

Symfony2 form populating entitiy based on other entity

I'm using Symfony2 form component to create and modify forms. I'm currently loading all model entities as choices, but I need to get only the Models related to the selected (posted) value from manufacturer.
How can I achieve this?
class VehicleType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('manufacturer', 'entity', array(
'label' => 'Brand',
'attr' => array('class' => 'input-block-level'),
'class' => 'BalslevCarBundle:Manufacturers',
'property' => 'short_name',
'empty_value' => 'All',
'required' => false
));
$builder->add('model', 'entity', array(
'label' => 'Model',
'class' => 'BalslevCarBundle:Models',
'property' => 'model',
'empty_value' => 'All',
'required' => false
));
}
public function getName()
{
return 'search';
}
}
First of all, you have to create "dynamically" your choices based onto user selection.
There are two ways, strictly coupled with your application behaviour, that leads (in both cases) at the same "core" code; the difference is only the way you call it.
So, let's explain the two possibilities:
Select manufacturer "statically" and don't change it over the time of request: use data retrieved from your business logic
Select manufacturer "dinamically" and can change them over the time of request: replace your form at every request; new form will be provided by business logic
So, the HEART of your question is: how can I retrieve only associated entities?
Answer
Into your VehicleType.php (where entity, in this case, is VehicleType ?)
private $manufacturerId;
public function __construct($manufacturerId)
{
$this->manufacturerId = $manufacturerId;
}
public function BuildForm(FormBuilderInterface $builder)
{
$manufacturerId = $this->manufacturerId;
$manufacturerQuery = function(EntityRepository $repo) use ($manufacturerId ){
return $repo->createQueryBuilder('m')
->where('m.id = :id')
->setParameter('id',$manufacturerId );};
$builder->add('manufacturer', 'entity', array(
'label' => 'Brand',
'attr' => array('class' => 'input-block-level'),
'class' => 'BalslevCarBundle:Manufacturers',
'property' => 'short_name',
'empty_value' => 'All',
'required' => false,
'query_builder' => $manufacturerQuery;
));
$builder->add('model', 'entity', array(
'label' => 'Brand',
'attr' => array('class' => 'input-block-level'),
'class' => 'BalslevCarBundle:Manufacturers',
'property' => 'short_name',
'empty_value' => 'All',
'required' => false,
'query_builder' => $modelQuery;
));
}
As you can see, in this way we use a custom repository to get only entities that are suitable for our needs. Obviously I am writing my DQL query without knowing your DB structure: it's your task and responsibility to adapt it to your real situation.
Moreover, you have to create another query for model (called $modelQuery in my answer) to retrieve correct models based on selected $id