Bulk setting Symfony form field values with setData in SF4.4 - php

In my Symfony 4.4 application I have the following code inside my controller. I am attempting to pre-populate the form based on previous submissions or data pulled from the database. Importantly, the DetailsType form includes multiple entities so it's not a clean 1 entity per form setup here.
$postVars = $request->request->all();
$formData = [];
if (count($postVars) > 0) {
$formData = $postVars['crmbundle_register_details'];
}
$form = $this->createForm(DetailsType::class, $formData, [
'attr' => ['class' => 'reCaptchaForm'],
]);
$form->setData([
'firstname' => $person->getFirstname(),
'lastname' => $person->getLastname(),
'email' => $person->getEmail(),
'country' => $person->getCountry(),
'timezone' => $person->getTimezone()
]);
My problem is if I try to pre-populate the form with setData above it does not work.
If I do it individually as below it works, but I can't see why. I'd prefer to pass setData an array rather than call setData multiple times.
$form->get('firstname')->setData($user->getFirstname());
$form->get('lastname')->setData($user->getLastname());
$form->get('email')->setData($user->getEmail());
$form->get('country')->setData($user->getCountry());
$form->get('timezone')->setData($user->getTimezone());

If the form contains many entities, it's better to have an embed form for each entity. In this case, you don't need to call the setters at all. The controller action code will be:
$form = $this->createForm(DetailsType::class, $someParentEntity, [
'attr' => ['class' => 'reCaptchaForm'],
]);
$form->handleRequest($request);

Related

Validating Botdetect recaptcha in Symfony 4 Without using an entity

Is there any way to validate a BotDetect recaptcha in the symfony form builder?
I have the below form, which lets a user enter their email.:
$form = $this->createFormBuilder()
->add('email', EmailType::class,[
'label' => false,
'attr' => [
'style' => 'text-align:center;',
'value' => $email,
]
])
->add('captchaCode', CaptchaType::class, array(
'captchaConfig' => 'ExampleCaptcha'
))
->add('Do some shiz wif my email bruh.', SubmitType::class)
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
return $this->redirect('/unblock/'.$email);
}
The problem with this, is their documentation specifies a validation constraint in an Entity. My application does not have any entities (yet), but I would like to know if anyone has found a way to validate the captcha from the controller?
I'm fine with adding it to my entities when I create them , but I'm wondering how this would be done on an application that doesn't have any entities, or connection to a database.
I ended up using the beelabs google recaptcha bundle from packagist (If anyone is interested) at the link: https://packagist.org/packages/beelab/recaptcha2-bundle
Their documentation tells you pretty much everything you need to know about installation and setup.
The one drawback is that it still lets the form submit even though you havent clicked the I am not a robot checkbox, so you would need to validate whether it's been clicked on the PHP end.
You can use this to get the response, which is usually either a hash, or an empty field.
$recaptcha = $request->get('g-recaptcha-response', '');
use Captcha\Bundle\CaptchaBundle\Validator\Constraints\ValidCaptcha;
$form = $this->createFormBuilder()
->add('captchaCode', CaptchaType::class, [
'captchaConfig' => 'LoginCaptcha',
'constraints' => [
new ValidCaptcha([
'message' => 'CAPTCHA validation failed, try again.'
])
]
]);

How to make yii2 ActiveForm ignore previous submitted values?

I'm trying to make a simple search form (it will grow more complex soon so I'm using an ActiveForm here instead of simply passing GET parameters to the action method).
controller:
public function actionIndex()
{
$search_form = new UserSearchForm();
$search_form->load(Yii::$app->request->get(), $formName = '');
return $this->render('index', [
'search_form' => $search_form
]);
}
view:
<?php $form = ActiveForm::begin(['id' => 'search-form', 'method' => 'get']); ?>
<?= $form->field($search_form, 'q')->textInput(['name' => 'q']) ?>
<?= Html::submitButton('Search') ?>
<?php ActiveForm::end(); ?>
I'm using $formName = '' in controller and 'name' => 'q' in view to make the query string cleaner (simple q instead of UserSearchForm[q]).
Everything looks fine until first submit. I see a hidden q field in the form and after second submit the URL looks like /user?q=value1&q=value2, each submit adds another q to hidden fields. Is there a good way to get rid of those hidden fields? Or maybe the whole approach is wrong? I guess I'll need hidden fields there anyway (sorting, pagination etc).
You should simply set form action (if empty it will be the current url) :
<?php $form = ActiveForm::begin([
'id' => 'search-form',
'method' => 'get',
'action' => ['controller/index']
]); ?>
If you use for filtering the same action and controller as for displaying results, then
$form = ActiveForm::begin([
'id' => 'filter-form',
'method' => 'get',
'action' => Url::toRoute(\Yii::$app->request->getPathInfo())
]);

