Zend - Dynamic form dropdown - zend-framework

I start with zend framework and I'm having trouble implementing a dynamic drop-down list.
I need to create a simple dropdown list of events select from the database.
This is my Module class :
public function getFormElementConfig()
{
return array(
"factories" => [
'participant_form' => function (ServiceManager $serviceManager) {
/** #var EntityManager $entityManager */
$entityManager = $serviceManager->get("doctrine.entitymanager.orm_default");
$events = $entityManager->getRepository('Application\Entity\Event')->findAll();
$eventForSelect = array();
foreach ($events as $event) {
$eventForSelect[$event->getId()] = $event->getName();
}
/** #var \Zend\Form\Form $form */
$form = new ParticipantForm();
$form->setHydrator(new DoctrineHydrator($entityManager));
$form->setObject(new Participant());
$form->setOption('event_for_select', $eventForSelect);
return $form;
},
]
);
}
but I do not know how to get the option 'event_for_select' in my form :
class ParticipantForm extends Form
{
public function __construct($name = null)
{
parent::__construct('user');
$this->setAttribute('class', 'form-horizontal');
$this->add([
'name' => 'id',
'type' => 'Hidden',
]);
$this->add([
'name' => 'firstname',
'type' => 'Text',
'options' => [
'label' => 'Prénom',
],
]);
$this->add([
'name' => 'event',
'type' => 'Select',
'options' => [
'label' => 'Event',
'value_options' => // ?? $event_for_select
],
]);
Thanks for your help !

Add field in your form class:
class ParticipantForm extends Form
{
protected $eventForSelect;
public function setEventForSelect($eventForSelect)
{
$this->eventForSelect = $eventForSelect;
// update field value options
$this->get('event')
->setValueOptions($this->eventForSelect);
return $this;
}
// rest of form class code
}
Then use setEventForSelect() method in your factory:
/** #var \Zend\Form\Form $form */
$form = new ParticipantForm();
$form->setHydrator(new DoctrineHydrator($entityManager));
$form->setObject(new Participant());
$form->setEventForSelect($eventForSelect);
return $form;
And then in form:
$this->add([
'name' => 'event',
'type' => 'Select',
'options' => [
'label' => 'Event',
'value_options' => $this->eventForSelect,
],
]);

Related

Magento 1.9 sending custom form - form data not found in controller

I am having trouble with a simple form. I am quite sure that I miss some critical info about this topic...
I created a custom module, I created a custom form
<?php
class modulename_History_Block_Adminhtml_History_Edit_Form extends Mage_Adminhtml_Block_Widget_Form {
protected function _prepareForm()
{
$form = new Varien_Data_Form(array(
'id' => 'edit_form',
'name' => 'edit_form',
'action' => $this->getUrl('*/*/history', array('id' => 'orders_export')),
'method' => 'post',
'enctype' => 'multipart/form-data',
'data' =>'somethingsomethingdarkaside'
));
$this->setForm($form);
$fieldset = $form->addFieldset('Filtrování objednávek', array('legend'=> 'Nastavte filtr pro report objednávek'));
$dateTimeFormatIso = Mage::app()->getLocale()->getDateTimeFormat(Mage_Core_Model_Locale::FORMAT_TYPE_SHORT);
$fieldset->addField('date_from', 'date', array(
'label' => 'Změna statusu objednávek od:',
'title' => 'Změna statusu objednávek od:',
'time' => true,
'name' => 'filter_date_from',
'image' => $this->getSkinUrl('images/grid-cal.gif'),
'format' => $dateTimeFormatIso,
'required' => true,
));
$fieldset->addField('export_history_order_status_changed', 'button', array(
'label' => 'Exportovat do souboru:',
'value' => 'Export',
'name' => 'export_history_order_status_changed',
'class' => 'form-button',
'onclick' => "setLocation('{$this->getUrl('*/*/export')}')",
));
$form->setUseContainer(true);
return parent::_prepareForm();
}
And then there is a controller. When the button is pressed, it goes to the correct controller to a correct action. However no data in post received:
<?php
class modulename_History_Adminhtml_History_HistoryController extends Mage_Adminhtml_Controller_Action {
protected function _initAction()
{
return $this;
}
/**
* A page with the form, creating the block with it.
*
*/
public function editAction()
{
$this->_title('Historie objednavky')
->loadLayout()
->_setActiveMenu('modulename/historymenu');
$this->_addContent($this->getLayout()->createBlock('modulename_history/adminhtml_history_edit'));
$this->renderLayout();
}
/**
* A main entrance - when the filter is set and the "export" button pressed then this is the function which starts.
*
* #return bool|Mage_Core_Controller_Varien_Action - either we return a downloadable file or we return false.
*/
public function exportAction()
{
if ($this->_setParameters())
{
if ($this->_setOrdersIds())
{
return $this->_getDownloadFile();
}
}
return false;
}
/**
* Try to get parameters from the admin form. If all correct then we return true. If there is something not set
* then we are unable to continue and we return false.
*
* #return bool - either we were successful with getting the parameters or not.
*/
protected function _setParameters()
{
$parameters = $this->getRequest()->getParams();
$pokus = $this->getRequest()->getPost();
$necf = $this->getRequest()->getPost('edit_form');
$neco = Mage::app()->getRequest()->getParam('edit_form');
}
}
Hellou,
so a competent colleque find an answer within minutes. I mean it was as silly as expected, just remove on click action on button and set the heading from the button to the form. And changed the button to submit. I quess the guy I copied the code from used some other spells of high magic so it worked for him. I hope this will help. Correct form below:
<?php
class modulename_History_Block_Adminhtml_History_Edit_Form extends
Mage_Adminhtml_Block_Widget_Form {
protected function _prepareForm()
{
$form = new Varien_Data_Form(array(
'id' => 'edit_form',
'name' => 'edit_form',
//'action' => $this->getUrl('*/*/history', array('id' => 'orders_export')),
'action' => $this->getUrl('*/*/export', array('id' => 'orders_export')),
'method' => 'post',
'enctype' => 'multipart/form-data',
'data' =>'somethingsomethingdarkaside'
));
$this->setForm($form);
$fieldset = $form->addFieldset('Filtrování objednávek', array('legend'=> 'Nastavte filtr pro report objednávek'));
$dateTimeFormatIso = Mage::app()->getLocale()->getDateTimeFormat(Mage_Core_Model_Locale::FORMAT_TYPE_SHORT);
$fieldset->addField('date_from', 'date', array(
'label' => 'Změna statusu objednávek od:',
'title' => 'Změna statusu objednávek od:',
'time' => true,
'name' => 'filter_date_from',
'image' => $this->getSkinUrl('images/grid-cal.gif'),
'format' => $dateTimeFormatIso,
'required' => true,
));
$fieldset->addField('export_history_order_status_changed', 'submit', array(
'label' => 'Exportovat do souboru:',
'value' => 'Export',
'name' => 'export_history_order_status_changed',
'class' => 'form-button',
//'onclick' => "setLocation('{$this->getUrl('*/*/export')}')",
));
$form->setUseContainer(true);
return parent::_prepareForm();
}

Symfony 3.3 Form - EntityType field does not select option

For some unknown reason, the EntityType form field will not display the selected option on submit, even though the name of the column matches and data passes.
I've created a form that I'm using to select some values that will filter a list of products.
<?php namespace AppBundle\Filter;
use AppBundle\Entity\ProductCategory;
use AppBundle\Repository\ProductCategoryRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class ProductFilterType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id', null, [
'required' => false,
'label' => 'SKU'
])
->add('productCategory', EntityType::class,
array(
'class' => ProductCategory::class,
'choice_label' => 'name',
'choice_value' => 'id',
'placeholder' => '',
'label_attr' => array('title' => 'Category for this product'),
'query_builder' => function (ProductCategoryRepository $v) {
return $v->createQueryBuilder('v')
->orderBy('v.name',' ASC');
}
))
->add('name', null, [
'required' => false,
])
->add('description', null, [
'required' => false,
])
;
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'app_bundle_product_filter_type';
} }
The form renders as expected, and it submits a post to the same URL, which filters based on the value received from the request.
That works ok.
However, when the form is re-rendered, the option that was selected before the form filter submission is no longer selected. All other inputs are repopulating.
I did notice that when I'm working with a form that is bound to an Entity (ie: Editting and saving the entity) and using the ConfigureOptions method to set the data class, that the EntityType form field works as expected. However, I need it to work in this case where the overall form is not bound to an Entity.
EDIT:
Doing these steps worked for me...but it seems a bit odd.
Injected entity manager into the form constructor:
public $em;
public function __construct(EntityManager $em) {
$this->em = $em;
}
Then updated the EntityType form field to get the object based on the array value:
->add('productCategory', EntityType::class,
array(
'class' => ProductCategory::class,
'choice_label' => 'name',
'choice_value' => 'id',
'placeholder' => '',
'label_attr' => array('title' => 'Category for this product'),
'data' => $this->em->getReference("AppBundle:ProductCategory",
isset($options['data']['productCategory']) ? $options['data']['productCategory'] : 0),
'query_builder' => function (ProductCategoryRepository $v) {
return $v->createQueryBuilder('v')
->orderBy('v.name',' ASC');
}
))
...
Another solution is using a Data Transformer.
Remove the data attribute from the productCategory type, and add a data transformer to the end of the build method:
$builder->get('productCategory')
->addModelTransformer(new CallbackTransformer(
function ($id) {
if (!$id) {
return;
}
return $this->em->getRepository('AppBundle:ProductCategory')->find($id);
},
function($category) {
return $category->getId();
}
));
If you use the same transformer in multiple places, you can extract it into its own class.
My workaround was like this ...
pass data and entity manager to formType.
$form = $this->createForm(new xxxType($this->get('doctrine.orm.entity_manager')), xxxEntity, array(
'method' => 'POST',
'action' => $this->generateUrl('xxxurl', array('id' => $id)),
'selectedId' => xxxId,
));
setDefaultOptions in form Type initialize as an empty array for selectedId
$resolver->setDefaults(array(
'data_class' => 'xxx',
'selectedId' => array()
));
and in builder
->add('productCategory', EntityType::class,
array(
'class' => ProductCategory::class,
'choice_label' => 'name',
'choice_value' => 'id',
'placeholder' => '',
'label_attr' => array('title' => 'Category for this product'),
'query_builder' => function (ProductCategoryRepository $v) {
return $v->createQueryBuilder('v')
->orderBy('v.name',' ASC');
},
'data'=>$this->em->getReference("xxx",$options['selectedId'])
))
for more details, you can see this answer Symfony2 Setting a default choice field selection

