Extending Zend_View_Helper_FormElement - zend-framework

I have created this file at My/View/Helper/FormElement.php
<?php
abstract class My_View_Helper_FormElement extends Zend_View_Helper_FormElement
{
protected function _getInfo($name, $value = null, $attribs = null,
$options = null, $listsep = null
) {
$info = parent::_getInfo($name, $value, $attribs, $options, $listsep);
$info['id'] = 'My new ID';
return $info;
}
}
How can i get the normal form elements to use this instead?
Why i want this?
Say that i use the same form multiple times on a page, the 'id='-tag of the formelements will apear multiple times, this is not w3c-valid. So initially i want to prefix the id with the id of the form.
Any better ideas or ways to do this is much apreciated.
Update: Just realized it's the same problem with the decorators :( Don't think this is the right path i've taken.

Create new form class extending Zend_Form and in the init() method use variable $ns to add prefix/suffix to all elements. You can set $ns variable through form constructor.
class Form_Test extends Zend_Form
{
protected $ns;
public function init()
{
$this->setAttrib('id', $this->ns . 'testForm');
$name = new Zend_Form_Element_Text('name');
$name->setAttrib('id', $this->ns . 'name');
$name->setLabel('Name: *')->setRequired(true);
$submit = new Zend_Form_Element_Submit('submit');
$submit->setAttrib('id', $this->ns . 'submitbutton');
$submit->setLabel('Add')->setIgnore(true);
$this->addElements(array($name, $submit));
}
public function setNs($data)
{
$this->ns = $data;
}
}
In the controller or wherever you are calling this forms specify each form instance:
$form1 = new Form_Test(array('ns' => 'prefix1'));
$this->view->form1 = $form1;
$form2 = new Form_Test(array('ns' => 'prefix2'));
$this->view->form2 = $form2;
// Validation if calling from the controller
if ($form1->isValid()) ...

Using multiple instances of same forms on a page can be validated if used as subform.
SubForms prefix all id's with the name/identifier of the subform.

Related

Zend_Form_Element requires each element to have a name

I'm trying to create a form in ZF 1.Here's my form class
class Application_Form_Album extends Zend_Form
{
public function init()
{
$this->setName('album');
#artist
$artist = new Zend_Form_Element_Text('artist');
$artist->setLabel('Artist')->setRequired(true)->addValidator('NotEmpty');
#title
$title = new Zend_Form_Element_Text('title');
$title->setLabel('Title')->setRequired(true)->addValidator('NotEmpty');
#submit
$submit = new Zend_Form_Element_Submit();
$submit->setAttribute('id','submitbutton');
$this->addElements(array($artist,$title,$submit));
}
}
and my controller action
public function addAction()
{
$form = new Application_Form_Album();
$form->submit->setLabel('Add');
$this->view->form = $form;
}
and my add.phtml
<?php echo $this->form;?>
But I'm getting this error.
Message: Zend_Form_Element requires each element to have a name
Not sure what I missed.Could anyone help me?
You should give a name for each form element. Missing name of submit. Zend Form generate id and name html tags from given name
For example:
$submit = new Zend_Form_Element_Submit('submitbutton');
And remove
$submit->setAttribute('id','submitbutton');
line.

Create Zend form element dynamically

I'm using Zend Framework 1.12 on a project. I having some problems with the Zend_Form. Some fields are generated dynamically on execution time, but the Zend_Form is static, a element predefined at creation.
So, when the form is sent, the validation doesn't work because new fields were added and the sent form doesn't match the form created.
How do that adaptation?
You should try, following solution: after sending the form, get the $_POST array, then check which fields/values do you have and create/modify form Object with this fields/validation.
I would have done this way :
class MyForm extends Zend_Form
{
public function init()
{
//... Create here the basic elements
}
public function initFromPostValue( $post )
{
if( array_key_exists( 'dynamicsField', $post ) ) {
$el = $this->createElement( 'select', 'dynamicsField' )
->setValidators( array( ... PUT your validators here ) );
$this->addElement( $el );
}
}
}
In the validation action :
public function validationAction()
{
$form = new MyForm();
$form->initFromPostValue( $_POST );
if( $form->isValid( $_POST ) ) {
// Form is valid
} else {
// Form is invalid
}
}

