I'm trying to render a checkbox inside a form but got the next error:
TransformationFailedException in Form.php line 1149: Unable to
transform value for property path "[active]": Expected a Boolean.
Yes, what I send is an integer 0 or 1, but twig get them as string and it has sense but I really can't find how to render the checkbox.
This is the controller:
$form = $form->add('active', 'checkbox', array('label' => 'User active?','required' => false));
And this is the Twig side:
{{ form_widget(form.active, { attr: { 'class': 'form-control' }}) }}
Any idea?
In your form add a model transformer as #Rinat suggested:
$form->add('active', 'checkbox', array('label' => 'User active?','required' => false));
$form->get('active')
->addModelTransformer(new CallbackTransformer(
function ($activeAsString) {
// transform the string to boolean
return (bool)(int)$activeAsString;
},
function ($activeAsBoolean) {
// transform the boolean to string
return (string)(int)$activeAsBoolean;
}
));
More details here: http://symfony.com/doc/current/cookbook/form/data_transformers.html
You should add
/**
* #ORM\Column(type="boolean")
*/
protected $active = false;
to your domain model.
Related
I have a form with 3 fields as below : storeClient , type and line that belong to Fashion entity.
I basically have the same problem mentioned here :
Symfony Form Validation not working in Edit
But I was surprised when I edited the form and chose the placeholder as option for Client and I filled the line and type fields , I got that for the client field, it DOES display my validation message "Please choose an option" .which is good .
However for the remaining two fields, if line or type are edited in such a way to choose the placeholder option, the application crashed and gives the argumet exception error.
I did a dump; die; inside the controller (you can see it commented below) . And I got the $fashion object like this when I chose the placeholder for all the fields aka for the client,type and line fields :
Fashion object :
storeClient: null
line: Proxies ...\RefLine
___isinitilized___ :true
and all the info of the line that was set initiallly in this object when I first enterede its edit page.
type: Proxies ...\RefType
___isinitilized___ :true
and all the info of the type that was set initiallly in this object when I first enterede its edit page.
so my question is why the validations work on edit for the client field and does not work for line and type ? I do not think that it is related to the fact that it is a choicettype whereas the other two are entitytype. Moreover, I didn't put a "?" in the setter of client. So i don't see why it works for this field and WHY it gave a Null value when I printed the object and it didn't print the initial client value that was already set in the object when I first landed on the edit page although the two other fields hold the values that were already stored in the object initially.
FashionType.php
->add('storeClient', ChoiceType::class,
[
'label' => 'Store Client',
'choices' => $choicesClient,
'choice_value' => function ($value) {
if ($value instanceof Client) {
return $value->getId();
} else {
return $value;
}
},
'placeholder' => 'Choose ..',
'choice_label' => 'diplayLabel',
'attr' => ['class' => "chosen"],
'required' => true,
]
)
->add('type',
EntityType::class,
[
'label' => 'Clothes Type',
'class' => RefType::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('refType')
->orderBy('refType.id', 'ASC');
},
'attr' => ['class' => "chosen"],
'placeholder' => 'Choose..',
'required' => true,
'choice_label' => 'label',
])
->add('line',
EntityType::class,
[
'label' => 'cotation.creation_form.ligne_de_cotation',
'class' => RefLine::class,
'choice_value' => function ($value) {
if ($value instanceof RefLine) {
return $value->getId();
} else {
return $value;
}
},
'query_builder' => function (EntityRepository $er) {
return $er->getShoppingLines();
},
'attr' => ['class' => "chosen"],
'placeholder' => 'Choose..',
'required' => true,
'choice_label' => 'getLabel',
])
IN my controller, this function is called upon submitting the form :
public function validerAction(Request $request, $idFashion)
{
$em = $this->getDoctrine()->getManager();
/** #var Fashion $fashion */
$fashion = ($idFashion === null) ? new Fashion() : $em->getRepository(
'App:Fashion'
)->find($idFashion);
$form = $this->createForm(FashionType::class, $fashion);
// try {
$form->handleRequest($request);
//} catch(\InvalidArgumentException) {
//dump($fashion);die;
// }
if ($form->isSubmitted() && $form->isValid()) {..}
Here are my setters:
/**
* Set line
*
* #param Refline $line
*
* #return Fashion
*/
public function setLine(RefLine $line)
{
$this->line = $line;
return $this;
}
/**
* Set type
*
* #param RefType $type
*
* #return Fashion
*/
public function setType(RefType $type)
{
$this->type = $type;
return $this;
}
/**
* Set storeClient
*
* #param Client $storeClient
* #return Fashion
*/
public function setStoreClient($storeClient)
{
$this->storeClient = $storeClient;
return $this;
}
THe three fields were declared like this :
/**
* #ORM\ManyToOne(targetEntity="App\Entity\RefLine")
* #ORM\JoinColumn(name="line_id", referencedColumnName="id", nullable=false)
*/
private $line;
In EntityType::class field type is by default nullable. If you want to add validation on that then you have to write this
/**
*
* #Assert\NotBlank(message="Please enter Line", groups="groupName")
*/
private $line;
For more details you can read https://symfony.com/doc/current/validation.html
if you are using group name then you should declare in Form
$resolver->setDefaults([
// ...
'validation_groups' => ['Default', 'groupName'],
]);
We are using Symfony Forms for our API to validate request data. At the moment we are facing a problem with the CollectionType which is converting the supplied value null to an empty array [].
As it is important for me to differentiate between the user suppling null or an empty array I would like to disable this behavior.
I already tried to set the 'empty_data' to null - unfortunately without success.
This is how the configuration of my field looks like:
$builder->add(
'subjects',
Type\CollectionType::class,
[
'entry_type' => Type\IntegerType::class,
'entry_options' => [
'label' => 'subjects',
'required' => true,
'empty_data' => null,
],
'required' => false,
'allow_add' => true,
'empty_data' => null,
]
);
The form get's handled like this:
$data = $apiRequest->getData();
$form = $this->formFactory->create($formType, $data, ['csrf_protection' => false, 'allow_extra_fields' => true]);
$form->submit($data);
$formData = $form->getData();
The current behavior is:
Input $data => { 'subjects' => null }
Output $formData => { 'subjects' => [] }
My desired behavior would be:
Input $data => { 'subjects' => null }
Output $formData => { 'subjects' => null }
After several tries I finally found a solution by creating a From Type Extension in combination with a Data Transformer
By creating this form type extension I'm able to extend the default configuration of the CollectionType FormType. This way I can set a custom build ModelTransformer to handle my desired behavior.
This is my Form Type Extension:
class KeepNullFormTypeExtension extends AbstractTypeExtension
{
public static function getExtendedTypes(): iterable
{
return [CollectionType::class];
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder->addModelTransformer(new KeepNullDataTransformer());
}
}
This one needs to be registered with the 'form.type_extension' tag in your service.yml:
PrivateApiBundle\Form\Extensions\KeepNullFormTypeExtension:
class: PrivateApiBundle\Form\Extensions\KeepNullFormTypeExtension
tags: ['form.type_extension']
Please note that you still use the CollectionType in your FormType and not the KeepNullFormTypeExtension as Symfony takes care about the extending...
In the KeepNullFormTypeExtension you can see that I set a custom model transformer with addModelTransformer which is called KeepNullDataTransformer
The KeepNullDataTransformer is responsible for keeping the input null as the output value - it looks like this:
class KeepNullDataTransformer implements DataTransformerInterface
{
protected $initialInputValue = 'unset';
/**
* {#inheritdoc}
*/
public function transform($data)
{
$this->initialInputValue = $data;
return $data;
}
/**
* {#inheritdoc}
*/
public function reverseTransform($data)
{
return ($this->initialInputValue === null) ? null : $data;
}
}
And that's it - this way a supplied input of the type null will stay as null.
More details about this can be found in the linked Symfony documentation:
https://symfony.com/doc/current/form/create_form_type_extension.html
https://symfony.com/doc/2.3/cookbook/form/data_transformers.html
In Sonata Admin is it possible to set or precompile a value in a form taking it from the query string?
I have a custom page with a calendar and when I click a date on the calendar I would like to be redirected to the create page of an event where the date is already set from the query string. Something like:
http://localhost:8000/admin/app/event/create?date=2017-07-11
I solved it by using the request service and a field marked as 'mapped' => false on my form builder:
protected function configureFormFields(FormMapper $formMapper)
{
$request = $this->getRequest();
$dateParameter = $request->query->get('date');
$date = null;
if ($dateParameter) {
$date = \DateTime::createFromFormat('!Y-m-d', $dateParameter);
}
if (!$date) {
$formMapper
->add('date', DatePickerType::class)
->add('calendarDate', HiddenType::class, array(
'data' => null,
'mapped' => false,
))
;
} else {
$formMapper
->add('date', DatePickerType::class, array(
'data' => $date,
'disabled' => true,
))
->add('calendarDate', HiddenType::class, array(
'data' => $date->format('Y-m-d'),
'mapped' => false,
))
;
}
$formMapper
// other fields
->add('other_field', ModelType::class)
;
}
Then, on the following method I am updating the entity field:
public function prePersist($myEntity)
{
$this->preUpdate($myEntity);
}
public function preUpdate($myEntity)
{
$dateString = $this->getForm()->get('calendarDate')->getData();
if ($dateString) {
$date = \DateTime::createFromFormat('!Y-m-d', $dateString);
if ($date) {
$myEntity->setDate($date);
}
}
}
In this way I can use my form with or without the query string parameter date:
http://localhost:8000/admin/app/event/24/edit?date=2017-07-01
And in the HTML I have this:
<input type="hidden" id="s59664a9ea44ce_calendarDate" name="s59664a9ea44ce[calendarDate]" class=" form-control" value="2017-07-01" />
P.S. when someone gives a -1 it should be nice to explain why...
I have following entity field:
/**
* #Assert\Regex(
* pattern = "/^d+\.(jpg|png|gif)$/",
* htmlPattern = "/^d+\.(jpg|png|gif)$/"
* )
**/
protected $src;
The form is created by something like this:
$builder
->add('src', TextareaType::class, array( //neither is TextType::class working
'attr' => array('class' => 'mysrc'),
)); //pattern will not be rendered
The problem is, as soon as I provide the field type class TextareaType::class the regex pattern isn't rendered as constraint to the form. Or in other words: The regex pattern is only rendered, if the field type is guessed:
$builder
->add('src', null, array(
'attr' => array('class' => 'mysrc'),
)); //pattern will be rendered
Any workaround? I want to have the regex patterns in one place, i.e. in my entity, not in a controller or a form.
Yepp, this is how it should work. Not only the field type is guessed, but some options too and if the type is not guessed the options neither.
BTW the textarea element does not support the pattern attribute, but if you want to you can add one: 'attr' => array('pattern' => '/jsregex/'), you should use a different pattern for the client side than the server side anyway. If you want to give the pattern in one place use a constant.
Maybe this one could help you:
FormType
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Validator\Constraints\Regex;
use YourBundle\Entity\YourEntity;
public function __construct(ValidatorInterface $validator)
{
$this->validator = $validator;
}
public function getRegexPattern($property)
{
$pattern = null;
$metadata = $this->validator->getMetadataFor(new YourEntity());
$propertyMetadata = $metadata->getPropertyMetadata($property);
$constraints = $propertyMetadata[0]->constraints;
foreach($constraints as $constraint) {
if ($constraint instanceof Regex) {
$pattern = $constraint->pattern;
}
}
return $pattern;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('src', TextType::class, array(
'attr' => array(
'class' => 'mysrc',
'pattern' => $this->getRegexPattern('src')
),
));
}
services.yml (Needed to inject validator in type)
services:
app.form.type.your_entity_type:
class: YourBundle\Form\YourEntityType
arguments:
validator: "#validator"
tags:
- { name: form.type }
Rendered
<input type="text" pattern="/^d+\.(jpg|png|gif)$/" class="src"/>
Like this, even if the TextType in manually set, you get the html validation through pattern retrieved from the constraints of the property.
I have a created a custom form field dropdown list for filtering by year. One of the things I want to do is to allow the user to filter by all years, which is the default option. I am adding this as an empty_value. However, when I render the form, it defaults on the first item that's not the empty value. The empty value is there, just above it in the list. How do I make the page default to, in my case 'All' when the page initially loads? Code is below.
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class YearType extends AbstractType
{
private $yearChoices;
public function __construct()
{
$thisYear = date('Y');
$startYear = '2012';
$this->yearChoices = range($thisYear, $startYear);
}
public function getDefaultOptions(array $options)
{
return array(
'empty_value' => 'All',
'choices' => $this->yearChoices,
);
}
public function getParent(array $options)
{
return 'choice';
}
public function getName()
{
return 'year';
}
}
I'm rendering my form in twig with a simple {{ form_widget(filter_form) }}
Try adding empty_data option to null, so it comes first. I have many fields of this type and it's working, for example:
class GenderType extends \Symfony\Component\Form\AbstractType
{
public function getDefaultOptions(array $options)
{
return array(
'empty_data' => null,
'empty_value' => "Non specificato",
'choices' => array('m' => 'Uomo', 'f' => 'Donna'),
'required' => false,
);
}
public function getParent(array $options) { return 'choice'; }
public function getName() { return 'gender'; }
}
EDIT: Another possibility (i suppose) would be setting preferred_choices. This way you'll get "All" option to the top. But i don't know if it can work with null empty_data, but you can change empty_data to whatever you want:
public function getDefaultOptions(array $options)
{
return array(
'empty_value' => 'All',
'empty_data' => null,
'choices' => $this->yearChoices,
'preferred_choices' => array(null) // Match empty_data
);
}
When I've needed a simple cities dropwdown from database without using relations, I've ended up using this config for city field (adding null as first element of choices array), as empty_data param didn't do work for me:
$builder->add('city',
ChoiceType::class,
[
'label' => 'ui.city',
'choices' => array_merge([null], $this->cityRepository->findAll()),
'choice_label' => static function (?City $city) {
return null === $city ? '' : $city->getName();
},
'choice_value' => static function(?City $city) {
return null === $city ? null : $city->getId();
},
]);