I use symfony validator assert, in my class subscription i have:
/**
* #var string
* #Assert\NotBlank(message="asasass")
* */
private $name;
Forms:
class SubscriptionType extends AbstractType{
public function getName() {
return 'Piotrek_subscription';
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('name', 'text', array(
'label' => 'Imię i nazwisko'
))
->add('email', 'text', array(
))
->add('save', 'submit', array(
'label' => 'Zapisz się'
)); }
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'Piotrek\WitajBundle\Entity\Subscription'
));
}
}
And my controler:
$Subscription = new Subscription();
$form = $this->createForm(new SubscriptionType(), $Subscription);
return array(
'form' => $form->createView(),
My form don't read the custom validate in comment. Imposes standard , but also do not know where. When I delete comment the ruler is still the same.
So how do you change the text validation?
You constraint is a server side one, that means when you submit your form the page reloads and you will get the errors. But the message you see is the default HTML5 error message when you try to submit an empty field, because unless you mention otherwise inside your formbuiler, all fields will be rendered as required that's why you get the default hhtml 5 error message
You can do what want using:
Method 1: this will display your custom message "asasasas" after loading the page
set the required option as false
builder->add('name', 'text',array('required' => false )
Method 2:
Change the default htm5 error message from inside your SubscriptionType , something like this: ( I don't remember it exactly but this could point you to the right path)
builder->add('name' ,'text',array(
'attr'=>array('oninvalid'=>"setCustomValidity('bla bla.. ')")
Hope this works for you.
It looks like symfony does not read annotations in your code.
Probably you have not enabled annotations in app/config.yml
framework:
validation: { enabled: true, enable_annotations: true }
Related
I'm using Symfony 3.3 and im getting this TransformationFailedException Error, when i load my profile page:
Unable to transform value for property path "postalcode": Expected a numeric.
The postalcode value for this user in the database is:
'34125abc'
The postalcode attribute defined in UserProfile Entity:
/**
* #ORM\Column(type="string")
*/
private $postalcode;
My ProfileController:
class ProfileController extends Controller{
/**
* #Route("/edit_profile", name="edit_profile")
*/
public function profileAction(Request $request){
$profile = $this->getDoctrine()->getManager()->getRepository('AppBundle:UserProfile')->findOneBy(['user_id' => $this->getUser()->getUserId()]);
// If no UserProfile exists, create a UserProfile Object to insert it into database after POST
if(null === $profile){
$profile = new UserProfile();
$profile->setUserId($this->getUser()->getUserId());
}
$form = $this->createForm(EditProfileFormType::class);
$form->setData($profile);
// only handles data on POST
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$result = $this->forward('AppBundle:API\User\Profile:update_profile', array(
'profile' => $profile
));
if(200 === $result->getStatusCode()){
$this->addFlash('success', "Profile successfully created!");
}
}
return $this->render(':User/Profile:edit_profile.html.twig', [
'EditProfileForm' => $form->createView(),
]);
}
}
My EditProfileFormType:
class EditProfileFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', ChoiceType::class, array(
'choices' => array(
'Mr' => 'Mr',
'Mrs' => 'Mrs'
)
))
->add('firstName')
->add('lastName')
->add('street')
->add('postalcode', NumberType::class)
->add('city')
->add('telephone')
->add('mobile')
->add('company')
->add('birthday' , BirthdayType::class)
->add('callback', CheckboxType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\UserProfile',
'validation_groups' => array('edit_profile')
]);
}
public function getBlockPrefix()
{
return 'app_bundle_edit_profile_form_type';
}
}
So the problem here seems to be the not numeric string value in the databse
'34125abc'
which is stored in the $profile entity object and is passed to the form by $form->setData($profile); So when the data is being set, the error is thrown because of the Numbertype in this line ->add('postalcode', NumberType::class). Is there a way to pass the postalcode value to the form, even if it's not numeric and only check the Numbertype, when the form is submitted? Because i don't need validation, when I pass data to the form. Just when, it's submitted.
The solution was quite simple, but hard to find out.. I changed
->add('postalcode', NumberType::class)
to
->add('postalcode', TextType::class)
in my EditProfileFormType.php. Why? Because the form builder just need to know the type of the field in the database. It's shouldn't care in this case if it is numeric or not, because thats the task of the model restriction. In this case it is a string, so it is Texttype in a form. All the form type's are applied, when the form is set, but the validation groups are just validated, when the form is submitted! That's exactly they way it should be!
I have the exactly same problem as described here: Optional embed form in Symfony 2:
I have a form for the entity Person that has an embedded form for the entity Phone. The user can leave all fields of Phone empty and the form will be valid. But if a single field of Phone was filled-in, all Phone-fields must be valid.
During my first approach, I simply annotated the Phone property of Person with #Assert\Valid() without #Assert\NotNull(). That works fine only when entering a new Person. When editing an existing Person and the Phone property was already filled-in, the deletion of all Phone fields (which should be valid) does not result into a valid submit.
The validation of this solution with a validation callback function works with some modifications for Symfony 3:
/**
*
* #Assert\Callback()
*/
public function validatePhone(ExecutionContextInterface $context)
{
if (/* Fields are not empty */)
{
$context->getValidator()->inContext($context)->validate($this->phone);
}
}
But after submitting the form, validation errors for the phone fields are not shown on the page. I can only see them in the debug toolbar.
Maybe, this solution needs to be modified somehow, to let the errors be displayed after form submission?
But maybe even my first approach might work, if it is somehow possible to set the property Phone of an existing Person object to null, if all form fields of Phone have been cleared?
try to use cascade_validation (be carefull removed from symfony3) and error_bubbling in your formType class
->add('phone', 'collection', array(
'type' => 'text',
'allow_add' => true,
'error_bubbling' => false,
'cascade_validation' => true,
));
Found the answer myself:
The modification of the solution from the other post that I made in order try to let it work for Symfony 3.3 needs to be differently:
/**
*
* #Assert\Callback()
*/
public function validatePhone(ExecutionContextInterface $context)
{
if (/* Fields are not empty */)
{
$context->getValidator()->validate($this->phone);
}
}
I needed a solution to an non-class form, what I've done is:
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder
->add('status', TextType::class)
->add('invoice', SomeSubForm::class, [
'required' => false
]);
;
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$data = $event->getData();
if (!isset($data['invoice'])) {
$event->getForm()->add('invoice', HiddenType::class, [
'required' => false
]);
}
});
}
SubForm has NotBlank asserts on properties.
I have a form that contain 2 dates: start date(datedebut) and end date(datefin).
I want the end date to be always after the start date. How can i do that?
My form type:
class ReservationType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('datedebut',DateType::class,array(
'widget' => 'choice',
'years' => range(date('Y'), date('Y')+20),
))
->add('datefin',DateType::class,array(
'widget' => 'choice',
'years' => range(date('Y'), date('Y')+20),
))
->add('nbplaces')
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Bridge\TravelBundle\Entity\Reservation'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'Bridge_TravelBundle_Reservation';
}
}
You can use a Callback Validator for this. Injected into that callback is a ExecutionContextInterface by which you can access the form, and thus other form params.
Here's an example:
use Symfony\Component\Validator\Constraints;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
// …
$builder
->add('start', 'datetime',
'constraints' => [
new Constraints\NotBlank(),
new Constraints\DateTime(),
],
])
->add('stop', 'datetime', [
'constraints' => [
new Constraints\NotBlank(),
new Constraints\DateTime(),
new Constraints\Callback(function($object, ExecutionContextInterface $context) {
$start = $context->getRoot()->getData()['start'];
$stop = $object;
if (is_a($start, \DateTime::class) && is_a($stop, \DateTime::class)) {
if ($stop->format('U') - $start->format('U') < 0) {
$context
->buildViolation('Stop must be after start')
->addViolation();
}
}
}),
],
]);
Usually these kind of tasks are solved by adding validation constraints to check if value of one field is greater then the other. Implement callback validation constraint as stated in the documentation: http://symfony.com/doc/current/reference/constraints/Callback.html You can also create your custom class constraint validator and place validation logic there: http://symfony.com/doc/current/validation/custom_constraint.html
This way whenever a user tries to submit value of datefin which is less than selected value of datedebut he will see a validation error and the form will not be processed.
After that you can always add some javascript code that will filter available dates in datefin field after value in datedebut field is changed.
Also you can use dynamic form modification to render the second date field (and filter its available dates on server side) only if value of the first one is submitted. Check this out: http://symfony.com/doc/current/form/dynamic_form_modification.html
I'm creating a simple list of shop carts with users and products assigned to it.
My form for new cart looks like this:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('cartName', 'text', array('label' =>'Nazwa koszyka:'))
->add('user', new UserForm(), array('data_class' => 'Zadanie\Bundle\Entity\User', 'label' => false))
->add('products','entity', array('label' => 'Wybierz produkty:', 'class' =>'Zadanie\Bundle\Entity\Product' , 'multiple' => true, 'required' => true))
->add('Zapisz', 'submit');
}
and everything is great except that i can submit the form even without selecting any product.
By far i just added "required" by jquery, but i don't like that. Can somebody explain to me why it is not working properly? :P
EDIT:
Here is the code from controller:
/**
* #Route("/cart/edit/{id}",name="_edit_cart")
* #Template()
*/
public function editAction($id, Request $request)
{
$cart = $this->getDoctrine()->getRepository('ZadanieBundle:Cart')->find($id);
if($cart == null)
{
throw $this->createNotFoundException('Nie znaleziono rekordu');
}
$form = $this->createForm(new CartForm(), $cart);
$form->handleRequest($request);
if($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$data = $form->getData();
$em->persist($data);
$em->flush();
$this->get('session')->getFlashBag()->set('message', 'Koszyk zaktualizowano.');
return $this->redirect($this->generateUrl('_main_carts'));
}
return array('form' => $form->createView());
}
SECOND EDIT:
i found a SOLUTION, ( don't know if the best, but works :) ) so if anybody encounters that:
You have to create your validation file ( validation.yml for example) under YourBundle/Resources/config, in which you have to put information about properties. In my case it was:
Zadanie\Bundle\Entity\Cart:
properties:
cartname:
- NotBlank: ~
user:
- NotBlank: ~
constraints:
- Callback:
methods:
- [Zadanie\Bundle\Form\MyValidator, isUserValid]
and then i created MyValidator:
namespace Zadanie\Bundle\Form;
use Symfony\Component\Validator\ExecutionContextInterface;
use Zadanie\Bundle\Entity\Cart;
class MyValidator {
public static function isUserValid(Cart $cart, ExecutionContextInterface $context)
{
if(!$cart->getUser()->getName())
$context->addViolationAt('name', 'Proszę podać imię.', array(), null);
if(!$cart->getUser()->getSurname())
$context->addViolationAt('surname', 'Proszę podać nazwisko.', array(), null);
if(count($cart->getProducts()) == 0)
$context->addViolationAt('products', 'Proszę wybrać produkt.', array(), null);
}
}
#Mati, regarding your first question about how the required option works, this option only sets the required attribute in HTML5 so does not do anything server side. From the documentation
As of HTML5, many browsers can natively enforce certain validation
constraints on the client side. The most common validation is
activated by rendering a required attribute on fields that are
required. For browsers that support HTML5, this will result in a
native browser message being displayed if the user tries to submit the
form with that field blank.
Regarding your solution, that will certainly work though you may want to consider relying on the built-in validators. I'm fairly sure the product count constraint can use the built-in Count Collection constraint.
I got a problem with a dynamic form on symfony2. I'm trying to generate some fields for a submitted form. In others words, the user enters some values, submits the form, and according to these values, my dynamics fields are added to this same form (which is, obviously, displayed a second time). To do that, I used this example from the cookbook : http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data
So, here is my FormationType class
class FormationType extends AbstractType
{
private $em;
private $context;
public function __construct($em, $context) {
$this->em = $em;
$this->context = $context;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('date')
->add('type', 'choice', array(
'mapped' => false,
'choices' => Formationlist::getTypeTypes(false),
'empty_value' => false,
))
->add('cost')
->add('travelCost')
->add('maximum')
->add('location')
->add('schedule')
;
$formModifier = function(FormInterface $form, $type) {
$formationList = $this->em->getRepository('CoreBundle:FormationList')->findBy(array("year" => 1, "type" => $type));
$form->add('formationList', 'entity', array(
'label'=> 'Titre formation',
'choices' => $formationList,
'class' => 'CoreBundle:FormationList',
'property' => 'title',)
);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function(FormEvent $event) use ($formModifier) {
$data = $event->getForm();
$type = $data->get('type')->getData();
$formModifier($event->getForm(), $type);
}
);
$builder->get('type')->addEventListener(
FormEvents::POST_SUBMIT,
function(FormEvent $event) use ($formModifier) {
$type = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $type);
}
);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'EXAMPLE\CoreBundle\Entity\Formation'
));
}
public function getName()
{
return 'example_corebundle_formationtype';
}
}
So, the two addEventListener work pretty well. The first time my form is displayed, the field in formModifier is not loaded, as expected. My controller class is the following one :
public function createAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$contextSrv = $this->get('example.service.context');
$context = $contextSrv->getContext();
$entity = new Formation();
$form = $this->createForm(new FormationType($em, $context), $entity);
$form->bind($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('formation_show', array('id' => $entity->getId())));
}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
Since one of my dynamic field can't be null, the first time the form is submitted, it can't be valid. So, the FormationType is loaded a second time. That means, if the field "type" was filled, my formModifier() function can load the dynamic field (formationList). Until there, everything works pretty well, and I got my new field.
But, after a second "submit" on the form...nothing happen. The page is just reloaded, and no errors are displayed.
I checked the form content with
var_dump($request->request->get('example_corebundle_formationtype'));
-> Every fields (including the dynamic one) are filled with valid values.
I also try this :
foreach($form->all() as $item) {
echo $item->getName();
var_dump($item->getErrors());
}
-> These lines don't show any error. But, the form is never valid.
var_dump($form->isValid());
-> It returns false. So the form is invalid.
Finally, if I remove the whole dynamic part, my form works.
I don't understand what's wrong. There is no errors displayed by the form, and the csrf token seems right. Did I miss something ? Thanks for your help.
I know this is a bit outdated but comes up quite high on Google.
The getErrors() metod returns only Form's global errors not error messages for the underlying fields, you need either getErrors(true) or more sophisticated method when using embeded forms in a form. Please see: https://knpuniversity.com/blog/symfony-debugging-form-errors for more information.
There is probably a validation error lying somewhere in your form.
Instead of your complicated calls to Form::getErrors() - which is not fully recursive, as the errors of any field deeper than the 2nd level will not be displayed - you should use Form::getErrorsAsString().
This is a debug method created by the Symfony guys for developers such as you, trying to understand where a validation error could lie in complex forms.
If no error is displayed although it should, this may be a form theming error. When creating a custom form theme, it is possible that a developper overrides or forgets to display the error block of a field.
Another possible source of the problem lies is the general display of the form. If you display your form using {{ form_widget(form) }}, then any error that bubbles to the top form will never be displayed. Make then sure that you use {{ form_row(form) }} instead.
I also encountered this problem a few times.
In my case I posted data in JSON format, so I had to do a request listener with a high priority which transforms json data into normal POST data, which is available in $request->request.
One scenario where the $form is invalid and there is no errors in also when the post data is empty, try to make a dump of $request->request->all() to see if you have the data.