How to merge subform array into single array in Zend Framework1.11

I am using Zend Framework1.11. In my Zend Form I have two zend sub form, I have added these two sub form using addSubForm function.
Now when I call this zend form in controller then isValid function is not working. I have called it as follow..
public function registeredAction(){
$form = new Application_Form_RegisteredForm();
$form->setAction('registered');
$formData = $this->_request->getPost();
if($form->isValid($formData)){
// save into database using model class.
} else {
$form->populate($formData);
}
$this->view->form = $form;
}
In following code isValid is not working, while I print_r the $fotmData requested array, it print array like:-
Array(
[personal] => Array
(
[firstname] => 'Example',
[lastname] => 'Solution'
)
[MAX_FILE_SIZE] => 8388608
[address] => Array
(
[country] => 'IND',
[state] => 'RAJ'
)
);
I have also used the setData() function but it is not working, it's give exceptional error "Message: Method setData does not exist", I have used php array_merge function but return array is not working with isValid().
Can anyone help me to solve this problem. so I can easily store form data into database.
Thanks!
Take a look at array_merge
http://php.net/manual/de/function.array-merge.php
$newFormData=array_merge($formData["personal"],$formData["address"]);
My solution is to create new base form with new method getSubFormsValues(), i.e.:
class My_Form extends Zend_Form
{
public function getSubFormsValues()
{
$values = array();
foreach ($this->getSubForms() as $form) {
$name = $form->getName();
$value = $form->getValues();
$values = array_merge($value[$name], $values);
}
return $values;
}
}
When you can call $my_form_obj->getSubFormValues() in your code.

Validate a set of fields / group of fields in Zend Form