Zf2 - Doctrine Hydrator Fieldset

I'm new with ZendFramework and Doctrine 2.
I try to implement a form with fieldset and Doctrine Hydrator.
The form and validators work good, but not Hydrator.
My source code:
AuthController.php
public function signupAction()
{
$userForm = new UserForm($this->getServiceLocator()->get('Doctrine\ORM\EntityManager'));
$request = $this->getRequest();
if ($request->isPost()) {
var_dump($request->getPost());
$userForm->setData($request->getPost());
if ($userForm->isValid()) {
$user = $userForm->getData();
var_dump($user);
}
}
return new ViewModel(array(
'form' => $userForm,
));
}
UserForm.php
class UserForm extends Form
{
public function __construct($em) {
parent::__construct('User');
$this->setAttribute('method', 'post')
->setHydrator(new DoctrineObject($em, 'User\Entity\User'));
$this->add(array(
'type' => 'User\Form\Fieldset\UserFieldset',
'options' => array(
'use_as_base_fieldset' => true
)
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Send'
)
));
}
}
UserFieldset.php
class UserFieldset extends Fieldset implements InputFilterProviderInterface, ObjectManagerAwareInterface
{
/**
* #var ObjectManager
*/
private $objectManager;
public function __construct()
{
parent::__construct('User');
// Id
$this->add(array(
'name' => 'id',
'type' => 'Zend\Form\Element\Hidden',
));
// FirstName
$this->add(array(
'name' => 'firstName',
'attributes' => array(
'required' => 'required',
),
'options' => array(
'label' => 'Prénom',
),
'type' => 'Zend\Form\Element\Text',
));
}
public function getInputFilterSpecification()
{
return array(
'firstName' => array(
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 2,
'max' => 100,
'messages' => array(
\Zend\Validator\StringLength::TOO_LONG => 'Le prénom est trop long (maximum %max% caractères)',
\Zend\Validator\StringLength::TOO_SHORT => 'Le prénom est trop court (minimum %min% caractères)',
),
),
),
),
),
'id' => array(
'required' => false,
),
);
}
public function setOption($key, $value) { }
/**
* Set the object manager
*
* #param ObjectManager $objectManager
*/
public function setObjectManager(ObjectManager $objectManager)
{
$this->objectManager = $objectManager;
}
/**
* Get the object manager
*
* #return ObjectManager
*/
public function getObjectManager()
{
return $this->getObjectManager();
}
}
I have not a User object, but a array...
A idea?
Thanks you!
Aurélien

