Zend_Form Prepend Element - php

I can add an element to a form like this:
$form->addElement($element);
However, that will put the element at the end of the form, I would like to prepend an element (put it at the beginning of the form).
Why? The form has dynamically generated fields (number of text fields and their labels are generated based on parameters from request) so the form class looks like this:
class Form1 extends Zend_Form
{
public function init()
{
$this->setMethod('post');
$submit = new Zend_Form_Element_Submit('submit1', array(
'label' => 'Submit',
'class' => 'input-submit'
));
$this->addElements(array(
$submit
));
}
}
There is only the submit button because I don't know how many text fields and with what labels there will be yet.

From the ZF manual on Zend_Form Metadata and Attributes
Zend_Form_Element handles a variety of attributes and element metadata. Basic attributes include:
order: the index at which an element should appear in the form. Uses the setOrder() and getOrder() accessors.
So you would set the button to a very high order number, e.g. 1000 and then add the dynamic elements starting with an order number of 1 (or any number lower than the button's order number).
However, when there is nothing but the button in the form at all, then why not just create the entire form on the fly and append the submit button once you are done attaching the elements from the request.

Related

How do I render (get HTML of) a form object in Agile Toolkit?

I'm using PHP framework Agile Toolkit version 4.3.2 (latest at this moment). I'm on a page that extends the default Page class.
Suppose I have a form object like:
$form = $this->add('Form');
$form->addField('text', 'name', 'Name');
$form->addSubmit('Save');
How do I get the form object's HTML? I want to send the form's HTML to another template part, something like:
$this->template->trySetHTML('Content', $form);
The function from above works if I use HTML code instead of the $form object.
But in this case when I refresh the page, instead of the form HTML appears a string like: Object Form(22f8a7bc__ancedsearch_form)
I tried: $form->render() or $form->getHTML() but these functions don't work.
So please tell me, how do I render an object in agile toolkit? How can I get the object's HTML code.
Edit
I'm extending the grid layout. For each column I add a search filtering option. I have extended the Grid_Advanced.php in order to be able to customize it. On each column, below the table header (column name), I'm inserting a form with an input (I'm sending the column name field):
$header_col->trySetHTML('advance_search_filter', $form_html);
The $form_html is returned from a file that extends atk4/lib/Filter.php (it's similar to quicksearch). The quicksearch automatically adds the html to the grid, but in my case I need it to be added to the table's head, after the column name. That's why I am trying to get the HTML of the form.
In this file I have the init function that looks something like:
public function init()
{
parent::init();
$this->addClass('grid-extended-search atk-box ui-widget ui-widget-content');
$this->default_controller = 'Controller_..._MVCForm';
$this->template->trySet('fieldset', 'atk-row');
$this->bs = $this->addSubmit('Search');
$this->save = $this->bs;
}
But this doesn't return nothing so I created a function to return template's HTML. But the form was empty, so I recreated the fields (which is bad):
$m = $this->view->model
foreach($this->fields as $f) {
$field = $this->view->columns[$f];
if($m->hasField($f)) {
if($field['type'] == 'text') {
$field_html = $this->addField('line', $f, $field['descr']);
$form_html .= $field_html->getInput();
}
$this->template->setHTML('Content', $form_html);
}
}
Any idea? Why the form is empty? I used addField to add the fields to the existing but the fields probably exist. I don't know how to get them, to get the existing form's HTML.
For most views:
$html = $view->getHTML();
However it might be more tricky for a Form.
In the solution that you have described (after edit), it seems that you don't really need all the functionality of the Form
To get HTML of an individual Field:
$field->getInput();
That will give you the "input" element that you can place inside your column headers. You can also use "Form_Plain" to wrap your GRID inside a <form> tag.
You would need to handle submission manually, though.

How to validate nested fieldsets in Zend Framework 2 without using factory

I've got a form class that dynamically creates fieldsets with elements/fieldsets recursively. I do this to get settings[general][rpp][value] as an input name (for example). The fields get generated because the settings are user defined in an XML file.
Fieldsets are getting created like this:
$fieldset = new Fieldset(...);
$fieldset->add(...);
$form->add($fieldset);
The form is being output correctly; everything works. Except i need validation.
My goal is to define validators and filters for these nested elements. I'm really confused at how it works - but it looks like just the form itself defines an input_filter setInputFilter(...) and i don't know how to get it to recognize the recursion without a factory and proprietary classes for the fieldsets instead of being dynamic.
Am i clear?
Thanks.
I've figured out how to do this highly dynamic type of form with validation and filters. I will explain here with this hypothetical script:
// create a form instance and a filter instance
$form = new Form();
$filter = new InputFilter();
// create a fieldset instance and another filter instance
$fieldset_a = new Fieldset('general');
$fieldset_a_filter = new InputFilter();
// create element(s) to assign to fieldset
$setting_1 = new Element('setting_1');
// create another input filter for element defining filters and validators
$setting_1_filter = new InputFilter(array(
'name' => 'setting_1',
'required' => true,
'validators' => array(), // ...
));
// add element to fieldset
$fieldset_a->add($setting_1);
// add fieldset to form
$form->add($fieldset_a);
// add element filter to fieldset filter
$fieldset_a_filter->add($setting_1_filter,'setting_1');
// add fieldset A filter to main input filter
$filter->add($fieldset_a_filter,'general');
$form->setInputFilter($filter);
So you can see you have to create input filters for each set of elements and each fieldset and then work backwards through them adding them to each other until the main input filter is built and you can assign it to the form instance.
This then would use the supplied validators with input names such as general[setting_1] after running $form->setData($this->request->getPost()) - $form->isValid()
This response could be 100 times more detailed, but it's better than what is available on the subject of dynamic fieldset validation.
here, in the ZF2 docs is explained as well http://framework.zend.com/manual/2.3/en/modules/zend.form.collections.html

Processing Zend_Form dynamically generated elements

I need to create a form where the elements (texbox, select, ..) will be dynamically inserted. Right now I have created a empty Form file with just a hidden element and them in my controller I go inserting elements according to certain conditions.
My form file:
class Form_Questions extends Zend_Form {
public function __construct() {
parent::__construct($options);
$this->setName('Questions');
// Hidden Label for error output
$hiddenlabel = new Zend_Form_Element_Hidden('hiddenlabel');
$hiddenlabel->addDecorator(new Form_Decorator_HiddenLabel());
$this->addElements( array($hiddenlabel) );
}
}
In the controller I have something like:
...
$form = new Form_Questions();
$request = $this->getRequest();
if ($request->isPost())
{
$formData = $request->getPost();
if ($form->isValid($request->getPost()))
{
die(var_dump($form->getValues()));
}
}
else
{
//... add textbox, checkbox, ...
// add final submit button
$btn_submit = new Zend_Form_Element_Submit('submit');
$btn_submit->setAttrib('id', 'submitbutton');
$form->addElement($btn_submit);
$this->view->form = $form;
}
The form displays fine but the validation is giving me big trouble. My var_dump() only shows the hidden element that is staticly defined in the Form file. It does not save the dinamic elements so altought I can get them reading what's coming via POST, I can not do something like
$form->getValue('question1');
It behaves like if Zend uses the Form file to store the values when the submit happend, but since the elements are created dinamically they do not persist (either their values) after the post so I can not process them using the standar getValue() way.
I would appreciate any ideas on how to make them "live" til after the post so I can read them as in a normal form.
The form which you are calling isValid() and getValues() methods on is actually your "empty" form - you have instantiated it only a few lines up and haven't added any elements to it at that point.
Remember that POST only sends an array of fieldName => fieldValue type, it doesn't actually send a Zend_Form object.
It is difficult to suggest a new solution without knowing what you are trying to achieve. It is generally better to add all possible elements to your Zend_Form right away, and then only use the ones you need in the view scripts, i.e. echo $this->form->myField;. This will allow isValid() to process all the elements of the form.
It sounds like the form is dynamic in the sense that the questions come from a db, not in then sense that the user modifies the form itself to add new questions.
Assuming this is the case, then I wouldn't add the question fields in the controller. Rather, I'd pass the questions to the form in the constructor and then add the question fields and the validators in the form's init() method. Then in the controller, just standard isPost() and isValid() processing after that.
Or, if you are saying that the questions to be added to the form are somehow a consequence of the hidden label posted, then perhaps you need two forms and two actions: one for the hidden field form and another for the questions.
Ok, the simplest solution I came up with - to my case and considering the really of the code I am currently playing with was to load all the questions I need from the database using a method from my Model (something like fetchQuestions()), them in my controller I go throught the recordset and create the form elements according to the current question of the recordset.
The elements are stacked in an array that is passed to my Form constructor. In the form constructor I read the array and generate all the dynamic elements. I them just echoed the form to the view.
I have not seem why it would be a bad idea to override the Form constructor as I also could not use any of the set/get methods to pass this to my form.

How do I use a custom #theme function to a fieldset in a drupal module?

I have a module that builds a form that includes a fieldset. Instead of using the <legend> element to render the fieldset title, I want to place this content in a <div> element instead. But I want to change the behavior only for the form returned by my module, so I don't want to place any new functionality into my theme's template.php file.
In mymod.module I have defined:
// custom rendering function for fieldset elements
function theme_mymod_fieldset($element) {
return 'test';
}
// implement hook_theme
function mymod_theme() {
return array(
'mymod_fieldset' => array('arguments' => array('element' => NULL)),
'mymod_form' => array('arguments' => array())
);
}
// return a form that is based on the 'Basic Account Info' category of the user profile
function mymod_form() {
// load the user's profile
global $user;
$account = user_load($user->uid);
// load the profile form, and then edit it
$form_state = array();
$form = drupal_retrieve_form('user_profile_form', $form_state, $account, 'Basic Account Info');
// set the custom #theme function for this fieldset
$form['Basic Account Info']['#theme'] = 'mymod_fieldset';
// more form manipulations
// ...
return $form;
}
When my page gets rendered, I expected to see the fieldset representing 'Basic Account Info' to be wholly replaced by my test message 'test'. Instead what happens is that the <fieldset> and <legend> elements are rendered as normal, but with the body of the fieldset replaced by the test message instead, like this:
<fieldset>
<legend>Basic Account Info</legend>
test
</fieldset>
Why doesn't my #theme function have the chance to replace the entire <fieldset> element? If I wrap a textfield in this function instead, I am able to completely replace the <input> element along with its label. Furthermore, if I provide an override in my site's template.php for theme_fieldset, it works as expected and I am able to completely replace the <fieldset>, so I know it is possible.
What's different about providing #theme functions to fieldsets inside a module?
Have you tried overriding theme_fieldset() instead of using the #theme function? I believe you could do something like this in your .module file:
function mymodule_fieldset($element) {
// do something;
return $html;
}
This would apply to all fieldsets. You could do some kind of check on $element for the fieldsets you want to affect and then use the default implementation for all others.
Take a look at: http://api.drupal.org/api/function/theme_fieldset/6
I know this is an old post -- but I've run into the same issue. I came up with an ugly work around. This is definitely a bug in the Form API. Maybe my temporary fix will be helpful to someone.
I found (and appended) a bug report here: http://drupal.org/node/225698
Worth checking that before trying my hacky fix.
I'm not sure what the children are in $form['Basic Account Info'] in this example, but basically what you can do is use drupal_render() on that fieldset's children, and then recreate a fieldset array separate from $form['Basic Account Info'], theme it with theme() and pass it back to the form array as markup..
$fieldsetElement = array(
//$child is the key of whatever child you need in the fieldset
//you may have to alter this for multiple children, stringing
//together multiple drupal_render calls on each children
//(#children needs to be a string.. unless your theme can handle the array)
'#children'=>drupal_render($form['Basic Account Info'][$child]),
'#attributes'=>array(),//set real attributes
'#title'=>$form['Basic Account Info']['#title']
);
$form['Basic Account Info'] = array(
'#type'=>'markup',//not really needed, is default
'#value'=>theme('mymod_fieldset',$fieldsetElement)
);
super-duper hacking, likely causes disconnect with form state and potential validation failure -- but both are fixable by trial and error with the form api. I wouldn't recommend this unless you really want to get your hands dirty with PHP and drupal form API, but that's really the only way, unless you can live without variable fieldset themes in your module... Maybe try prefix and suffix?
This is just off the top of my head but maybe the difference is because a fieldset is not a form element but just a seperator or a grouper, if you will. Maybe the #theme callback is only for form elements?
The concept of your code works, meaning you can do what you want to do.
There are some things that can explain why it doesn't work.
The fieldset is not $form['Basic Account Info'].
Need to clear cache.
$form['Basic Account Info']['#theme'] is lost/overridden later in the code execution.
Try to take a look at $form before you do any of the moderations. When I tried to copy your code I run into a bug:
user.pages.inc file needed to be loaded
I was having the same issue.
You need to use #theme_wrappers instead of #theme
'#type' => 'fieldset',
'#theme_wrappers' => array('mymodule_fieldset'),

Zend_Form Display Groups Problem

Here is a short snippet from my class extending Zend_Form:
$this->addElements(array(
$inputField1,
$inputField2,
$inputField3,
$submitButton
));
$this->addDisplayGroup(array('inputField1',
'inputField2',
'inputField3',
'submitButton'),
'fieldset1',
array('legend' => 'Lorem Ipsum'));
The problem is when I print the form, it prints only the submit button, the fieldset with input fields (those are ordinary Zend_Form_Element_Text elements) isn't there.
EDIT: If I remove the $this->addDisplayGroup() (or comment it), the text elements are displayed without a problem.
Sounds like you aren't using the getDisplayGroup() in your controller or later on in the form init() method

Categories