I am trying to build a simple test application using Zend Framework 1.12.7. I have the following code to create a form in a file in the forms folder called Album.php:
class Application_Form_Album extends Zend_Form
{
public function init()
{
$this->setName('album');
$id = new Zend_Form_Element_Hidden('id');
$id->addFilter('Int');
$artist = new Zend_Form_Element_Text('artist');
$artist->setLabel('Artist')
->setRequired(true)
->addFilter('StripTags')
->addFilter('StringTrim')
->addValidator('NotEmpy');
$title = new Zend_Form_Element_Text('title');
$title->setLabel('Title')
->setRequired(true)
->addFilter('StripTags')
->addFilter('NotEmpty');
$submit = new Zend_Form_Element_Submit('submit');
$submit->setAttrib('id', 'submitbutton');
$this->addElements(array($id,$artist,$title,$submit));
}
}
I then added the following code to my IndexController addAction():
public function addAction()
{
$form = new Application_Form_Album();//Create the Form Object
$form->submit->setLabel('Add');//Set label of Submit button
$this->view->form = $form;//Assign view for rendering
if($this->getRequest()->isPost()){//If TRUE then form submitted
$formData = $this->getRequest()->getPost();
if($form->isValid($formData)){//If VALUD then add record
$artist = $form->getValue('artist');
$title = $form->getValue('title');
$albums = new Application_Model_DbTable_Albums();
$albums->addAlbum($artist, $title);
$this->_helper->redirector('index');//return to homepage
}else{
$form->populate($formData);//INVALID so repopulate the form
}
}
}
Then in my view add.phtml I added this code:
<?php
$this->title = "Add new album";
$this->headTitle($this->title);
echo $this->form;
I am getting no errors and the title "Add new album" shows when I am on this URL
http://localhost/zf-tutorial/public/index/add
does anyone know why my form is not displaying???
Ok I've figured it out, and it might be useful for anyone who is just starting to use Zend Like myself. Anyways as of Zend Framework v1.12 the correct way to create a form is by using the addElement method for each element of the form. So the correct way to render my form included in my original post is like this:
public function init()
{
$this->setName('album');
$this->setMethod('post');
$this->addElement('hidden', 'id', array(
'filters'=>array('Int')
));
$this->addElement('text', 'artist', array(
'label' => 'Artist',
'required'=> true,
'filters'=>array('StringTrim','StripTags'),
'validators'=>array('NotEmpty')
));
$this->addElement('text', 'title', array(
'label' => 'Title',
'required'=> true,
'filters'=>array('StripTags'),
'validators'=>array('NotEmpty')
));
$this->addElement('submit', 'submit', array(
'ignore'=>true,
'label'=>'Create'
));
}
Your form creation and handling differs from mine in only two ways:
I typically set the form in the view at the bottom, after all the checks and processing, just before exiting the method.
I do not believe that it is necessary for you have the else clause in which you populate the form with the $formData. The call to isValid($formData) "essentially" does that.
Still hard to see why either of these would cause your form not to render, but worth a try.
Related
I'm working on a project with ZF2 and Zend Form. I'd like to add an avatar into a user profile.
The problem is that I only get the file name and save it in the DB. I would like to insert it into a folder so I'll be able to get it and display it. The rest of the form is working.
My guess is that I have to get information from $FILES, but I have no idea how to do this. I've read the documentation but can't see how to apply this to my project.
Thank you in advance!
Here's my Controller Action
public function signinAction()
{
$this->em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
$form = new SignupForm($this->em);
$model = new ViewModel(array("form" => $form));
$url = $this->url()->fromRoute("signin");
$prg = $this->prg($url, true);
if($prg instanceof \Zend\Http\PhpEnvironment\Response){
return $prg;
}
else if($prg === false){
return $model;
}
$user = new User();
$form->bind($user) ;
$form->setData($prg) ;
if($form->isValid()){
$bcrypt = new Bcrypt() ;
$pwd = $bcrypt->create($user->getPassword());
$user->setPassword($pwd);
$this->em->persist($user) ;
$this->em->flush() ;
return $this->redirect()->toRoute('login');
}
return $model ;
}
Here's my form file :
class SignupForm extends Form
{
private $em = null;
public function __construct($em = null) {
$this->em = $em;
parent::__construct('frm-signup');
$this->setAttribute('method', 'post');
$this->setHydrator(new DoctrineEntity($this->em, 'Application\Entity\User'));
//Other fields
...
//File
$this->add(array(
'type' => "File",
'name' => 'avatar',
'attributes' => array(
'value' => 'Avatar',
),
));
//Submit
...
}
}
And the form in my view :
$form = $this->form;
echo $this->form()->openTag($form);
//other formRow
echo $this->formFile($form->get('avatar'));
echo $this->formSubmit($form->get('submit'));
echo $this->form()->closeTag();
There are two things you could look at for getting your avatar to work:
Using the Gravatar view helper (uses gravatar.com service that automatically links images to email addresses)
documentation on using the gravatar service can be found here
Upload images yourself with the file upload classes that are shipped with ZF2:
form class for file upload can be found here
input filter class documentation can be found here
If you follow those docs you should be able to manage what you want.
Note: check especially the use of the Zend\Filter\File\RenameUpload filter in the example in the input filter documentation. This filter renames/moves the uploaded avatar file to the desired location.
I'm trying to create simple form with Zend, I need to use this form in most part, so I create the default form then in controller i modify it for the occurrence with private function. But I have two problems:
the form getValues() doesn't take the value of text element.
I put render at the end of the form action, but it doesn't render to the right page.
The form consists of a text field and the sumbit button
Here is the code of my controller:
That is for customize the form
private function getSearchForm($action = '', $name, $type, $placeholder)
{
$urlHelper = $this->_helper->getHelper('url');
$this->_searchForm = new Application_Form_Admin_Search_Search();
$this->_searchForm->setName($name);
$text = $this->_searchForm->getElement('ricerca');
$text->setLabel('Ricerca '.$type);
$text->setName($type);
$text->setAttrib('placeholder', $placeholder);
$this->_searchForm->setAction($urlHelper->url(array(
'controller' => 'admin',
'action' => $action),
'default'
));
return $this->_searchForm;
}
there are the actions:
public function pneumaticoAction()
{
$this->_searchForm = $this->getSearchForm('pneumaticosearch', 'search', 'pneumatico', 'Ricerca per: modello, marchio o codice');
$this->view->searchForm = $this->_searchForm;
}
public function pneumaticosearchAction()
{
if (!$this->getRequest()->isPost()) {
$this->_helper->redirector('index', 'public');
}
$form=$this->_searchForm;
if (!$form->isValid($this->getRequest()->getPost())) {
$this->render('pneumatico');
}
$values = $form->getValues();
$this->view->assign(array(
"pneumatici" => $this->_modelAdmin->searchPneumatici($values['pneumatico'])
));
$this->render('pneumatico');
}
First question, whenever you get routed to pneumaticosearch action, you do not set $this->_searchForm but you have it as:
$form=$this->_searchForm;
Should be something like this:
$form = $this->getSearchForm('pneumaticosearch', 'search', 'pneumatico', 'Ricerca per: modello, marchio o codice');
And the second question. When you run render, it is similar to pass $this->view parameters to .phtml. I don't see your view files, but I guess you need to set view first:
$this->view->searchForm = $form
I have a number of fieldsets, and I would like to create an input filter class for each of them. The idea is then that for each of my forms, I can create an input filter class that is composed of other input filters. For instance, when creating an account via a registration form, I would like to take the base Account input filter I have for my Account entity and use it in a new input filter class that can modify the inputs or add additional ones. Something like the below.
class Register extends InputFilter
{
public function __construct(ObjectRepository $accountRepository, Account $accountFilter)
{
/***** Add inputs from input filters *****/
$this->inputs = $accountFilter->getInputs();
/***** Add additional validation rules *****/
// Username
$usernameAvailability = new NoObjectExists(array(
'object_repository' => $accountRepository,
'fields' => array('username'),
));
$username = $this->get('username');
$username->getValidatorChain()
->attach($usernameAvailability, true);
// E-mail
$emailAvailability = new NoObjectExists(array(
'object_repository' => $accountRepository,
'fields' => array('email'),
));
$email = $this->get('email');
$email->getValidatorChain()
->attach($emailAvailability, true);
}
}
I pass in an input filter to the constructor, and I want to add the inputs of this filter to my Register filter and modify the inputs.
The problem I am having is that only some of my inputs seem to validate as intended, and I cannot seem to figure out why. When I submit my form, only some inputs are validated as expected:
Interestingly, the e-mail input does not behave as expected when filling out an e-mail that already exists in my database. The result should be a validation error that it already exists, but this does not happen. If I debug and look at my form, I found the following:
The form's filter has the right inputs with the right validators, and as shown on the above image, the username input does seem to validate correctly. But for some reason, this is not visually reflected in my form.
Below is my code.
Fieldsets
class Profile extends Fieldset
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('profile');
$this->setHydrator(new DoctrineHydrator($objectManager))
->setObject(new ProfileEntity());
// Elements go here
$this->add(new AccountFieldset($objectManager));
}
}
class Account extends Fieldset
{
public function __construct()
{
parent::__construct('account');
$username = new Element\Text('username');
$username->setLabel('Username');
$password = new Element\Password('password');
$password->setLabel('Password');
$repeatPassword = new Element\Password('repeatPassword');
$repeatPassword->setLabel('Repeat password');
$email = new Element\Email('email');
$email->setLabel('E-mail address');
$birthdate = new Element\DateSelect('birthdate');
$birthdate->setLabel('Birth date');
$gender = new Element\Select('gender');
$gender->setLabel('Gender')
->setEmptyOption('Please choose')
->setValueOptions(array(
1 => 'Male',
2 => 'Female',
));
$this->add($username);
$this->add($password);
$this->add($repeatPassword);
$this->add($email);
$this->add($birthdate);
$this->add($gender);
$this->add(new CityFieldset());
}
}
Form
class Register extends Form
{
public function __construct()
{
parent::__construct('register');
// Terms and Conditions
$terms = new Element\Checkbox('terms');
$terms->setLabel('I accept the Terms and Conditions');
$terms->setCheckedValue('yes');
$terms->setUncheckedValue('');
$terms->setAttribute('id', $terms->getName());
// Submit button
$submit = new Element\Submit('btnRegister');
$submit->setValue('Register');
$profileFieldset = new ProfileFieldset($objectManager);
$profileFieldset->setUseAsBaseFieldset(true);
// Add elements to form
$this->add($terms);
$this->add($profileFieldset);
$this->add($submit);
}
}
View
$form->prepare();
echo $this->form()->openTag($form);
$profile = $form->get('profile');
$account = $profile->get('account');
echo $this->formRow($account->get('username'));
echo $this->formRow($account->get('password'));
echo $this->formRow($account->get('repeatPassword'));
echo $this->formRow($account->get('email'));
echo $this->formRow($account->get('birthdate'));
echo $this->formRow($account->get('gender'));
$city = $account->get('city');
echo $this->formRow($city->get('postalCode'));
echo $this->formRow($form->get('terms'));
echo $this->formSubmit($form->get('btnRegister'));
echo $this->form()->closeTag();
Controller
$form = new Form\Register();
$profile = new Profile();
if ($this->request->isPost()) {
$form->bind($profile);
$form->setData($this->request->getPost());
$form->setInputFilter($this->serviceLocator->get('Profile\Form\Filter\Register'));
if ($form->isValid()) {
// Do stuff
}
}
return new ViewModel(array('form' => $form));
Am I misunderstanding something here? Is there a better way to do this while still having multiple input filter classes? I would really prefer to keep my code maintainable like this rather than copying validation rules around for different forms. Sorry for the long post - it was really difficult to explain this problem!
Okay, it seems like I figured this out. Apparently my first approach was quite wrong. I found a way to have an input filter class for each of my fieldsets and then reuse these input filters for my form while adding additional validation rules for certain form elements (from my fieldsets). This way, I can have my generic validation rules defined in standard input filter classes per fieldset and modify them for different contexts (i.e. forms). Below is the code. The classes differ a bit from the question because that was slightly simplified.
Main input filter
// This input filter aggregates the "fieldset input filters" and adds additional validation rules
class Register extends InputFilter
{
public function __construct(ObjectRepository $accountRepository, InputFilter $profileFilter)
{
/***** ADD ADDITIONAL VALIDATION RULES *****/
// Username
$usernameAvailability = new NoObjectExists(array(
'object_repository' => $accountRepository,
'fields' => array('username'),
));
$emailInput = $profileFilter->get('account')->get('username');
$emailInput->getValidatorChain()->attach($usernameAvailability, true);
// E-mail
$emailAvailability = new NoObjectExists(array(
'object_repository' => $accountRepository,
'fields' => array('email'),
));
$emailInput = $profileFilter->get('account')->get('email');
$emailInput->getValidatorChain()->attach($emailAvailability, true);
/***** ADD FIELDSET INPUT FILTERS *****/
$this->add($profileFilter, 'profile');
}
}
Profile input filter
class Profile extends InputFilter
{
public function __construct(InputFilter $accountFilter)
{
$this->add($accountFilter, 'account');
// Add generic validation rules (inputs) for the profile fieldset here
}
}
The Account input filter referred to in the code above is a completely normal input filter class that extends Zend\InputFilter\InputFilter and adds inputs. Nothing special about it.
My fieldsets remain untouched and are identical to the ones in the question, as are the form class and the controller. The Register input filter is added to the form with the setInputFilter method, and that's it!
With this approach, each input filter instance is added to a fieldset - something that my first approach did not do. I hope this helps someone with similar problems!
I have a User entity with some validators specified in it.
But I want to extend that list of validators so as to add one based on a service call to check if a user email is not already used.
The problem is that I need an entity manager injected in the validator to call the service. And I can't (or rather don't know how to and don't want to :-) inject the entity manager into the entity.
I also want to leave in place the existing validation on the entity, as it is closer to the repository and might be used outside of my controller. So I cannot move all the validators out of this entity, into my new custom filter.
My new custom filter thus shall reuse the existing validators of the entity, as is.
The new custom filter is called UserFormFilter and it receives in its controller the existing entity filter, loops on it, adding to itself, each of the passed in validators:
class UserFormFilter extends InputFilter
{
public function __construct(EntityManager $em, $identity, InputFilter $userDefaultInputFilter)
{
// Add the validators specified in the user entity
foreach ($userDefaultInputFilter->inputs as $inputFilter) {
$this->add($inputFilter);
}
$this->add(array(
'name' => 'email',
'required' => true,
'filters' => array(),
'validators' => array(
array('name' => 'EmailAddress'),
array(
'name' => 'Application\Validator\User',
'options' => array('manager' => $em, 'identity' => $identity)
)
),
));
}
}
I can now instantiate a custom UserFormFilter in my UserController:
$formFilter = new \Application\Form\UserFormFilter($em, $user->getInputFilter());
$form->setInputFilter($formFilter);
You can see in the above code that the custom UserFormFilter takes the default filter specified in my User entity getInputFilter method. As a side note, it also passes the entity manager enabling the custom filter to carry out the service call.
Building a custom adminhtml module. Im using a simple form. it looks like this :
<?php
class Namespace_Modulename_Block_Adminhtml_Modulename_Edit_Tab_Form extends Mage_Adminhtml_Block_Widget_Form
{
protected function _prepareForm()
{
$form = new Varien_Data_Form();
$this->setForm($form);
$fieldset = $form->addFieldset('modulename_form',array('legend'=>Mage::helper('modulename')->__('Module Data')));
$fieldset->addField('test', 'text', array(
'label' => Mage::helper('modulename')->__('Test'),
'name' => 'test',
));
// I want to add a custom button here. Say an action called "Add Option".
//Clicking this action adds input boxes or dropdowns to the form that are to
//to be included in the post when submitting the form (obviously).
I have been looking for a solution on Stack overflow and have not been able to find something that could help. I have then tried searching for something similar present in the Mage Core.
In the admin panel, if i go to [Catalog->Attributes->Manage Attributes] and click on a standard attribute like "Manufacturer" for example, on the second tab, called "Manage labels/Options" i see the following screen :
There is a button and action which allows me to add options (in the form of textboxes) to the form. Identifying this as something i am trying to replicate, i went into the core to try and figure out what to do. If i open the following files (Magento Enteprise 12.0.2) :
Mage_Adminhtml_Block_Catalog_Product_Attribute_Edit_Tab_Options
I see it is empty, but extends Mage_Eav_Block_Adminhtml_Attribute_Edit_Options_Abstract
and I have gone through this file, but little makes sense to me. Am I going down the wrong way? How can I achieve something similar, a button and action which adds fields to a admin form?
thanks
You can set custom template for your form, and add there any tags (like button) via html.
You can add renderer for you field
$customField = $fieldset->addField('test', 'text', array(
'label' => Mage::helper('modulename')->__('Test'),
'name' => 'test',
));
$customField->setRenderer($this->getLayout()->createBlock('yuormodule/adminhtml_yourform_edit_renderer_button'));
in Block Class
class Yournamespace_Yourmodule_Block_Adminhtml_Yourform_Edit_Renderer_Button extends Mage_Adminhtml_Block_Abstract implements Varien_Data_Form_Element_Renderer_Interface {
public function render(Varien_Data_Form_Element_Abstract $element) {
//You can write html for your button here
$html = '<button></button>';
return $html;
}
}
1- First you must create a container for your Grid like this
public function __construct() {
parent::__construct();
$this->setTemplate('markavip/dataflow/edit/form/mapping.phtml');
}
2- in the Same Container you will add your buttons like
public function _prepareLayout() {
$this->setChild('add_button', $this->getLayout()->createBlock('adminhtml/widget_button')
->setData(array(
'label' => Mage::helper('markavip_dataflow')->__('Add Option'),
'class' => 'add',
'id' => 'add_new_option_button'
)));
$this->setChild('delete_button', $this->getLayout()->createBlock('adminhtml/widget_button')
->setData(array(
'label' => Mage::helper('markavip_dataflow')->__('Delete'),
'class' => 'delete delete-option'
)));
return parent::_prepareLayout();
}
public function getAddNewButtonHtml() {
return $this->getChildHtml('add_button');
}
public function getDeleteButtonHtml() {
return $this->getChildHtml('delete_button');
}
3- and in the PHTML you will add thes buttons like this :
<?php echo $this->getAddNewButtonHtml() ?>
and after this you will play in JS functions for add and hide
I am having issues with zend_form and zend_decorator.
I have created a decorator class to default all forms to use list elements, however it doesn't seem to be working!
Essentially my_decorator_design extends zend_form and then my forms extend the decorator.
Ideas?
class My_Decorator_Design extends Zend_Form {
public function loadDefaultDecorators() {
$this->addDecorator('FormElements')
->addDecorator('HtmlTag', array('tag' => 'ul')) //this adds a <ul> inside the <form>
->addDecorator('Form');
$this->setElementDecorators(array(
'ViewHelper',
'Label',
'Errors',
new Zend_Form_Decorator_HtmlTag(array('tag' => 'li')) //wrap elements in <li>'s
));
$this->setDisplayGroupDecorators(array(
'FormElements',
'Fieldset',
new Zend_Form_Decorator_HtmlTag(array('tag' => 'li')), //wrap groups in <li>'s too
new Zend_Form_Decorator_HtmlTag(array('tag' => 'ul'))
));
$this->setDisplayGroupDecorators(array(
'FormElements',
'Fieldset',
new Zend_Form_Decorator_HtmlTag(array('tag' => 'li')) //wrap groups in <li>'s too
));
}
}
class Forms_User_Update extends My_Decorator_Design {
public function __construct($options=array()) {
parent::__construct($options);//if we ever want to pass on things to zend_form
$this->setName('user_update');
$this->loadDefaultDecorators();
//user_name, first_name, email, password, date_of_birth
$user_name = new Zend_Form_Element_Text('user_name');
$first_name = new Zend_Form_Element_Text('first_name');
$email = new Zend_Form_Element_Text('email');
$password = new Zend_Form_Element_Password('password');
$password2 = new Zend_Form_Element_Password('password2');
$submit = new Zend_Form_Element_Submit('Submit');
$user_name->setRequired(true)
->setLabel('Username');
$first_name->setRequired(false)
->setLabel('First Name');
$email->setRequired(true)
->setLabel('Email:')
->addFilter('StringToLower')
->addValidator('NotEmpty', true)
->addValidator('EmailAddress');
$password->setLabel('Password:')
->setRequired(false)
->setIgnore(false)
->addValidator('stringLength', false, array(6));
$password2->setLabel('Confirm Password:')
->setRequired(false)
->setIgnore(true);
$submit->setLabel("Submit")
->setIgnore(true);
$this->addElements(array(
$user_name, $first_name, $email, $password, $password2, $submit
));
//$this->Submit->removeDecorator('Label');
//$this->addElementPrefixPath('My_Decorator', 'My/Decorator/', 'decorator');
$this->setMethod('post');
$this->setAction('/update-account');
}
}
You call loadDefaultDecorators in the constructor, and call setElementDecorators from there.
But addElement uses the element decorators only when you construct elements from strings, not when you pass a ready made element, while setElementDecorators only sets the decorators for the already existing controls (which are none in the constructor).
As you create the elements first and then pass them as Elements the element decorators are never set.
Remove the call to loadDefaultDecorators in the constructor.
I have also faced these types of problems but finally I have customized the zend form for both file element and other input elements.
Use the following code to apply file decorator with other elements in the zend form .
<?php
public function init()
{
// Set the method for the display form to POST
$this->setMethod('post');
$this->setElementDecorators(array(
'ViewHelper',
array('HtmlTag',array('tag' =>'div' ,'class'=>'field')),
array('Label',array('tag' =>'div','class'=>'label')),
));
// Add an email element
$this->addElement('text','name',array('label'=>'Name:',
'required'=>true,
$this->addElement('text','username',array('label'=>'User Name:','id'=>'user_name',
'required'=>true,
));
$this->addElement('password','password',array('label'=>'Password:',
'required'=>true,
));
$this->addElement('password','cpassword',array('label'=>'Password Again:',
'required'=>true,
));
$this->addElement('text','phone',array('label'=>'Phone:',
'required'=>true,
));
$this->addElement('text', 'email', array(
'label' => 'Email:',
'value' =>'test',
));
$this->addElement('file', 'photo', array(
'label' => 'Profile Photo:',
'decorators' =>array('file',array('HtmlTag',array('tag'=>'div','class'=>'field'))
,array('Label',array('tag'=>'div','class'=>'label')))
));
$this->addElement('submit', 'save',array('label'=>'Register Me','class'=>'submitbtn','style'=>'margin-left:150px;'));
}
?>
I hope that will work fine