Is there any good solution for the following requirements:
A form with one field for zip code and default validators like number, length, etc.
After submission, the form is checked against a database.
If the zip code is not unique we have to ask for an city.
Examples:
Case 1: Submited zip code is unique in database. Everything is okay. Process form
Case 2: Submited zip code is not unique. Add a second field for city to the form. Go back to form.
We want to handle this in an generic way (not inside an controller). We need this logic for
a lot of forms. First thought was to add it to isValid() to every form or write a
validator with logic to add fields to the form. Subforms are not possible for us, because we need this for different fields (e.g. name and street).
Currently I'm using isValid method inside my forms for an User Form to verify the password and confirm password field. Also, when the form is displayed in a New Action, there are no modifications, but when displayed in an Edit Action, a new field is added to the form.
I think that is a good option work on the isValid method and add the field when the validation return false, and if you want something more maintainable, you should write your own validatator for that purpose.
Take a look at my code:
class Admin_Form_User extends Zf_Form
{
public function __construct($options = NULL)
{
parent::__construct($options);
$this->setName('user');
$id = new Zend_Form_Element_Hidden('id');
$user = new Zend_Form_Element_Text('user');
$user->setLabel('User:')
->addFilter('stripTags')
->addFilter('StringTrim')
->setAllowEmpty(false)
->setRequired(true);
$passwordChange = new Zend_Form_Element_Radio('changePassword');
$passwordChange->setLabel('Would you like to change the password?')
->addMultiOptions(array(1 => 'Sim', 2 => 'Não'))
->setValue(2)
->setSeparator('');
$password = new Zend_Form_Element_Password('password');
$password->setLabel('Password:')
->addFilter('stripTags')
->addFilter('StringTrim')
->setRequired(true);
$confirm_password = new Zend_Form_Element_Password('confirm_password');
$confirm_password->setLabel('Confirm the password:')
->addFilter('stripTags')
->addFilter('StringTrim')
->addValidator('Identical')
->setRequired(true);
$submit = new Zend_Form_Element_Submit('submit');
$submit->setLabel('Save');
$this->addElements(array($id,$name,$lastname,$group,$user,$passwordChange,$password,$confirm_password,$submit));
$this->addDisplayGroup(array('password','confirm_password'),'passwordGroup');
$this->submit->setOrder(8);
$this->setDisplayGroupDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'div','id' => 'div-password'))
)
);
$passwordChange->clearDecorators();
}
public function addPasswordOption()
{
$this->changePassword->loadDefaultDecorators();
$this->getDisplayGroup('passwordGroup')
->addDecorators(array(
array('HtmlTag', array('tag' => 'div','id' => 'div-password'))
)
);
$this->password->setRequired(false);
$this->confirm_password->setRequired(false);
}
public function setPasswordRequired($flag = true)
{
$this->password->setRequired($flag);
$this->confirm_password->setRequired($flag);
}
public function isValid($data)
{
$confirm = $this->getElement('confirm_password');
$confirm->getValidator('Identical')->setToken($data['password']);
return parent::isValid($data);
}
}
So, in my controller:
public function newAction()
{
$this->view->title = "New user";
$this->view->headTitle($this->view->title, 'PREPEND');
$form = $this->getForm();
if($this->getRequest()->isPost())
{
$formData = $this->_request->getPost();
if($form->isValid($formData))
{
$Model = $this->getModel();
$id = $Model->insert($formData);
$this->_helper->flashMessenger('The user data has beed updated.');
$this->_helper->redirector('list');
}
}
$this->view->form = $form;
}
public function editAction()
{
$this->view->title = "Edit user";
$this->view->headTitle($this->view->title, 'PREPEND');
$id = $this->getRequest()->getParam('id');
$form = $this->getForm();
// Add yes or no password change option
$form->addPasswordOption();
$Model = $this->getModel();
if($this->getRequest()->isPost())
{
$formData = $this->getRequest()->getPost();
// Change password?
if($formData['changePassword'] == 2) $form->setPasswordRequired(false);
if($form->isValid($formData))
{
$Model->update($formData);
$this->_helper->flashMessenger('The user data has beed updated.');
$this->_helper->redirector('list');
}
}
$data = $Model->getById($id)->toArray();
$form->populate($data);
$this->view->form = $form;
}
You will probably need a Javascript form validator for that. In the submit function perform an AJAX call to check if the zipcode is unique. If not, show an extra city field.
But you still have to perform the validation server side: never trust user input, even if it's validated on the client side.

Is there a way to generate views from Zend_Form ? (read-only)

