ZF2 refresh input filter after dynamicly adding elements to form - forms

I have a form that triggers on event in __construct method to load some items from another modules . So far so good , a field set is loaded from the other module and added to the form and in the request->getPost() I have the data for the elements inside the fieldset , but the $form->getData() doesn't have the data for the fieldset.
I am calling $form->getInputFilter() before adding this fieldsets to the form and it seems that calling the $form->getInputFilter() dosn't creates the filters for the newly added elements . so how can i create inputfilters for the dynamic events without recreating the hole filters again ?
Or should i just delay calling $form->getInputFilter() untill all of the elemnts have been added to the form ?

I also added some elements to the form later what was ignored by the input filter.
My solution is most likely not exactly the best one, but as you haven't received any other answers yet, here's what I did:
I added
use Zend\InputFilter\Factory as InputFactory;
in the class where I'm validating the form data and then used
$factory = new InputFactory();
$form->getInputFilter()->add($factory->createInput(array(
'name' => 'title_str',
'required' => true,
'filters' => array(
array('name' => 'Int'),
),
)));

#Afterdark017 that works and also i think it is possible to reset the filters.
protected function resetFilters(){
$this->filter = null;
$this->hasAddedInputFilterDefaults = false;
}
but i have not tested this yet.

Related

Symfony2 embedded forms + dynamic form update

I have a dropdown menu in my form and the form structure depends on its value. I have managed to solve the "form-update-issue" with event subscriber/listener class, where i am trying to update the main form according to dropdown's value.
The main problem is that i have to modify the form from values that persisted in the database.
My DB schema:
I have 4 table: Model, ModelCategory, ModelCategoryKey, ModelParameter.
ModelCategory 1--n Model 1--m ModelParameter
ModelCategory 1--n ModelCategoryKey
ModelCategoryKey 1--n ModelParameter
After the user choose a ModelCategory from the form's (form based on Model entity) dropdown i have to update the form with ModelParamater rows, but it's number and default values depends on ModelCategory 1--n ModelCategoryKey assocaiton.
I've tried to attach NEW ModelParameter entities to the main Model entity during the PRE_BIND event (also set their default values) and it seems working fine, but when i add the 'parameters' with a 'collection' typed element to the form i get the next error:
Entities passed to the choice field must be managed. Maybe persist them in the entity manager?
Clearly my entities can't be (and shouldn't be) persisted at this time.
All ideas are welcome!
UPDATE:
Modifying the form after preSubmit/preBind:
$form->add('parameters','collection',array(
'type' => new ModelParameterType(),
));
OR
$form->add(
$this->factory->createNamed('parameters','collection',null,
array(
'type' => new ModelParameterType()
))
);
where the 'factory' attribute is a FormFactoryInterface. The error message is the same.
UPDATE2:
Further investigation proved, that if i don't add "default" entities to the assocation. Then it works without error.
Here is the source of my form modifying method:
public function preSubmit(FormEvent $event) {
$form = $event->getForm();
$id = $event->getData()['modelCategory'];
$entity = $form->getData();
$categoryKeys = $this->em->getRepository('MyBundle:ModelCategoryKey')->findByModelCategory(
$this->em->getReference('MyBundle:modelCategory',$id)
);
foreach ($categoryKeys as $key) {
$param = new ModelParameter();
$param->setModel($entity);
$param->setKey($key);
$entity->addParameter($param);
}
$form->add(
$this->factory->createNamed('parameters','collection',null,
array(
'type' => new ModelParameterType(),
'allow_add' => true,
'cascade_validation' => true
))
);
}
SEEMS TO BE SOLVED BY
I have just commented out the $param->setModel($entity); line and it seems to be working fine. I will work this out more and will share the experience, if it realy works.
choice field accepts only managed entities, as the value is set to the entity after submit, and form posts only entities ID, so it must be saved beforehand.
You don't need choice field - you need collection of parameter subforms.
$formBuilder
->add('category', 'category_select')
->add('parameters', 'collection', array('type' => 'parameter'))
;
I'm assuming here that category_select is choice field with categories and parameter is subform with it's own values, depending on your parameter structure.
When you have category in your controller, you can bind the newly created entity with added Parameter entities with their key set, depending on ModelCategoryKey.
I've managed to solve my problem, so here it is what i've found out:
It is enough to add the newly created object by the adder function of the inverse side. I don't have to call the owning side's setter.
Inverse side adder function must be modified, that it calls the owning side's setter.
Inverse side adder function must check if the object is not in the collection already.
PRE_SET_DATA event happens, when the form is created. (so in new entities it is empty, and in old ones it is filled)

ZendFramework: Changing a form element after failed validation