How to validate my form in Zend Framework 2 and Doctrine?

so i have some entities and i want to validate my forms, i use Zend Framework 2 and Doctrine Orm, Update Thanks to Notuser : now i get this error :
Fatal error: Call to undefined method Zend\ServiceManager\ServiceManager::initForm() in C:\wamp2\www\test\module\Application\src\Application\Controller\BlogController.php
this is my model.config.php :
'doctrine' => array(
'driver' => array(
'application_entities' => array(
'class' =>'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(__DIR__ . '/../src/Application/Entity')
),
'application_forms' => array(
'class' =>'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(__DIR__ . '/../src/Application/Form')
),
'application_inputs_filters' => array(
'class' =>'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(__DIR__ . '/../src/Application/InputFilter')
),
'orm_default' => array(
'drivers' => array(
'Application\Entity' => 'application_entities',
'Application\Form' => 'application_forms',
'Application\InputFilter' => 'application_inputs_filters'
)
)
)
),
and this my controller :
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Application\Entity\Article;
use Application\Entity\Image;
use Application\Form\ArticleForm;
class BlogController extends AbstractActionController
{
protected $_objectManager;
public function addAction()
{
$form = $this->getServiceLocator('Application\Form\ArticleForm');
$form->initForm();
$request = $this->getRequest();
$form->setData($request->getPost());
$article = new Article();
if ($this->zfcUserAuthentication()->hasIdentity()) {
if ($form->isValid())
{
$file = $this->params()->fromFiles('url');
$adapter = new \Zend\File\Transfer\Adapter\Http();
$adapter->setDestination('public/upload');
if($adapter->receive($file['name'])){
$article->setTitle($this->getRequest()->getPost('title'));
$article->setDate(new \DateTime($this->getRequest()->getPost('date')));
$article->setContent($this->getRequest()->getPost('content'));
$article->setPublication($this->getRequest()->getPost('publication'));
$image = new Image();
$image->setUrl($file['name']);
$image->setAlt($this->getRequest()->getPost('alt'));
$article->setImage($image);
$this->getObjectManager()->persist($article);
$this->getObjectManager()->flush();
$newId = $article->getId();
return $this->redirect()->toRoute('blog');
}
}
}
else
{
return $this->redirect()->toRoute('user');
}
return new ViewModel(array('article' => $article));
}
And this is my ArticleForm :
class ArticleForm extends Form {
public function __construct()
{
parent::__construct('UserEntry');
$this->setAttribute('method', 'post');
$this->setAttribute('enctype', 'multipart/form-data');
$this->setAttribute('class', 'contact_form');
}
/**
*
*/
public function initForm()
{
$this->addFormFields(); //function where we added all fields
$articleInputFilter = new ArticleInputFilter();
$this->setInputFilter($articleInputFilter->getInputFilter()); //Asign input Filter to form
}
/**
*
*/
protected function addFormFields()
{
$this->addSubmit();
$this->addTitle();
$this->addContent();
$this->addDate();
$this->addPublication();
$this->addImage();
}
/**
*
*/
protected function addTitle()
{
$this->add(array(
'name' => 'title',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => _('Title')
),
));
}
/**
*
*/
protected function addContent()
{
$this->add(array(
'name' => 'content',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => _('Content')
),
));
}
/**
*
*/
protected function addDate()
{
$this->add(array(
'name' => 'date',
'attributes' => array(
'type' => 'date',
),
'options' => array(
'label' => _('Date'),
'id' => 'datepicker',
),
));
}
/**
*
*/
protected function addPublication()
{
$this->add(array(
'name' => 'publication',
'attributes' => array(
'type' => 'checkbox',
),
'options' => array(
'label' => _('Publication'),
'use_hidden_element' => true,
'checked_value' => 1,
'unchecked_value' => 'no',
),
));
}
/**
*
*/
protected function addImage()
{
$this->add(array(
'name' => 'Image',
'attributes' => array(
'type' => new ImageForm(),
),
'options' => array(
'label' => _('Image')
),
));
}
/**
*
*/
protected function addSubmit()
{
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => _('Add'),
'class' => 'submit',
),
));
}
}
Finally this is my ArticleInputFilter :
class ArticleInputFilter extends InputFilter implements InputFilterAwareInterface
{
/**
* #var string
*/
public $title;
/**
* #var int
*/
public $image;
/**
* #var string
*/
public $content;
/**
* #var Date
*/
public $date;
/**
* #var Boolean
*/
public $publication;
/**
* #param $data
*/
public function exchangeArray($data)
{
$this->title = (isset($data['title'])) ? $data['title'] : $this->title;
$this->image = (isset($data['image'])) ? $data['image'] : $this->image;
$this->content = (isset($data['content'])) ? $data['content'] : $this->content;
$this->publication = (isset($data['publication'])) ? $data['publication'] : $this->publication;
$this->date = (isset($data['date'])) ? $data['date'] : $this->date;
}
/**
* #param InputFilterInterface $inputFilter
* #return void|InputFilterAwareInterface
* #throws \Exception
*/
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Not used");
}
/**
* #return InputFilter|InputFilterInterface
*/
public function getInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'title',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 6,
'max' => 100,
),
),
),
)));
$inputFilter->add($factory->createInput(array(
'name' => 'content',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 10,
),
),
),
)));
$inputFilter->add($factory->createInput(array(
'name' => 'publication',
'required' => false,
)));
$inputFilter->add($factory->createInput(array(
'name' => 'date',
'required' => true,
)));
$inputFilter->add($factory->createInput(array(
'name' => 'image',
'required' => true,
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
That it i write my hole code except my ImageForm and ImageInputFilter, So please if someone has any idea how to do that i will be very appreciative ;)
In controller
$form = $this->getServiceLocator('Application\Form\BlogForm");//don't forger to add Application\Form\BlogForm to module.config.php and check your namespace
$form->initForm();//explained further
/** #var \Zend\Http\Request $request */
$request = $this->getRequest();//whole request fo #var for further using
if ($request->isPost()) { //there is something in post
$form->setData($request->getPost()); //data from post is added for further validation
if ($form->isValid()) { //Form is Valid lets save form
$files = $request->getFiles()->toArray(); //array witch files use var_dump($files);die; to show structure
$article = new Article();
$article->setTitle($request->getPost('title'));
...
if(!empty($files)&&$files['image']['error']==0) {
$article->setImage($functionToCreateImageEntityFromFile(($files['image']));
}
...
$this->getObjectManager()->persist($article);
$this->getObjectManager()->flush();
$newId = $article->getId();
return $this->redirect()->toRoute('blog');
}
//form is not valid so you can display form with Errors
} //there is no post so you can display clean form
Next let's take form:
(it's my proposal)
namespace Application\Form; //check your namespace
use Zend\Form\Form;
use Application\InputFilter\BlogInputFilter; //check your namespace
class BlogForm extends Form {
public function __construct()
{
parent::__construct('UserEntry');
$this->setAttribute('method', 'post');
$this->setAttribute('enctype', 'multipart/form-data');
}
/**
*
*/
public function initForm()
{
$this->addFormFields(); //function where we added all fields
$blogInputFilter = new BlogInputFilter(); //Input Filter for Validation
$this->setInputFilter($BlogInputFilter->getInputFilter()); //Asign input Filter to form
}
/**
*
*/
protected function addFormFields()
{
$this->addSubmit();
$this->addTitle();
$this->addImage();
...//add more here
}
/**
*
*/
protected function addTitle()
{
$this->add(array(
'name' => 'title',
'attributes' => array(
'type' => 'text',
'id' => 'title',
'class' => 'text'
),
'options' => array(
'label' => _('Title') //it's only for multilang you can put here any string
),
));
}
/**
*
*/
protected function addImage()
{
$this->add(array(
'name' => 'image',
'attributes' => array(
'type' => 'file',
'id' => 'image'
),
'options' => array(
'label' => _('Image'),
),
));
}
/**
*
*/
protected function addSubmit()
{
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => _('Save'),
'class' => 're',
),
));
}
}
Now its Time for InputFilter
namespace Application\InputFilter;//check your namespace
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
class BlogInputFilter implements InputFilterAwareInterface
{
/**
* #var int
*/
public $title;
/**
* #var string
*/
public $image;
//add all fields
/**
* #param $data
*/
public function exchangeArray($data)
{
$this->title = (isset($data['title'])) ? $data['title'] : $this->title;
$this->image = (isset($data['image'])) ? $data['image'] : $this->image;
//add fields
}
/**
* #param InputFilterInterface $inputFilter
* #return void|InputFilterAwareInterface
* #throws \Exception
*/
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Not used");
}
/**
* #return InputFilter|InputFilterInterface
*/
public function getInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'title',
'required' => true,//required field
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 1,
'max' => 100,//limit from 1 to 100 chars
),
),
),
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
to read:
Forms: http://framework.zend.com/manual/2.0/en/modules/zend.form.quick-start.html#factory-backed-form-extension
ImputFilter: http://framework.zend.com/manual/2.0/en/modules/zend.input-filter.intro.html
File upload: Can't find good tutorial
module.config.php should have key like this:
'service_manager' => array(
'invokables' => array(
'Application\Form\ArticleForm' => 'Application\Form\ArticleForm',
),
),
There is no need to add Application\Form\ArticleForm to use becouse you use ServiceLocator
This is your problem:
$form = $this->getServiceLocator('Application\Form\ArticleForm');
$form->initForm();
It should be:
$form = $this->getServiceLocator()->get('Application\Form\ArticleForm');
$form->initForm();
The way you have written it, the bracket contents are ignored and it becomes:
$form = $this->getServiceLocator()
which is why you get an "undefined method: ServiceManager::initForm()" error when you call initForm().

