I was wondering if it were possible to set something like a property path to an annontation constraint where the assertion should be applied on.
Consider this example:
/**
* #ORM\OneToOne(targetEntity="Document", cascade={"persist"})
* #Assert\Image(mimeTypes={"jpeg", "png"}, path="this.file")
*
protected $document;
In this example I would like to apply the Image constraint to the file property which is a child of the Document entity with an attribute like path="this.file"
Is this somehow possible?
I can only think of defining dynamically the constraint in php.
use Symfony\Component\Validator\Constraints\Image;
// [...]
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('document', FileType::class, [
'constraints' => [
new Image([
'mimeTypes' => ["jpeg", "png"],
'path' => $anyVariable
])
],
]);
You could make your own validator as explained in this doc and then apply to any field you want.
Hope it helps
Related
I have created a data transformation string to date, but I really wanted to try and extend the DateType as it has all the logic for handling dates, has anyone done this before or should I just use a DataTransformer (which is what I have implemented)?
I tried doing the following:
Class HiddenDateType extends DateType {
public function getParent() {
return HiddenType::class;
}
}
But this didn't work, when I looked into the DateType Class it was a little more complicated than I was expecting and didn't know where to hook into the returned type.
I found a few solutions but most of them where pretty hacky, ie; Hiding the entire field via CSS, or changing the type inside the twig template.
This is my transformer, it's VERY redemantry, its frustrating that there is code RIGHT inside the DateType but my skills are lacking :(
class StringToDateTransformer implements DataTransformerInterface
{
/**
* #param \DateTime|null $value
* #return string
*/
public function transform($value)
{
if(null === $value) {
return '';
}
return $value->format('Y-m-d');
}
/**
* #param string $value
* #return \DateTime
*/
public function reverseTransform($value)
{
try {
return new \DateTime($value);
} catch (\Exception $e) {
throw new TransformationFailedException(sprintf(
'Invalid Format for %s format yyyy-mm-dd',
$value
));
}
}
}
On the actual rendered page there is a single div which has a daterangepicker javascript event (on click) is attached to it, this event is triggered when clicked which allows the user to select a start and end date.
The javascript will then populate two hidden DateType fields (startDate, endDate) within the form.
This is the div that the daterangepicker picker is attached to.
<div id="selectedRange">
<i class="fa fa-calendar"></i>
<span></span> <i class="fa fa-caret-down"></i>
</div>
You should be able to do this in your formType class buildForm() method
// ./src/form/yourType.php
$builder->add('hidden_date_field_name', DateType::class, [
'attr' => [
'hidden' => 'hidden',
'disabled' => 'disabled',
],
]);
You should be fine using a DateType or DateTimeType if you do not plan to use something like bootstrap timepicker or any other JS modification of your input.
However, if that's the case and you want to make a better looking date input, the simple way to do it is to use it as a string like so :
$builder->add('date', TextType::class, array(
'required' => true,
'label'=>'Date',
'attr' => array(
'class' => 'datetimepicker',
'data-provide' => 'datepicker',
'data-format' => 'dd-mm-yyyy HH:ii'
),
));
And be sure to add your ModelTransformer to the input in your FormType like so:
$builder->get('date')
->addModelTransformer(new StringToDateTransformer());
Note that you can specify the date format for your js component in the input attribute.
CollectionType field has special 'prototype' variable when 'allow_add' option is set to true. This variable can be used to render prototype html like this:
data-prototype="{{ form_widget(form.collectionfieldname.vars.prototype)|e('html_attr') }}"
It looks like 'prototype' is simply an instance of collection children FormView built with partial data (e.g. name is set to "__name__" while most other vars are left blank).
Where all this magic happens? Is it possible to modify what data is passed to prototype view while building form? For example, I would like to change default value of "value" variable from blank to "__val__" (outside of Twig template).
Answer to own question - values defined in "entry_options" setting are used to build prototype. It is possible to pass these values to form builder like this:
$builder
->add('email', CollectionType::class, array(
...
'entry_options' => array(
'someoption' => 'somevalue',
),
...
))
If this is not enough, default behaviour can be modified by overriding "buildForm" method in "CollectionType" class which is responsible for collecting options and building prototype:
class CollectionType extends AbstractType
{
...
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ($options['allow_add'] && $options['prototype']) {
$prototypeOptions = array_replace(array(
'required' => $options['required'],
'label' => $options['prototype_name'].'label__',
), $options['entry_options']);
if (null !== $options['prototype_data']) {
$prototypeOptions['data'] = $options['prototype_data'];
}
$prototype = $builder->create($options['prototype_name'], $options['entry_type'], $prototypeOptions);
$builder->setAttribute('prototype', $prototype->getForm());
}
...
}
...
}
Say, there is a simple many-to-one relations: Model has hair type and eyes color:
/**
* #ORM\Entity
*/
class Model
{
/**
* #ORM\ManyToOne(targetEntity="Hair")
* #ORM\JoinColumn(name="hair_id", referencedColumnName="id")
*/
protected $hair;
/**
* #ORM\ManyToOne(targetEntity="Eyes")
* #ORM\JoinColumn(name="eyes_id", referencedColumnName="id")
*/
protected $eyes;
For example, hair could be: blonde, brown, black, red;
The eyes: blue, green, gray, brown.
In the search form I want user to be able to select multiple hair types and eyes at once. I use 'multiple' property:
class ModelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('hair', EntityType::class, [
'class' => 'AppBundle:Hair',
'choice_label' => 'name',
'multiple' => true,
])
->add('eyes', EntityType::class, [
'class' => 'AppBundle:Eyes',
'choice_label' => 'name',
'multiple' => true,
])
->getForm();
;
}
The form renders like this:
Of course, when selecting multiple values and submitting it causes an error:
Expected argument of type "AppBundle\Entity\Hair", "Doctrine\Common\Collections\ArrayCollection" given
Perhaps, this is not for using in such a case?
Are any best practices for building search forms in Symfony? Didn't find any...
The problem is not the form but your mapping.
I assume your form is binded with the Model Entity.
That's why the ManyToOne relation accepts only one related entity.
Solution :
Don't bind your form to the Model Entity, just use a form without class :
http://symfony.com/doc/current/form/without_class.html
Model can have many hair color and many eyes color ?
In this case you have to use many-to-many relation instead of many-to-one
If not, you have to remove 'multiple' => true, in you ModelType
I'm using Symfony 2.8 and I'm trying to create a registration form containing a "terms & conditions" check-box which isn't mapped to the underlying data model.
I've followed this cookbook article:
How to Implement a Simple Registration Form
Everything in the form validation works, except for the the "terms & conditions" check-box. After submitting the form, the check-box validation doesn't get triggered.
This is my code:
namespace Likestripe\AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
use Symfony\Component\Validator\Constraints\IsFalse;
use Symfony\Component\Validator\Constraints\IsTrue;
class RegistrationFormType extends AbstractType { //BaseType
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
// call parent constructor
//parent::buildForm($builder, $options);
// add your custom fields
$builder->add('salutation', ChoiceType::class, array('choices' => array('Herr' => 0, 'Frau' => 1), 'choices_as_values' => true));
$builder->add('firstname', TextType::class, array());
$builder->add('lastname', TextType::class, array());
$builder->remove('username');
$builder->add('company', new CompanyFormType());
$builder->add('conditions', CheckboxType::class, array('mapped' => false, 'constraints' => new IsTrue(array("message" => "Bitte akzeptieren Sie noch die AGB und Nutzungsbedingungen."))));
$builder->add('submit', SubmitType::class, array('label' => 'Registrieren'));
} // end function
public function getParent() {
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
// Or for Symfony < 2.8
// return 'fos_user_registration';
}
/**
* function deprecated since 2.8 https://github.com/symfony/symfony/blob/2.8/UPGRADE-2.8.md#form
*
* #return string
*/
public function getName() {
//return 'likestripe_user_registration';
return $this->getBlockPrefix();
} // end function
public function getBlockPrefix() {
return 'likestripe_user_registration';
}
} // end class
I can't see any difference between my code and the code demonstrated in the cookbook article.
Screen capture of my Symfony debug console:
UPDATE:
As Kamil proposed, I've checked if the 'conditions' check-box form parameter gets posted after submitting the form.
The param gets posted als long as the check-box is checked, but if not, the "conditions" form parameter doesn't get posted at all... this behavior reminds me of this case.
I'm still wondering why the official Symfony documentation proposes a isTrue Validator which doesn't seem to be the solution to check for an unchecked check-box, any suggestions how to fix this?
Checkbox checked:
Checkbox unchecked:
Thanks in advance for your help
ninsky
If you let the 'required' of your CheckboxType to true, the constraints isTrue is not useful because the checkbox will always be true !
If change change that to :
$builder->add('conditions', CheckboxType::class, array('mapped' => false, 'required' => false, 'constraints' => new IsTrue(array("message" => "Bitte akzeptieren Sie noch die AGB und Nutzungsbedingungen."))));
With this configuration you can submit the form and if the box is not checked the constraints will send your message.
Hope this help ...
As a workaround, you can add a form listener to make sure you submit the value. Add following to the bottom of your buildForm() function:
$builder->addEventListener(FormEvents::PRE_BIND, function (FormEvent $event) {
$data = $event->getData();
if (!isset($data['conditions'])) {
$data['conditions'] = false;
}
$event->setData($data)
});
As another workaround, you can perform a check in controller and show flash message if request headers do not contain 'conditions'.
Symfony docs mention it:
$builder
->add('email', EmailType::class);
// ...
->add('termsAccepted', CheckboxType::class, array(
'mapped' => false,
'constraints' => new IsTrue(),
))
);
Well, I don't see why is not working for you so in the meantime I give you an alternative way :)
$builder->add('conditions', CheckboxType::class , array('mapped' => false, 'required' => true, 'attr'=>array('onchange'=>"try{setCustomValidity('')}catch(e){}",'oninvalid'=>"setCustomValidity('Bitte akzeptieren Sie noch die AGB und Nutzungsbedingungen.')")));
Hope this work for you.
I've run your code with everything but the CompanyFormType line and it is working as expected.
Could you remove this CompanyFormType line and give it a try ?
You have to set the validation group Overriding Default FOSUserBundle Forms
By default, the Registration validation group is used when validating
a new user registration. Unless you have overridden this value in the
configuration, make sure you add the validation group named
Registration to your name property.
How to do that you can find out here: Overriding Default FOSUserBundle Validation
I try to use Genemu JQueryColor Field with Symfony2
<?php
// ...
public function buildForm(FormBuilder $builder, array $options)
{
$builder
// ...
->add('color', 'genemu_jquerycolor')
->add('colorpicker', 'genemu_jquerycolor', array(
'widget' => 'image'
))
}
It runs Exception
Neither the property "colorpicker" nor one of the methods
"getColorpicker()", "colorpicker()", "isColorpicker()",
"hasColorpicker()", "__get()" exist and have public access in class
"KALAN\NetRDVBundle\Entity\Station
I try just
->add('colorpicker', 'genemu_jquerycolor', array(
'widget' => 'image'
))
No error, but just inpput text with code color
I try
->add('color', 'genemu_jquerycolor', array(
'widget' => 'image'))
No error, the background color is ok but i can change the color.
I try just
->add('colorpicker', 'genemu_jquerycolor', array(
'widget' => 'image'))
Or
->add('colorpicker', 'genemu_jquerycolor')
The error is the same
Neither the property "colorpicker" nor one of the methods
"getColorpicker()", "colorpicker()", "isColorpicker()",
"hasColorpicker()", "__get()" exist and have public access in class
"KALAN\NetRDVBundle\Entity\Station".
Even if i can't add
{{ form_widget(form.colorpicker) }}
May be its late maybe its not , the thing is like this.
Your class doesnt have the property colorpicker .
When you add a field to the builder , the builder will try to map it to the field of the entity you are making the formType for (in this case Station).
What you need to do is tell to the builder not to map that field. You can achieve this using the "mapped" property.
http://symfony.com/doc/current/reference/forms/types/form.html#mapped ------>Form type doc.