I don't know why, but I remembered that when you have a symfony form with required fields not rendered, the isValid function do not check for them.
I mean it only check if the widget got a value when you use it. And it seem in my current project that it is wrong.
Is there a setting or something allowing to do this ?
Edit : Here is the form :
class DemandeForm extends BaseDemandeForm
{
public function configure()
{
$this->widgetSchema['DEMANDE_DESC'] = new sfWidgetFormTextarea();
$this->validatorSchema['DEMANDE_DESC'] = new sfValidatorString(array('required' => true),
array("required" => str_replace('$1','Description de la demande',MessagePeer::getFrameworkMessageFromNum(7))));
$this->validatorSchema['DEMANDE_LIB'] = new sfValidatorString(array('required' => true),
array("required" => str_replace('$1','Libelle de la demande',MessagePeer::getFrameworkMessageFromNum(7))));
$this->widgetSchema['TIERS_ID'] = new crfcWidgetAutocomplete();
$this->validatorSchema['TIERS_ID'] = new sfValidatorString(array('required' => true),
array("required" => str_replace('$1','Tiers',MessagePeer::getFrameworkMessageFromNum(7))));
$bat_choices = $this->getOption('bat_choices');
$this->widgetSchema['BAT_CODE'] = new sfWidgetFormChoice(array('choices' => $bat_choices , 'expanded' => false, 'multiple' => false));
$this->validatorSchema['BAT_CODE'] = new sfValidatorChoice(array('choices' => array_keys($bat_choices),'required' => false)
);
$local_choices = $this->getOption('local_choices');
$this->widgetSchema['LOCAL_CODE'] = new sfWidgetFormChoice(array('choices' => $local_choices, 'expanded' => false, 'multiple' => false));
$this->validatorSchema['LOCAL_CODE'] = new sfValidatorChoice(array('choices' => array_keys($local_choices),'required' => false)
);
$site_choices = $this->getOption('site_choices');
$this->widgetSchema['SITE_CODE'] = new sfWidgetFormChoice(array('choices' => $site_choices, 'expanded' => false, 'multiple' => false));
$this->validatorSchema['SITE_CODE'] = new sfValidatorChoice(array('choices' => array_keys($site_choices),'required' => false)
);
$ress = Array(""=>"") + EnumereValeurPeer::getByType('FONCTIONS',true);
$this->widgetSchema['FONCTION_EVAL_NUMINT'] = new sfWidgetFormChoice(array('choices' => $ress , 'expanded' => false, 'multiple' => false));
$this->validatorSchema['FONCTION_EVAL_NUMINT'] = new sfValidatorChoice(array('choices' => array_keys($ress),'required' => false),
array("required" => str_replace('$1','Fonction demandeur',MessagePeer::getFrameworkMessageFromNum(7))));
$ress = Array(""=>"") + EnumereValeurPeer::getByType('CIRCONSTANCE',true);
$this->widgetSchema['CIRCONSTANCE_EVAL_NUMINT'] = new sfWidgetFormChoice(array('choices' => $ress, 'expanded' => false, 'multiple' => false));
$this->validatorSchema['CIRCONSTANCE_EVAL_NUMINT'] = new sfValidatorChoice(array('choices' => array_keys($ress),'required' => false),
array("required" => str_replace('$1','Circonstance de la demande',MessagePeer::getFrameworkMessageFromNum(7))));
$ress = Array(""=>"") + EnumereValeurPeer::getByType('TRAVAUX',true);
$this->widgetSchema['CATEGORIE_EVAL_NUMINT'] = new sfWidgetFormChoice(array('choices' => $ress , 'expanded' => false, 'multiple' => false));
$this->validatorSchema['CATEGORIE_EVAL_NUMINT'] = new sfValidatorChoice(array('choices' => array_keys($ress),'required' => false),
array("required" => str_replace('$1','Catégorie de travaux',MessagePeer::getFrameworkMessageFromNum(7)))
);
$ress = Array(""=>"") + EnumereValeurPeer::getByType('QUALIFICATION',true);
$this->widgetSchema['QUALIFICATION_EVAL_NUMINT'] = new sfWidgetFormChoice(array('choices' => $ress , 'expanded' => false, 'multiple' => false));
$this->validatorSchema['QUALIFICATION_EVAL_NUMINT'] = new sfValidatorChoice(array('choices' => array_keys($ress),'required' => true),
array("required" => str_replace('$1','Qualification',MessagePeer::getFrameworkMessageFromNum(7)))
);
$ress = Array(""=>"") + EnumereValeurPeer::getByType('PROGRAMME',true);
$this->widgetSchema['PROGRAMME_EVAL_NUMINT'] = new sfWidgetFormChoice(array('choices' => $ress , 'expanded' => false, 'multiple' => false));
$this->validatorSchema['PROGRAMME_EVAL_NUMINT'] = new sfValidatorChoice(array('choices' => array_keys($ress),'required' => true),
array("required" => str_replace('$1','Programme',MessagePeer::getFrameworkMessageFromNum(7)))
);
$ress = Array(""=>"") + EnumereValeurPeer::getByType('SUPPORT',true);
$this->widgetSchema['SUPPORT_EVAL_NUMINT'] = new sfWidgetFormChoice(array('choices' => $ress , 'expanded' => false, 'multiple' => false));
$this->validatorSchema['SUPPORT_EVAL_NUMINT'] = new sfValidatorChoice(array('choices' => array_keys($ress),'required' => false),
array("required" => str_replace('$1','Support',MessagePeer::getFrameworkMessageFromNum(7))));
$this->widgetSchema['COMMENTAIRE'] = new sfWidgetFormTextarea();
$this->validatorSchema['COMMENTAIRE'] = new sfValidatorString(array('required' => false));
$this->widgetSchema['DEMANDE_DTDEM'] = new myWidgetFormRichDate();
$this->validatorSchema['DEMANDE_DTDEM'] = new sfValidatorDate(array('required' => true, 'date_format'=>'#(?P<day>\d{2})/(?P<month>\d{2})/(?P<year>\d{4})#'),
array('required'=> str_replace('$1','Date de la demande',MessagePeer::getFrameworkMessageFromNum(7)),
'bad_format' => str_replace('$1','Date de la demande',MessagePeer::getFrameworkMessageFromNum(19)),
'invalid' => str_replace('$1','Date de la demande',MessagePeer::getFrameworkMessageFromNum(19))));
$this->widgetSchema['DEMANDE_DTFIN'] = new myWidgetFormRichDate();
$this->validatorSchema['DEMANDE_DTFIN'] = new sfValidatorDate(array('required' => true, 'date_format'=>'#(?P<day>\d{2})/(?P<month>\d{2})/(?P<year>\d{4})#'),
array('required'=> str_replace('$1','Date de fin des travax souhaitée',MessagePeer::getFrameworkMessageFromNum(7)),
'bad_format' => str_replace('$1','Date de fin des travax souhaitée',MessagePeer::getFrameworkMessageFromNum(19)),
'invalid' => str_replace('$1','Date de fin des travax souhaitée',MessagePeer::getFrameworkMessageFromNum(19))));
$this->widgetSchema['DEMANDE_ACCESSIBILITE'] = new sfWidgetFormInputCheckbox();
$this->validatorSchema['DEMANDE_ACCESSIBILITE'] = new sfValidatorBoolean(array('required' => false));
$this->widgetSchema['DEMANDE_URGENT'] = new sfWidgetFormInputCheckbox();
$this->validatorSchema['DEMANDE_URGENT'] = new sfValidatorBoolean(array('required' => false));
$this->widgetSchema['DEMANDE_INCENDIE'] = new sfWidgetFormInputCheckbox();
$this->validatorSchema['DEMANDE_INCENDIE'] = new sfValidatorBoolean(array('required' => false));
$this->validatorSchema->setOption('allow_extra_fields' , true);
$this->validatorSchema->setOption('filter_extra_fields' , false);
}
}
Edit 2 :
The question is : is it normal than the DEMANDE_DTDEM field required, raise a required form field error, whereas he hadn't been rendered ?
Shouldn't a form field be validated only when he was used ?
Yes, it's the normal behaviour. Fields VALIDATION has nothing to do with rendering.
If you don't want to render a widget you must explicitly remove both widget and validator (with unset or useFields).
If you define a widget and a validator (like in your case), but you don't call to $form['DTDEM']->render(), the value will be always empty.
IT'S VERY IMPORTANT THAT YOU UNDERSTAND THAT. In this case, the field is required, and then become that error, but if it wasn't required, the $form->save() method will always clean the value of the object in your database!
NEVER leave the widgets unrendered. You should do some echo $form while debugging to see what widgets are configured, and if you don't want some of them, just remove them in the form configure() method (as I said, with unset or useField)
I wish to add a complement to the glerendegui's answer.
What he is said is true in the general case. But, there is an element game changing. The form's option filter_extra_fields(false) alter significantly the rules. And is the reason for my original misunderstanding.
Because with this option set to false, the $form->save() will not set empty values in your objets, when you widget have not been rendered.
This is an important addition, because in all legacy code i have inherited, the use of this option was common, and unchallenged.
Related
I think this is a easy one.
I have a Silex application, with Symfony Form (with Validator).
This is a portion of my form:
return $this->factory->createBuilder(FormType::class)
->add('holidayId', HiddenType::class, array())
->add('firstName', TextType::class, array(
'label' => 'Il tuo nome'
))
->add('lastName', TextType::class, array(
'label' => 'Il tuo cognome'
))
->add('email', EmailType::class, array(
'label' => 'La tua email',
'constraints' => array(
new Assert\NotBlank(),
new Assert\Email()
),
'invalid_message' => 'Indirizzo email non valido',
))
->add('phone', TextType::class, array(
'label' => 'Cellulare',
'constraints' => array(
new Assert\NotBlank(),
new Assert\Regex("/[\d\-\ ]+/"),
new Assert\Length(array('min' => 5, 'max'=>20))
)
))
->add('city', ChoiceType::class, array(
'placeholder' => 'Seleziona una città',
'choices' => $cities,
))
->add('age', NumberType::class, array(
'label' => 'Età',
'constraints' => array(
new Assert\NotBlank(),
new Assert\Range(array('min'=>18, 'max'=>100))
)
))
->add('gender', ChoiceType::class, array(
'label' => 'Sesso',
'choices' => array(
'Uomo' => 0,
'Donna' => 1
),
'expanded' => true,
'constraints' => array(
new Assert\NotBlank(),
new Assert\Choice(array(0, 1))
)
))
->add('privacy', ChoiceType::class, array(
'choices' => array(
'Dichiaro di aver preso visione della privacy policy e autorizzo il trattamento dei dati personali per le finalità di cui ai punti a) b) c) della stessa.' => 1,
),
'expanded' => true,
'multiple' => true
))
->add('marketing', ChoiceType::class, array(
'choices' => array(
'Presto il consenso per l’utilizzo e la comunicazione dei miei dati a terzi da parte di P&R Eventi e Vacanze s.r.l. per finalità promozionali relative ai servizi turistici e per la ricezione di offerte commerciali.' => 1,
),
'expanded' => true,
'multiple' => true,
'required' => false
))
->add('send', SubmitType::class, array(
'label' => 'INVIA RICHIESTA',
'attr' => array('class' => 'btn btn-corporate btn-lg btn-block'),
))
->getForm();
Which returns an instance of Form.
On my controller I have:
$form = $requestType->getForm();
$form->handleRequest($request);
if($form->isValid()){
$data = $form->getData();
// .....
return $app->redirect('/thank-you');
}
The validation works fine, it redirects me when the form is valid.
On my twig view I render the form:
{{ form_start(form) }}
{{ form_errors(form) }}
<div class="form-group">
{{ form_label(form.firstName) }}
{{ form_widget(form.firstName, {'attr': {'class': 'form-control'}}) }}
</div>
<div class="form-group">
{{ form_label(form.lastName) }}
{{ form_widget(form.lastName, {'attr': {'class': 'form-control'}}) }}
</div>
<div class="form-group">
{{ form_label(form.email) }}
{{ form_widget(form.email, {'attr': {'class': 'form-control'}}) }}
</div>
...
The form_errors method doesn't show anything if the form isn't valid. Is ever BLANK ....
What's wrong?
Thank you in advance!
M.
I think that form doesn't contain childs errors by default
Add errors output for each element
<div class="form-group">
{{ form_label(form.firstName) }}
{{ form_errors(form.firstName) }}
{{ form_widget(form.firstName, {'attr': {'class': 'form-control'}}) }}
</div>
Or add special type for this form and change builder to pass all errors to view
namespace Form;
class Edit extends AbstractType
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
parent::buildView($view, $form, $options);
$view->vars = array_replace($view->vars, array(
'errors' => $form->getErrors(true)
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder
->add('holidayId', HiddenType::class, array())
->add('firstName', TextType::class, array(
'label' => 'Il tuo nome'
))
...
}
}
....
$form = $app['form.factory']->create(\Form\Edit::class, [], []);
I am having a little trouble adding validators to a ZF2 form object. I an building a form from a schema set in a database so I can validate a set of user input quickly.
This is the code that generates the form object
//initialise the form
$form = new Form();
//need to loop through the schema to create the form
for($i=0; $i < count($schema); $i++)
{
$form_options = array();
//add the basics to the form
$form_options['name'] = $schema[$i]['field_name'];
$form_options['type'] = $schema[$i]['field_type'];
//check if this is a required field
if($schema[$i]['is_required'] == 'true')
{
$form_options['required'] = true;
}
//add functions to filter the input form
$function_filters = explode(',', $schema[$i]['function_filter']);
if(!empty($function_filters))
{
$filters = array();
for($j=0; $j < count($function_filters); $j++)
{
$filters = array('name' => $function_filters[$j]);
}
$form_options['filters'] = $filters;
}
//add validators to the field array
$validators = array();
if(!is_null($schema[$i]['min_length']) && !is_null($schema[$i]['max_length']))
{
$validators[] = array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => (int) $schema[$i]['min_length'],
'max' => (int) $schema[$i]['max_length'],
)
);
}
//our regex validator if it exists
if(!is_null($schema[$i]['regex_filter']) || strlen($schema[$i]['regex_filter']) != 0)
{
$validators[] = array(
'name' => 'regex',
'options' => array(
'pattern' => $schema[$i]['regex_filter'],
'messages' => array(
\Zend\Validator\Regex::INVALID => $schema[$i]['regex_invalid'],
\Zend\Validator\Regex::NOT_MATCH => $schema[$i]['regex_not_match'],
\Zend\Validator\Regex::ERROROUS => $schema[$i]['regex_errorus'],
)
)
);
}
//only add the validators if theres something in there
if(!empty($validators))
{
$form_options['validators'] = $validators;
}
$form->add($form_options);
}
//return our form object
return $form;
The block of code behaves the way I expect it, the output of $form_option after it's performed the above code looks like this:-
Array
(
[name] => username
[type] => Text
[required] => 1
[filters] => Array
(
[name] => StripTags
)
[validators] => Array
(
[0] => Array
(
[name] => StringLength
[options] => Array
(
[encoding] => UTF-8
[min] => 3
[max] => 50
)
)
[1] => Array
(
[name] => regex
[options] => Array
(
[pattern] => /^[a-zA-Z0-9_]{3,50}$/
[messages] => Array
(
[regexInvalid] => L_REGEX_INVALID
[regexNotMatch] => L_REGEX_USERNAME
[regexErrorous] => L_REGEX_ERRORUS
)
)
)
)
)
When I came to test it, it ignores the StringLength & Regex validators entirely, and only pay attention to the required validator for the last form element.
Anyone have any idea what's gone wrong?
I have event added to onBootstrap()
$e->getApplication()->getEventManager()->getSharedManager()->attach('\Application\Form\Presentation\Edit', 'init', function($e) {
exit(print_r('<pre>') . var_dump($e));
}, 1);
How I can run this event after call form method init ?
I have solution,
In onBootstrap()
$services = $e->getApplication()->getServiceManager();
$sharedEventManager = $e->getApplication()->getEventManager()->getSharedManager();
$sharedEventManager->attach('PresentationEdit','init', function($event) use ($services) {
$form = $event->getTarget();
$form->add(array(
'type' => '\Zend\Form\Element\Text',
'name' => 'title2',
'attributes' => array(
'type' => 'text',
'class' => 'form-control',
'autocomplete' => 'off',
),
'options' => array(
'label' => 'presentation.title',
)
));
}, 100);
In form:
use Zend\EventManager\EventManager;
In init method:
$event = new EventManager('PresentationEdit');
$event->trigger('init', $this);
It works, but is this the correct approach?
I'm building a simple form mailer in symfony using sfExtraForm plugin captcha. Everything works except sfValidator is not redirecting the user and showing an error when any field is invalid. The only thing I can think of is I did not generate the module. I built it from scratch.
I have to do the redirect manually. Is this because this is a sfForm and not an sfDoctrine Form? It is if the line if ($form->isValid()) returns false but without the associated redirect and error codes. Here is the program. Thanks:
Form:
<?php
class AdsForm extends sfForm
{
public function configure()
{
$this->setWidgets(array(
'name' => new sfWidgetFormInputText(),
'email' => new sfWidgetFormInputText(),
'phone' => new sfWidgetFormInputText(),
'fax' => new sfWidgetFormInputText(),
'attention' => new sfWidgetFormInputText(),
'message' => new sfWidgetFormInputText(),
'captcha' => new sfWidgetFormReCaptcha( array('public_key'=>self::PUBLIC_KEY)),
));
$this->widgetSchema->setNameFormat('ads[%s]');
$this->setValidators(array(
'name' => new sfValidatorString(array('max_length' => 50)),
'email' => new sfValidatorString(array('max_length' => 50)),
'phone' => new sfValidatorString(array('max_length' => 50)),
'fax' => new sfValidatorString(array('max_length' => 50)),
'attention' => new sfValidatorString(array('max_length' => 50)),
'message' => new sfValidatorString(array('max_length' => 255)),
'captcha' => new sfValidatorReCaptcha(array('private_key' => self::PRIVATE_KEY))
));
$this->widgetSchema->setLabels(array(
'name' => 'Your Name:*',
'email' => 'Your Email:*',
'phone' => 'Your Phone:*',
'fax' => 'Your Fax',
'attention' => 'Attention:*',
'message' => 'Mail Message:*',
'captcha' => 'Image Verification:*',
));
}
}
Action:
class adsActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
$this->form=new adsForm();
}
public function executeSend(sfWebRequest $request){
$this->forward404Unless($request->isMethod(sfRequest::POST));
$form=new adsForm();
$captcha = array('recaptcha_challenge_field' => $request->getParameter('recaptcha_challenge_field'),'recaptcha_response_field' => $request->getParameter('recaptcha_response_field'),);
$form->bind(array_merge($request->getParameter('ads'), array('captcha' => $captcha)));
if ($form->isValid())
{
print_r($_REQUEST);
$ads=$this->getRequestParameter('ads');
print_r($ads);
$headers = 'From: '."\r\n" .
'Reply-To: no-reply#yellowexplorer.com' . "\r\n" .
'X-Mailer: PHP/' . phpversion();
//mail(' ******#yahoo.com','Yellow Explorer','An account has been created for you at yellowexplorer.com with a username of '.$this->username.'. Please click here to finish creating your account.',$headers);
}
else{
$this->redirect('/ads');
}
}
}
You're not seeing any errors because you're redirecting! To render form fields with their errors, you need to be rendering the actual form instance that you called bind on. Try changing $this->redirect('/ads') to $this->setTemplate('index').
Other potential changes (outside the scope of the question):
You can enforce the request method requirements of the send action at the routing level. Make the send route an sfRequestRoute and add the requirement { sf_method: post }.
Symfony has a built in mailer, sfMailer. Any reason you've decided not to use it?
I been trying all day to do multiple insert for the same form based on a number and i couldn't go any where with it. I hope someone here would be help me out...
am using admin generator on 1.4 doctrine. I have a form which i generated with only two fields. what am trying to do is, based on a number inserted the form will be repeated x number of times.
In the generator file I added a partial which placed a text field in the beginning of the form with default value of 1. If I choose 2 the form below gets duplicated twice..
Here is what i did to my form.. In action
class prizesActions extends autoPrizesActions
{
public function executeNew(sfWebRequest $request)
{
$this->form = $this->configuration->getForm(null, array('n' => 5));
$this->prizes = $this->form->getObject();
}
}
and in the PrizesForm, I wrote the following
class PrizesForm extends BasePrizesForm
{
public function configure()
{
$array = $this->getOptions();
for ($i = 0; $i < $array['n']; $i++) {
$this->setWidgets(array(
'id' => new sfWidgetFormInputHidden(),
'prize_no' => new sfWidgetFormInputText(),
'prize' => new sfWidgetFormInputText(),
'created_at' => new sfWidgetFormDateTime(),
'updated_at' => new sfWidgetFormDateTime(),
));
$this->setValidators(array(
'id' => new sfValidatorDoctrineChoice(array('model' => $this->getModelName(), 'column' => 'id', 'required' => false)),
'prize_no' => new sfValidatorInteger(array('required' => false)),
'prize' => new sfValidatorString(array('max_length' => 200, 'required' => false)),
'created_at' => new sfValidatorDateTime(),
'updated_at' => new sfValidatorDateTime(),
));
$this->widgetSchema->setNameFormat('prizes['.$i.'][%s]');
$this->errorSchema = new sfValidatorErrorSchema($this->validatorSchema);
}
unset( $this['updated_at'],
$this['created_at']
);
}
}
I think the loop is working but its over writing the widgets at every entry and i cannot find other method to append instead. Any ideas?
Thanks,
Did you try embedForm()? Code below should give you an idea.
class PrizesForm extends BasePrizesForm
{
public function configure()
{
$this->setWidgets(array(
'id' => new sfWidgetFormInputHidden(),
'prize_no' => new sfWidgetFormInputText(),
'prize' => new sfWidgetFormInputText(),
'created_at' => new sfWidgetFormDateTime(),
'updated_at' => new sfWidgetFormDateTime(),
));
$this->setValidators(array(
'id' => new sfValidatorDoctrineChoice(array('model' => $this->getModelName(), 'column' => 'id', 'required' => false)),
'prize_no' => new sfValidatorInteger(array('required' => false)),
'prize' => new sfValidatorString(array('max_length' => 200, 'required' => false)),
'created_at' => new sfValidatorDateTime(),
'updated_at' => new sfValidatorDateTime(),
));
$this->widgetSchema->setNameFormat('prizes[%s]');
}
}
class PrizesGroupForm extends sfForm
{
public function configure()
{
$array = $this->getOptions();
for ($i = 0; $i < $array['n']; $i++)
{
$this->embedForm('prizes_' . $i, new PrizesForm());
}
$this->widgetSchema->setNameFormat('prizes_group[%s]');
}
}