$form->isValid says it is invalid, but my form does not show any error message - Zend Framework 2

Im having a problem with the addAction in my CRUD application. On the controller the logic does not pass the $form->isValid() verification but the form does not show any error message.
I tried with this (Thanks Sam):
foreach($form->get('product')->getElements() as $el)
{
echo $el->getName()." = ".$el->getValue()." > ".$el->getMessages()." <br/>";
}
That only show the name and value of the field, but not the error message.
I've tried letting the form totally blank and it fire "Value is required and can't be empty" error messages, but then, i fill each field one by one until i don't get more error messages but the form still invalid.
My form has a Product Fieldset as a base fieldset and a submit button. Inside Product Fieldset i have an ID field, a name field, a price field and a Brand Fieldset. Inside my Brand Fieldset i have a id field. Like this:
ProductForm:
class ProductForm extends Form
{
public function init()
{
// we want to ignore the name passed
parent::__construct('product');
$this->setName('product');
$this->setAttribute('method', 'post');
$this->add(array(
'name' => 'product',
'type' => 'Administrador\Form\ProductFieldset',
'options' => array(
'use_as_base_fieldset' => true
),
));
$this->add(array(
'name' => 'submit',
'type' => 'Submit',
'attributes' => array(
'value' => 'Add',
'id' => 'submitbutton',
),
));
}
}
ProductFieldset:
class ProductFieldset extends Fieldset implements ServiceLocatorAwareInterface
{
protected $serviceLocator;
function __construct($name = null)
{
parent::__construct('product_fieldset');
$this->setHydrator(new ArraySerializableHydrator());
$this->setObject(new Product());
}
public function init()
{
$this->add(array(
'name' => 'id',
'type' => 'Hidden',
));
$this->add(array(
'name' => 'name',
'type' => 'Text',
'options' => array(
'label' => 'Name',
),
));
$this->add(array(
'name' => 'price',
'type' => 'Text',
'options' => array(
'label' => 'Price',
),
));
$this->add(array(
'name' => 'brand',
'type' => 'BrandFieldset',
));
}
public function setServiceLocator(ServiceLocatorInterface $sl)
{
$this->serviceLocator = $sl;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
}
BrandFieldset:
class BrandFieldset extends Fieldset
{
function __construct(BrandTable $brandTable)
{
parent::__construct('brand_fieldset');
//$this->setHydrator(new ClassMethodsHydrator(false))->setObject(new Brand());
$this->setHydrator(new ArraySerializableHydrator());
$this->setObject(new Brand());
$brandSelectOptionsArray = $brandTable->populateSelectBrand();
$this->add(array(
'name' => 'id',
'type' => 'Select',
'options' => array(
'label' => 'Brand',
'empty_option' => 'Please select a brand',
'value_options' => $brandSelectOptionsArray,
),
));
}
}
This is my new Form statement in the addAction:
$formManager = $this->serviceLocator->get('FormElementManager');
$form = $formManager->get('Administrador\Form\ProductForm');
Inside my model 'Product' i have the inputFilters, required filter for 'id' field, required filter for 'name' field. And for the Brand field i created other inputFilter and added it to the main inputFilter:
$brandFilter->add($factory->createInput(array(
'name' => 'id',
'required' => true,
'filters' => array(
array('name' => 'Int'),
),
)));
$inputFilter->add($brandFilter, 'brand');
The weird behavior is that my editAction works fine and has the same logic.
Is it there any form of echoing an internal error message from the form, something that helps me to understand WHY the form is not valid.
EDIT 2013-06-01
Here is my full Controller:
class ProductController extends AbstractActionController
{
protected $productTable;
protected $brandTable;
public function indexAction()
{
return new ViewModel(array(
'products' => $this->getProductTable()->fetchAll(),
));
}
public function addAction()
{
$formManager = $this->serviceLocator->get('FormElementManager');
$form = $formManager->get('Administrador\Form\ProductForm');
$form->get('submit')->setValue('Add');
$request = $this->getRequest();
if ($request->isPost()) {
$product = new Product();
$product->brand = new Brand();
$form->setInputFilter($product->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$product->exchangeArray($form->getData());
$this->getProductTable()->saveProduct($product);
// Redirect to list of products
return $this->redirect()->toRoute('product');
}
}
return new ViewModel(array(
'form' => $form,
));
}
public function editAction()
{
$id = (int) $this->params()->fromRoute('id', 0);
if (!$id) {
return $this->redirect()->toRoute('product', array(
'action' => 'add'
));
}
// Get the Product with the specified id. An exception is thrown
// if it cannot be found, in which case go to the index page.
try {
$product = $this->getProductTable()->getProduct($id);
}
catch (\Exception $ex) {
return $this->redirect()->toRoute('product', array(
'action' => 'index'
));
}
$formManager = $this->serviceLocator->get('FormElementManager');
$form = $formManager->get('Administrador\Form\ProductForm');
$brand = $this->getBrandTable()->getBrand($product->brand);
$product->brand = $brand;
$form->bind($product);
$form->get('submit')->setAttribute('value', 'Edit');
$request = $this->getRequest();
if ($request->isPost()) {
$form->setInputFilter($product->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$this->getProductTable()->saveProduct($form->getData());
// Redirect to list of products
return $this->redirect()->toRoute('product');
}
}
return array(
'id' => $id,
'form' => $form,
);
}
public function deleteAction()
{
$id = (int) $this->params()->fromRoute('id', 0);
if (!$id) {
return $this->redirect()->toRoute('product');
}
$request = $this->getRequest();
if ($request->isPost()) {
$del = $request->getPost('del', 'No');
if ($del == 'Yes') {
$id = (int) $request->getPost('id');
$this->getProductTable()->deleteProduct($id);
}
// Redirect to list of products
return $this->redirect()->toRoute('product');
}
return array(
'id' => $id,
'product' => $this->getProductTable()->getProduct($id)
);
}
public function getProductTable()
{
if (!$this->productTable) {
$sm = $this->getServiceLocator();
$this->productTable = $sm->get('Administrador\Model\ProductTable');
}
return $this->productTable;
}
public function getBrandTable()
{
if (!$this->brandTable) {
$sm = $this->getServiceLocator();
$this->brandTable = $sm->get('Administrador\Model\BrandTable');
}
return $this->brandTable;
}
}
My case was I passed wrong input filter. isValid returns false, but $form->getMessages() is empty. Form OrderForm had the following:
$form->setInputFilter(new \Application\Form\UserInputFilter($er));
When I changed UserInputFilter to OrderInputFilter it works.
Well, I got the answer :D
This is how the addAction should be:
public function addAction()
{
$formManager = $this->serviceLocator->get('FormElementManager');
$form = $formManager->get('Administrador\Form\ProductForm');
$form->get('submit')->setValue('Add');
$product = new Product();
$product->brand = new Brand();
$form->bind($product); // I need to bind the product to the form to pass the isValid() validation
$request = $this->getRequest();
if ($request->isPost()) {
$form->setInputFilter($product->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$product = $form->getData();
$this->getProductTable()->saveProduct($product);
// Redirect to list of products
return $this->redirect()->toRoute('product');
}
}
return new ViewModel(array(
'form' => $form,
));
}
Apparently i needed to bind and empty product object to the form to be able to pass the isValid() validation. After that i retrieve a product object from the $form->getData().
You can also do: $form->setBindOnValidate(false);