Symfony2 Embed form values

I have my own utility CalculatorType for just calculating values for my document(I am using ODM), not reference for another document or sub arrays|object.
It has simple 2 inputs:
$builder
->add('price', 'text', array(
'label' => false,
'data' => isset($options['data']) ? $options['data']->getPrice() : '0.00'
))
->add('count', 'integer', array(
'label' => false,
'data' => isset($options['data']) ? $options['data']->getCount() : '10000'
));
In parent form I have:
$builder->
... // multiple fields
->add('calculator', 'calculator');
So when I am trying to save my form, I have an error:
Neither the property "calculator" nor one of the methods
To skip setting calculator field, I've added mapped => false to options
->add('calculator', 'calculator', array('mapped' => false));
and added eventlistener to transform calculator data
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$data = $event->getData();
$data["price"] = $data["calculator"]["price"];
$data["count"] = $data["calculator"]["count"];
unset($data["calculator"]);
$event->setData($data);
});
Now form submits values but calculator fields not passing to embed form, because of unsetting $data['calculator']
If I comment unset($data["calculator"]); then I have an error
This form should not contain extra fields
So I can't find any way to make this form work. Any ideas?
Found mistake in my form, so there is option for such type of forms: http://symfony.com/doc/current/cookbook/form/inherit_data_option.html

Zend Form Edit and Zend_Validate_Db_NoRecordExists