I was wondering if is there an easy way to generate views from form objects when dealing with CRUDs.
I mean, when we have these options: VIEW | EDIT | DELETE
I want my VIEW option like EDIT option, but without form elements, just the values.
This will minimize so much the time spent to create these views.
Someone knowks something like that?
In my last project I had this dilemma too. My solution may not be the most elegant, but it did the job. Mind you, I use a Form viewscript decorator in stead of a full decorator generated elements. But you could adjust this example to use decorators I presume. What I'm showing is a very basic example, to give you a general idea. Here's what I did:
class Cms_Form_Page extends Zend_Form
{
const FOR_CREATE = 'forCreate';
const FOR_READ = 'forRead';
const FOR_UPDATE = 'forUpdate';
const FOR_DELETE = 'forDelete';
protected $_name = 'page';
private $_for;
private $_viewScripts = array(
self::FOR_CREATE => 'page-manager/partials/form-page-create.phtml',
self::FOR_READ => 'page-manager/partials/form-page-read.phtml',
self::FOR_UPDATE => 'page-manager/partials/form-page-update.phtml',
self::FOR_DELETE => 'page-manager/partials/form-page-delete.phtml'
);
public function __construct( $for = self::FOR_CREATE, $options = null )
{
$this->_for = $for;
parent::__construct( $options );
}
public function init()
{
$this->setName( $this->_name )
->setAttribs( array( 'accept-charset' => 'utf-8' ) )
->setDecorators( array(
'PrepareElements',
array( 'ViewScript', array( 'viewScript' => $this->_viewScripts[ $this->_for ] ) ),
'Form'
) );
$elements = array();
swith( $this->_for )
{
case self::FOR_CREATE:
$title = new Zend_Form_Element_Text( 'title' );
$elements[] = $title;
break
case self::FOR_READ:
$id = new Zend_Form_Element_Hidden( 'id' );
$elements[] = $id;
break;
case self::FOR_UPDATE:
$id = new Zend_Form_Element_Hidden( 'id' );
$elements[] = $id;
$title = new Zend_Form_Element_Text( 'title' );
$elements[] = $title;
break;
case self::FOR_DELETE:
$id = new Zend_Form_Element_Hidden( 'id' );
$elements[] = $id;
break;
default:
throw new Exception( 'invalid Form type' );
}
$submit = new Zend_Form_Element_Button( 'submit' );
$elements[] = $submit;
$this->addElements( $elements );
}
}
So, basically, I pass one of the class constants to it's constructor. And based on that value, I determine what elements are needed for the form, and how the elements should be rendered.
For instance, for create you could have a select dropdown form field where you would choose a Locale, where for delete this would be a hidden field (not shown in my example btw).
Hope this has given you some ideas.
PS:
In one of the selected viewscripts you could then simply show the value of an element (along with rendering the hidden element too), with something like:
<?
$form = $this->element;
?>
... some html
// let's presume id and locale are hidden form fields for current form type
// (Cms_Form_Page::FOR_UPDATE for instance)
<?= $form->id->renderViewHelper(); ?>
<?= $form->locale->renderViewHelper(); ?>
// and here we simply output the current locale value
// of course, you should have populated the values in the form somewhere first
<dt>Current locale:</dt>
<dd><?= $form->locale->getValue(); ?></dd>
...etc
So, I think you'ld be best of with using viewscript decorators for the form, or you could roll your own form element decorator that renders the hidden field (if neccesary) and simply shows it's value in some html tag.
Hector from Nabble, show me this, which seems to be the best way:
class Default_View_Helper_FormView extends Zend_View_Helper_Abstract
{
public function formView(Zend_Form $form)
{
$html = "<dl>";
foreach ($form->getElements as $element) {
$html .= "<dt>{$element->getLabel()}</dt>";
$html .= "<dd>{$element->getValue()}</dd>";
}
$html .= "</dl>";
return $html;
}
}
I'm not sure if I understand, but I think that for the view option you can just fetch the data from your model. No need to access them through Zend_Form.
But if you want the make your form read-only, you can add readonly (setAttrib('readonly', 'readonly')) attribute to your elements.
Made a couple minor additions to the accepted answer to cover common elements that may be special cases:
class Default_View_Helper_FormView extends Zend_View_Helper_Abstract
{
public function formView( Zend_Form $form )
{
$html = '<dl>';
foreach ( $form->getElements() as $element ) {
if( $element instanceof Zend_Form_Element_Submit ) {
continue;
}
$html .= '<dt>' . $element->getLabel() . '</dt>';
$value = $element->getValue();
if( $element instanceof Zend_Form_Element_Checkbox ) {
$value = ($value) ? 'Yes' : 'No';
}
else if( $element instanceof Zend_Form_Element_Select ) {
$value = $element->getMultiOption($value);
}
$html .= '<dd>' . $value . '</dd>';
}
$html .= '</dl>';
return $html;
}
}
The only problem with the accepted answer is that you're creating all the elements and then ignoring them.
Using the control logic from fireeyedboy's answer, you could instead switch all the elements to Zend_View_Helper_FormNote which does the same thing.
Just depends on if the optimization matters.