So I have a form set up in the following manner:
In my forms directory:
Address.php
class Address extends Zend_Form{
// Creates an address input box including address/country/state/zip
// The states is created as a drop down menu
public function init() {
// relevant code to question
$this->addElements(array(
array('select', $names['state'], array(
'label' => "State",
'class' => 'state',
'multiOptions' => array('' => '') + AddressHelper::stateList(),
'required' => $this->_required,
)),
));
}
}
MyForm.php:
class MyForm extends Zend_Form {
public function init() {
//set-up some general form info
// this is the relevant part for my question
// $opt is a predefined variable
$this->addSubForms(array(
'info' => new SubForm($opts),
'mailing' => new Address($opts + array(
'legend' => 'Address',
'isArray' => false,
'required' => true,
)),
));
}
}
Survey.php
class Survey extends MyForm{
// initialize parent (MyForm) and add additional info for the Survey form
}
Okay, so when survey is submitted, if it fails validation, I need to change the Address state element from a select to an input type=text.
So in my controller, under the action that checks for validation I have the following:
public function createAction(){
if ($this->_form->isValid($post)) {
$this->_saveDraft($post, $this->_submissionType);
$this->addSessionMessage('Submission created!');
return $this->redirector->gotoRouteAndExit(array(), 'home', true);
}else{
/* IMPORTANT */
// I need to change the Address select field to a text field here!
$errors[] = 'There was a problem';
$this->view->assign(compact('form', 'errors', 'submission'));
$this->_viewRenderer->renderScript('update.phtml');
}
}
So, would I just create a method in the Address class and somehow call it to swap out. I'm just not sure how to go about this.
You would be looking at using removeElement() to remove the select element, and then addElement() to replace it with the text only version.
The problem you are going to have is that when the validation fails, the select element is changed to a text element and the form is re-displayed. Now, upon resubmission, you need to make the change again prior to calling isValid() because the form uses text input for state instead of select. So you need to make the change twice. Once after failed validation prior to re-displaying the form, and once prior to calling isValid(), but only if there was a previously failed submission.
Now why is it that if the form fails validation, you want the select element for state to be text? Can't it work just the same with a select element and you just pre-select the correct state for them?
EDIT:
You use the form object to call add/removeElement.
$removed = $form->getSubForm('mailing')->removeElement('state_select');
$form->getSubForm('mailing')->addElement($text_state_element);
That call should work to remove an element from a subform.
Without subforms, it is just:
$form->removeElement('username');
$form->addElement($someNewElement);
You can use getElement() in a similar way if you need to get an element from a form to make changes (e.g. remove/add validators, change description, set values)
$el = $form->getElement('username');
$el->addValidator($something)
->setLabel('Username:');
Hope that helps.

Creating a Zend_Validate object from an array

I have this in a Zend_Form's init method:
$username_validators = array(
'Alpha',
array('StringLength', false, array(3, 20)),
);
$some_form->addElement('text', 'username', array(
'filters' => array('StringTrim', 'StringToLower'),
'validators' => $username_validators,
'required' => true,
'label' => 'Username:',
));
Is it possible to create a Zend_Validate object that loads the same validators array that I'm passing addElement? It would be something like:
$v = new Zend_Validate();
//this is the part I'm unsure. Zend_Validate doesn't have an addValidators method.
$v->addValidators($username_validators);
echo $v->isValid('testuser1');
Sure you can add a collection of validators from a member variable, as long as they don't require any dynamic options that need to be specified at instantiation.
Edit
It appears to me that, out of the box, you cannot do something similar. Zend_Form has a plugin loader/registry that enables you to use "short forms" for validators. The plugin loader is configured with paths and class prefixes that allow it to actually create true validator instances from the short forms and any provided validator options.
In contrast, the code of Zend_Validate::addValidator() appears to actually require an actual validator instance.
But it looks like you could kind of piggyback on this form/element registry as follows: create a form element, assign short form validators to the element, call getValidators() on the element (Zend_Form_Element::getValidators() seems to convert each short form validator into a real instance), and then feed these validators one at a time into Zend_Validate. Seems to be a long way around, but it should work.
Yes, you can do what you want as long as $username_validators has been declared and is accessible in the scope of the function / class. If you are using a class, you will declare a private variable:
private $userVariables;
Then in the constructor populate it:
public function __construct()
{
$this->userVariables = array(
//validator options here
);
}
You can now assign this single validator as many times as you like by calling $this->userVariables:
$v = new Zend_Validate();
$v->addValidators($this->userVariables); //this is the part I'm unsure
echo $v->isValid('testuser1');

Zend Avoid submit button value in GET parameters in url