I am slowly building up my Zend skills by building some utility websites for my own use. I have been using Zend Forms and Form validation and so far have been happy that I have been understanding the Zend way of doing things. However I am a bit confused with how to use Zend_Validate_Db_NoRecordExists() in the context of an edit form and a field that maps to database column that has to be unique.
For example using this simple table
TABLE Test
(
ID INT AUTO_INCREMENT,
Data INT UNIQUE
);
If I was simply adding a new row to the Table Test, I could add a validator to the Zend Form element for the Data field as such:
$data = new Zend_Form_Element_Text('Data');
$data->addValidator( new Zend_Validate_Db_NoRecordExists('Test', 'Data') )
At form validation this validator will check that the contents of the Data element does not already exist in the table. Thus the insert into Test can go ahead without violating the Data fields UNIQUE qualifier.
However the situation is different when editing an existing row of the Test table. In that case the validator needs to check that the element value meets one of two mutually exclusive conditions conditions:
The user has changed the element value, and the new value does not currently
exist in the table.
The user has Not changed the element value. Thus the value does currently exist in the table (and this is OK).
The Zend Validation Docs talk about adding a parameter to the NoRecordExists() validator for the purpose of excluding records from the validation process. The idea being to "validate the table looking for any matching rows, but ignore any hits where the a field has this specific value". Such a use case is what is needed for the validating the element when editing a table. The pseudo code to do this in 1.9 is like so (actually I got this from the 1.9 source code - I think the current docs may be wrong):
$data = new Zend_Form_Element_Text('Data');
$data->addValidator( new Zend_Validate_Db_NoRecordExists('Test', 'Data',
array ('field'=>'Data', 'Value'=> $Value) );
The problem is that the value that is to be excluded ($Value) is bound to the validator at the time it is instantiated (also when the form is instantiated). But when the form is editing a record, that value needs to be bound to the contents of the $data field when the form was initially populated with data - IE the Data value initially read from the Test table row. But in typical Zend patterns a form is instantiated and populated in two separate steps which precludes binding the exclude value to the desired element value.
The following Zend psuedo code marks where I would like the binding of $Value to the NoRecordExists() validator to occur (and note that this is a common Zend controller pattern):
$form = new Form()
if (is Post) {
$formData = GetPostData()
if ($form->isValid($formData)) {
Update Table with $formData
Redirect out of here
} else {
$form->populate($formData)
}
} else {
$RowData = Get Data from Table
$form->populate($RowData) <=== This is where I want ('value' => $Value) bound
}
I could sub-class Zend_Form and override the populate() method to do a one-shot insertion of the NoRecordExists() validator on initial form population, but that seems like a huge hack to me. So I wanted to know what other people think and is there some pattern already written down that solves this problem?
Edit 2009-02-04
I've been thinking that the only decent solution to this problem is to write a custom validator and forget about the Zend version. My form has the record ID as hidden field, so that given the table and column names I could craft some SQL to test for uniqueness and exclude the row with an ID of such an such. Of course this started me thinking about how I would be tying the form to the dB layer that the Model is supposed to hide!
This is how it's done:
I your FORM, you add this validator (for example email field):
$email->addValidator('Db_NoRecordExists', true, array('table' => 'user', 'field' => 'email'));
Don't add custom error message for this since after that it didn't work for me, e.g.:
$email->getValidator('Db_NoRecordExists')->setMessage('This email is already registered.');
In your Controller add this:
/* Don't check for Db_NoRecordExists if editing the same field */
$form->getElement('email')
->addValidator('Db_NoRecordExists',
false,
array('table' => 'user',
'field' => 'email',
'exclude' => array ('field' => 'id', 'value' => $this->request->get('id'))));
And after this you do verifications, e.g.:
if ($this->getRequest()->isPost())
{
if($form->isValid($this->getRequest()->getPost()))
{
....
That's it!
This will also work :
$this->addElement('text', 'email', array(
'label' => 'Your email address:',
'required' => true,
'filters' => array('StringTrim'),
'validators' => array(
'EmailAddress',
array('Db_NoRecordExists', true, array(
'table' => 'guestbook',
'field' => 'email',
'messages' => array(
'recordFound' => 'Email already taken'
)
)
)
)
));
After reviewing the overwhelming response I've decided that I'm going with a custom validator
Look at this one:
Answer raised by me and well-solved by Dickie
private $_id;
public function setId($id=null)
{
$this->_id=$id;
}
public function init()
{
.....
if(isset($this->_id)){
$email->addValidator('Db_NoRecordExists', false, array('table' => 'user', 'field' => 'email','exclude' => array ('field' => 'id', 'value' => $this->_id) ));
$email->getValidator('Db_NoRecordExists')->setMessage('This email is already registered.');
}
Now u can use:
$form = new Form_Test(array('id'=>$id));
You could just call $form->getElement('input')->removeValidator('Zend_Validator_Db_NoRecordExists'); instead of supplying the exclusion.
I have just tried this example for email address uniqueness and it works perfectly with below stuffs :
1] In my form:
// Add an email element
$this->addElement('text', 'email', array(
'label' => 'Email :',
'required' => true,
'filters' => array('StringTrim'),
'validators' => array(
'EmailAddress',
)
));
Here's something special that I needed to add for unique email address to work:
$email = new Zend_Form_Element_Text('email');
$email->addValidator('Db_NoRecordExists', true, array('table' => 'guestbook', 'field' => 'email'));
2] In my controller:
$form->getElement('email')
->addValidator('Db_NoRecordExists',
false,
array('table' => 'guestbook',
'field' => 'email',
'exclude' => array ('field' => 'id', 'value' => $request->get('id'))));
if ($this->getRequest()->isPost()) {
if ($form->isValid($request->getPost())) {
Hope it helps you people !
Thanks

Validate field in model with no table (CakePHP)

I've got a model in CakePHP that doesn't have a table, called Upload. I've got a validation in this Model for a field called source_id.
I've got a form that builds a nice looking $this-data, giving me a well formated set, including:
$this->data['Upload']['source_id']
However, the validation rule I have set doesn't seem to run at all. I copied this validation rule from another model where it does work, so I'm confident that it works:
var $validate = array(
'source_id' => array(
rule' => 'numeric',
'required' => true,
'allowEmpty' => false,
'message' => 'Error!.'
)
);
Can you not validate fields for a model that lacks a database table?
The form uses the Upload model, and submits to another controller action method.
CakePHP 1.2, PHP/MySQL 5, XAMPP.
I'm dumb. You have to trigger a validation check, either with a save() or
$this->Upload->set($this->data);
$this->Upload->validates();
Working now.
You can also fake the database structure by setting the $_schema array, like so:
var $useTable = false;
var $_schema = array(
'name' =>array('type'=>'string', 'length'=>100),
'email' =>array('type'=>'string', 'length'=>255),
'phone' =>array('type'=>'string', 'length'=>20),
'subject' =>array('type'=>'string', 'length'=>255),
'message' =>array('type'=>'text')
);

Categories