I'm am running into more issues than I can list trying to figure out how in the &@#$*& to get Doctrine working in Zend 2 with forms for populating data. (the available documentation bites!!!)
I'm just trying to populate a simple select list from the records in a specific table/Entity. The Entity only has two properties. An ID and a name. I want the value to be the ID and the option innerHTML text to be the name. No brainer.
The main object I am trying to write the form for is called 'Lists' and has 3 properties. A ListId, a TypeId and a ListName. In Doctrine, it is a Many-to-One relationship for the Type and the Types object is the one I want to use for the selector. (properties: TypeId and TypeName)
I created a basic form, and set up a controller action to retrieve it via the service manager using the FormElementManager. I then bind it to an instance of the Entity. Eventually, if ListId is provided, I will retrieve that record before binding, but for now, I just create a new (empty) Lists object.
Problem 1:
First I tried using a fieldset following nearly line-for-line from the ZF2 docs. When I create my fieldset using the service locator aware code, it gets the getServiceLocator() method, which as per many references I see, returns the FormElementManager in form context. Thus the recommendation was to call getServiceLocator() again on the FormElementManager to get the actual Service locator in Zend. The method exists but returns 'null' from my Fieldset. I have both the form and the fieldset defined in modules.config.php under form_elements:
'form_elements' => array(
'invokables' => array(
'listForm' => 'Application\Form\ListForm',
'typeFieldset' => 'Application\Form\Fieldset\TypeFieldset'
)
)
And I reference the fieldset from the form (in init() function) as follows:
$this->add(
array(
'name' => 'Type',
'type' => 'typeFieldset',
'options' => array(
'label' => 'List Type',
),
'attributes' => array(
'required' => 'required'
)
)
);
If I can't get to the full Service manager, I can't use Doctrine to populate the fieldset properly.
Problem 2:
I found another example that suggested moving the service-locator-aware code up to the form then use Doctrine as the hydrator. I got that one to work and get the field populated as I wish and got the labels properly filled in using the callback function.
$this->add(
array(
'name' => 'TypeId',
'type' => 'DoctrineORMModule\Form\Element\DoctrineEntity',
'options' => array(
'label' => 'Type',
'object_manager' => $entityManager,
'target_class' => 'Application\Entity\Types',
'property' => 'TypeId',
'label_generator' => function($targetEntity) {
return $targetEntity->getTypeId() . ' - ' . $targetEntity->getTypeName();
},
'display_empty_item' => true,
'empty_item_label' => '---',
'find_method' => array(
'name' => 'findBy',
'params' => array(
'criteria' => array(),
'orderBy' => array('TypeId' => 'ASC'),
),
),
),
)
);
This caused me to start playing with the input of the Id to pre-populate the form. The problem is that the Id field is then conditional and it causes problems if the Id is blank. I'm assuming that the hidden field for the Id will either need to be set conditional, or I somehow need to parse it conditionally before the hydrator tries to store the result. I can't seem to find any documentation on how to do this. For the time being, I did a cludge fix by adding a 'addId' method to the form that I can call from the controller if ListId was a parameter and is a valid Integer that corresponds to an existing record.
public function editlistAction() {
$listId = $this->params()->fromRoute('id', '');
$form = $this->getForm('listForm');
$list = null;
if(!empty($listId) && (intval($listId) > 0)) {
$list = $this->getMapper()->findRecordById($listId);
$form->addId($listId); // function that adds hidden element
} else {
$list = new \Application\Entity\Lists();
}
$form->bind($list);
if ($this->request->isPost()) {
// do something with the result
}
return array(
'listId' => $listId,
'form' => $form
);
}
I then only try to add the hidden element in the view if the $form->has('ListId') returns true. Is there a better way to do this?
Problem 3:
In the view if I just echo $this->formCollection($form); it will display all of the proper fields, but is unformatted and ugly. So I was trying to figure out how to go field by field so I could format around them.
When I tried doing an echo $this->formCollection($form->get('ListId')) (or any other field name) I keep getting an error 'undefined method' for the type::getIterator() (e.g. undefined method Zend....\Hidden::getIterator() ). So I tried calling the various decorators directly such as: $this->formText($form->get('ListName')); but that drops the label field. Is there a good damn example anywhere on the internet of how to properly format a Zend Framework 2 Form that doesn't read like pig-latin in greek hiroglyphs?
Problem 4:
So I resorted to using the ugly form, to just see if I could get the back-end functionality to work. The only thing that I added to the controller action was to check $form->isValid() (currently I have not set any validators) and if true, dump the bound object. When I do this, it pukes yet again - go figure. It seems to be having trouble with the fact that one of the bound parameters from the form is called 'submit'. Do I actually have to manually remove parameters from standard form elements prior to hydration somewhere and/or change the basic suggested 'exchangeArray()' method in the entity to ignore them? Seriously!??!?! If there is a way to remove them, where? (I'm not even 100% sure at what point the hydration is actually occuring) And if not, how do I avoid this from effecting the Zend/Doctrine hydration?
.
I have been at this for more than 6 hours now, have about 30 tabs open on my browser (not counting the 120 or so I closed already) and have done more google searches today than I care to recall. It seems everyone does each step subtly different and there is no one definitive source for a good, basic, howto. (I sat and stared blankly at one Zend Framework documentation page that went on for 80% of the page just setting up an extremely lame set of objects for a one paragraph description of how to bind it to a decorator, only before realizing it was talking about ZF1 groan)
Any help is greatly appreciated. I have about 2 dozen more gray hairs, my bloodpressure is up about 30 points and my voice is hoarse from yelling at the monitor.