I have a form, created with Zend_Form, with method = GET used for searching records with elements as below:
[form]
user name [input type="text" name="uname"]
[input type="submit" value="Search" name="search"]
[/form]
After form is submitted all the GET parameters along with submit button value are appearing in the url.
http://mysite.com/users/search?uname=abc&search=Search
How to avoid submit button value appearing in the url? is custom routing the solution ?
When you create your element, you can simply remove the name attribute that was automatically set at creation
$submit = new Zend_Form_Element_Submit('search')->setAttrib('name', '');
Or inside a Zend_Form
// Input element
$submit = $this->createElement('submit', 'search')->setAttrib('name', '');
// Or Button element
$submit = $this->createElement('button', 'search')->setAttribs(array
(
'name' => '', 'type' => 'submit',
);
When a form gets submitted, all of its elements with their names and values become a part of a GET / POST - query.
So, if you don't want an element to appear in your GET - query, all you need to do is to create this element without a name. That's probably not the best approach, but since we're talking about the 'submit' element, I guess it doesn't matter that much.
Looking at Zend_View_Helper_FormSubmit helper, you can see that it's creating the 'submit' element and setting its name. So, the possible solution would be to create your own view helper and use it for rendering the 'submit' element instead of the default helper.
You can set a custom helper with
$element->setAttribs( array('helper' => 'My_Helper_FormSubmit') );
Then build your own form element class and remove the name attribute from the element with preg_replace. The beauty of it is, it will not interfere with the other decorators.
So the something like this:
class My_Button extends Zend_Form_Element_Submit
{
public function render()
{
return preg_replace('/(<input.*?)( name="[^"]*")([^>]*>)/', "$1$3", parent::render(), 1);
}
}
You can remove name attribute for submit button in javascript.
jQuery example:
$('input[name="submit"]').removeAttr('name');
In the controller that represents the form's action, redirect to another (or the same controller) only including the relevant params.
Pseudocode:
$params = $this->getRequest()->getParams();
if isset($params['search'])
unset($params['search']);
return $this->_helper->Redirector->setGotoSimple('thisAction', null, null, $params);
handle form here
This is basically the same idea as Post/Redirect/Get except that you want to modify the request (by unsetting a parameter) in between the different stages, instead of doing something persistent (the images on that Wiki-page shows inserting data into a database).
If I were you, I would leave it in. IMO it's not worth an extra request to the webserver.

Drupal select list only submitting first character

I have a select list I've created in a form alter, however, when I select an option and submit the value, only the first digit gets stored in the database. I know this has something to do with how the array is formatted, but I can't seem to get it to submit properly.
function addSR_form_service_request_node_form_alter(&$form, $form_state) {
$form['field_sr_account'] = array( '#weight' => '-50',
'#type' => 'select',
'#title' => 'Select which account',
'#options' => addSR_getMultiple());
//Custom submit handler
$form['#submit'][] = 'addSR_submit_function';
}
function addSR_submit_function{
$form_state['values']['field_sr_account'] = array('0' => array('value' => $form['#field_sr_account']));
Below is the function that returns the associative array. It is returning the proper options, as I can view the correct value/option in the HTML source when the page loads
//The values returned are not the problem, however, the format of the array could be..
function addSR_getMultiple(){
$return = array();
$return['one'] = 'Choice1'
$return['two'] = 'Choice2'
return $return;
}
Update:
Drupal 6: Only inserting first character of value to MySQL
I had a similar issue with the same field. However, in that case, I knew the value I wanted to submit, and I was able to assign the value to the field in the form alter, before the form was submitted. The difference with this issue, is that I don't know the value of the field until it is submitted, so I can't "assign" it in the form alter. How can I assign it the same way in the submit handler.
Edit after question update (and discovery of root problem within the linked separate question):
As you are trying to manipulate CCK fields, and those have pretty special handling mechanisms compared to 'standard' Drupal FAPI form elements, you should probably read up on CCK form handling in general, and hook_form_alter() and CCK fields and CCK hooks in particular. Glancing at those documentations (and the other CCK articles linked in the left sidebar), it looks like there should be a straight forward solution to your problem, but it might require some digging.
As a potential 'quick fix', you could try keeping your current approach, and adjust the submitted value on validation, somewhat like so:
function addSR_form_service_request_node_form_alter(&$form, $form_state) {
$form['field_sr_account'] = array(
'#weight' => '-50',
'#type' => 'select',
'#title' => 'Select which account',
'#options' => addSR_getMultiple()
);
// Add custom validation handler
$form['#validate'][] = 'addSR_validate_function';
}
function addSR_validate_function (&$form, &$form_state) {
// Assemble result array as expected by CCK submit handler
$result = array();
$result[0] = array();
$result[0]['value'] = $form_state['values']['field_sr_account'];
// Set this value in the form results
form_set_value($form['field_sr_account'], $result, $form_state);
}
NOTE: This is untested code, and I have no idea if it will work, given that CCK will do some stuff within the validation phase as well. The clean way would surely be to understand the CCK form processing workflow first, and manipulating it accordingly afterward.