i am using zendframework 2 and doctrine 2. i want to populate the values of my MultiCheckbox from values in my database .
i got the technique from: https://github.com/doctrine/DoctrineModule/blob/master/docs/form-element.md
namespace Users\Form;
use Zend\Form\Form;
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
use Doctrine\Common\Persistence\ObjectManager;
class addForm extends form implements ObjectManagerAwareInterface
{
protected $objectManager;
public function setObjectManager(ObjectManager $objectManager)
{
$this->objectManager = $objectManager;
}
public function getObjectManager()
{
return $this->objectManager;
}
public function __construct($name = null)
{
parent::__construct('add');
$this->setAttribute('method', 'post');
$this->setAttribute('enctype','multipart/formdata');
$this->add(array(
'type' => 'DoctrineModule\Form\Element\ObjectMultiCheckbox',
'name' => 'option',
'options' => array(
'label' => 'Options Véhicule',
'object_manager' => $this->getObjectManager(),
'target_class' => 'Users\Entity\optionsvehicule',
'property' => 'property'
, )));
the error message i received:
No object manager was set.
I have tried and found similar error. After some search I found solution posted on https://github.com/doctrine/DoctrineModule/issues/175. Which works.
For implement you need to do some changes like that
In Module.php add method getFormElementConfig :
public function getFormElementConfig()
{
return array(
'invokables' => array(
'addForm' => 'Users\Form\addForm',
),
'initializers' => array(
'ObjectManagerInitializer' => function ($element, $formElements) {
if ($element instanceof ObjectManagerAwareInterface) {
$services = $formElements->getServiceLocator();
$entityManager = $services->get('Doctrine\ORM\EntityManager');
$element->setObjectManager($entityManager);
}
},
),
);
}
In Your Form Class addForm.php, replace constructor with init method :
namespace Users\Form;
use Zend\Form\Form;
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
use Doctrine\Common\Persistence\ObjectManager;
class addForm extends form implements ObjectManagerAwareInterface
{
protected $objectManager;
public function setObjectManager(ObjectManager $objectManager)
{
$this->objectManager = $objectManager;
}
public function getObjectManager()
{
return $this->objectManager;
}
//public function __construct($name = null)
public function init()
{
$this->setAttribute('method', 'post');
$this->setAttribute('enctype','multipart/formdata');
$this->add(array(
'type' => 'DoctrineModule\Form\Element\ObjectMultiCheckbox',
'name' => 'option',
'options' => array(
'label' => 'Options Véhicule',
'object_manager' => $this->getObjectManager(),
'target_class' => 'Users\Entity\optionsvehicule',
'property' => 'property'
, )));
In Your Controller Class, Call form obejct through Service Locator :
//$form = new addForm();
$forms = $this->getServiceLocator()->get('FormElementManager');
$form = $forms->get('addForm');
The $objectManager property is undefined.
This is because you call the $this->getObjectManager() method immediately within the __construct() and before you set the variable.
The form depends on the object manager; so you could just add it as a constructor argument which would ensure it is set before the class is used.
Also, the constructor should only really be used for setting up the object's initial properties and state, use init() for modifying form elements.
class addForm extends Form
{
protected $objectManager;
public function __construct(ObjectManager $objectManager)
{
parent::__construct('add-form');
$this->objectManager = $objectManager;
}
// The form element manager will call `init()`
// on the form so we can add the elements in this method
public function init() {
//....
$this->setAttribute('method', 'post');
$this->setAttribute('enctype','multipart/formdata');
// $this->add(....
// more elements added here
}
}
Last thing is to register a factory that actually does the injection
class Module {
public function getFormElementConfig() {
return array(
'factories' => array(
'ModuleName\Form\FooForm' => function($formElementManager) {
$serviceManager = $formElementManager->getServiceLocator();
$objectManager = $serviceManager->get('ObjectManager');
$form = new Form\FooForm($objectManager);
return $form;
},
),
);
}
}
Related
Can implement dynamic validation on element level? I used this example to implement validation of one element dependent on the value of the other element. But for this purpose I'll need to implement this for every single form where I use this element (comment) with this validation. I have many forms like that. Is there way to do the following:
to take this filter/validation logic to the element level using some kind of "data-comment-for" attribute and retrieving the value of the element on which it depends from the parent form.
This is my current code (but I need to have it for every form now. It does not look elegant at all) :
class CompetencyAdvanceHumanRightsAndJusticeFormFilter extends InputFilter
{
public function isValid($context = null)
{
$figradeCommentName = 'applJusticeFIGrade'.'Comment';
$forGrade = $this->get('applJusticeFIGrade');
$gradeComment = $this->get($figradeCommentName);
$applJusticeFIGradeRawValue = $forGrade->getRawValue('applJusticeFIGrade');
if(is_numeric($applJusticeFIGradeRawValue)){
$gradeValue = intval($applJusticeFIGradeRawValue);
}else{
$gradeValue = $applJusticeFIGradeRawValue;
}
if ($gradeValue != 'na' && $gradeValue > 0) {
$gradeComment->setRequired(true);
$validatorChain = new Validator\ValidatorChain();
$validatorChain->attach(
new Validator\NotEmpty(),
true
);
$gradeComment->setValidatorChain($validatorChain);
}
return parent::isValid($context);
}
public function __construct(){
$this->add(array(
'name' => 'id',
'required' => true,
'filters' => array(
array('name' => 'Int'),
),
));
$this->add(array(
'name' => 'studEvalId',
'required' => true,
'filters' => array(
array('name' => 'Int'),
),
));
}
}
EDIT:
I added code for the custom element to the question. There are some "leftovers" of my attempts to place this logic to the element level.
Comment Element
class Comment extends Element implements InputProviderInterface
{
/**
* #var ValidatorInterface
*/
protected $validator;
// set its type
protected $attributes = array(
'type' => 'comment'
);
public function init()
{
if (null === $this->validator) {
$validator = new StringLength();
$validator->setMax(10);
$validator->setMessage('The comment should not exceed 1000 letters!', StringLength::INVALID);
$this->validator = $validator;
}
}
/**
* Get a validator if none has been set.
*
* #return ValidatorInterface
*/
public function getValidator()
{
return $this->validator;
}
/**
* #param ValidatorInterface $validator
* #return $this
*/
public function setValidator(ValidatorInterface $validator)
{
$this->validator = $validator;
return $this;
}
/**
* remove require and validator defaults because we have none
*
* #return array
*/
public function getInputSpecification()
{
// return array(
// 'name' => $this->getName(),
// 'required' => false,
// 'validators' => array(
// $this->getValidator(),
// ),
// 'filters' => array(
// new FIGradeCommentDynamicBufferFilter()
// ),
// );
return array(
'name' => $this->getName(),
'required' => false,
'filters' => array(
array('name' => 'Zend\Filter\StringTrim'),
),
'validators' => array(
$this->getValidator(),
),
);
}
// tell it where to find its view helper, so formRow and the like work correctly
public function getViewHelperConfig()
{
return array('type' => '\OnlineFieldEvaluation\View\Helper\FormComment');
}
}
You could make a base abstract input-filter class and an interface and make all your form filters extend the base class that implements the interface with the methods you expect inside your form classes to make the thing work correctly.
Make an interface with the methods:
interface GradeCommentFormFilterInterface()
{
protected function getGradeInput();
protected function getCommentInput();
}
Then you move the common code to your base class:
abstract class BaseGradeCommentFormFilter extends InputFilter implements GradeCommentFormFilterInterface
{
protected function getGradeInput()
{
return $this->get(static::GRADE_NAME);
}
protected function getCommentInput()
{
return $this->get(static::GRADE_NAME . 'Comment');
}
public function isValid($context = null)
{
$gradeInput = $this->getGradeInput();
$commentInput = $this->getCommentInput();
$rawValue = $this->getRawValue($gradeInput);
if(is_numeric($rawValue))
{
$gradeValue = intval($rawValue);
}
else
$gradeValue = $rawValue;
if ($gradeValue != 'na' && $gradeValue > 0) {
$commentInput->setRequired(true);
$validatorChain = new Validator\ValidatorChain();
$validatorChain->attach(
new Validator\NotEmpty(),
true
);
$commentInput->setValidatorChain($validatorChain);
}
return parent::isValid($context);
}
}
Now you can use your abstract class like this:
class CompetencyAdvanceHumanRightsAndJusticeFormFilter extends BaseGradeCommentFormFilter
{
const GRADE_NAME = 'applJusticeFIGrade';
//... other code
}
I quickly tried to make it work for your case, but this isn't tested, and probably there are ways to optimize this, but it gives you an idea of what you can do.
I created a Validator for one of my form fields. To do that, I need the ServiceLocator so I would like to use a factory ...
Edit :
Here is my factory :
namespace Maintenance\Factory\Validator;
/* Zend */
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
/* Controller */
use Maintenance\Validator\Echeancedebut;
class EcheancedebutFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$realServiceLocator = $serviceLocator->getServiceLocator();
$maiContratService = $realServiceLocator->get(
'Maintenance\Service\Model\FMaiContratService'
);
return new Echeancedebut($maiContratService);
}
}
My Validator :
class Echeancedebut extends AbstractValidator
{
const ERROR_DATEDEB = 'ERROR_DATEDEB';
protected $maiContratService;
protected $messageTemplates = array(
self::ERROR_DATEDEB => "Saisie inférieure à la date de début du contrat"
);
public function __construct($maiContratService) {
$this->maiContratService = $maiContratService;
}
public function isValid($value){
$this->setValue($value);
if (!$this->validatedate($value)) {
$this->error(self::ERROR_DATEDEB);
return false;
}
return true;
}
private function validatedate($date) {
return false;
}
}
Edit :
My InputFilter:
public function getInputFilter()
{
if (! $this->inputFilter) {
$inputFilter = new InputFilter();
$this->inputFilter = $inputFilter;
}
$inputFilter->add(
array(
'name' => 'dateDeb',
'required' => true,
'allow_empty' => false,
'validators' => array(
array(
'name' => 'Date',
'locale' => 'FR_fr',
'options' => array(
'format' => 'd/m/Y',
),
),
array(
'name' => 'Maintenance\Validator\Echeancedebut',
'options' => array(
'contratId' => $this->iMaiContratId,
)
);
return $this->inputFilter;
}
The problem is that it doesn't retrieve my error message, why ?
The name of your factory and the name you registered in your config are not corresponding:
Maintenance\Factory\Validator\EcheancedebutFactory (inside your config)
Maintenance\Factory\Validator\EcheancedebFactory (the full class name)
Echeancedebut vs Echeancedeb.
Change that and I think it should work.
Okay so injecting Sl in a validator is a bad idea. I will pass my options as arguments via the inputFilter, called by my controller. So the problem is solved. Thanks for your answers.
I am creating a website using Zend Framework 2, and I'm using as an example the exercise from the official course of Zend Technology, Zend Framework 2: Fundamentals.
I have a table called posts and I want to show the table content in my home page, ordered by id. These are the codes I have written:
Controller/PostsTableTrait.php
trait PostsTableTrait
{
private $postsTable;
public function setPostsTable($postsTable)
{
$this->postsTable = $postsTable;
}
}
Controller/IndexController.php
class IndexController extends AbstractActionController
{
use PostsTableTrait;
public function indexAction()
{
return new ViewModel(array(
'post' => $this->postsTable->getPosts()
));
}
}
Factory/IndexControllerFactory.php
class IndexControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$serviceManager = $serviceLocator->getServiceLocator()->get('ServiceManager');
$indexController = new IndexController();
$indexController->setPostsTable($serviceManager->get('Rxe\Factory\PostsTable'));
return $indexController;
}
}
Factory/PostsTableFactory.php
class PostsTableFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
return new PostsTable(PostsTable::$tableName, $serviceLocator->get('Zend\Db\Adapter\AdapterService'));
}
}
Model/PostsTable.php
class PostsTable extends TableGateway
{
public static $tableName = "posts";
public function getPosts()
{
$select = new Select(self::$tableName);
$select->columns(array(
'date',
'title',
'text',
'category'
));
$select->order('id DESC');
return $select;
}
}
config/module.config.php
'controllers' => array(
'invokables' => array(
'Rxe\Controller\Index' => 'Rxe\Controller\IndexController',
'Rxe\Controller\Panel' => 'Rxe\Controller\PanelController'
),
'factories' => array(
'Rxe\Factory\PanelController' => 'Rxe\Factory\PanelControllerFactory'
)
),
'service_manager' => array(
'factories' => array(
'Rxe\Factory\PanelForm' => 'Rxe\Factory\PanelFormFactory',
'Rxe\Factory\PanelFilter' => 'Rxe\Factory\PanelFilterFactory',
'Rxe\Factory\PostsTable' => 'Rxe\Factory\PostsTableFactory',
'Zend\Db\Adapter\AdapterService' => 'Zend\Db\Adapter\AdapterServiceFactory'
)
),
I don't know if the error could be in the getPosts() method. I have tried many different ways to return the query but none of them made any difference, not even showed another error.
You have registered the controller as an 'invokable'. When the the controller manager creates IndexController it will do so without using the IndexControllerFactory; therefore the Rxe\Factory\PostsTable dependency is never set.
To fix this, update module.config.php and register the index controller with your factory class.
'controllers' => [
'factories' => [
'Rxe\Controller\Index' => 'Rxe\Factory\IndexControllerFactory',
],
],
Also (not an error as such) but the IndexControllerFactory calls ->get('ServiceManager') using the service manager.
You could update it to be like this.
class IndexControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $controllerManager)
{
// #var \Zend\ServiceManager\ServiceManager
$serviceManager = $controllerManager->getServiceLocator();
$indexController = new IndexController();
$indexController->setPostsTable($serviceManager->get('Rxe\Factory\PostsTable'));
return $indexController;
}
}
I am creating a form which needs dynamic options based on the route value of survey_question_reference
'main-surveyquestions'=> [
'type' => 'segment',
'options' => [
'route' => '/survey-questions[/:survey_question_reference][/:answer]',
'constraints' => [
'survey_question_reference' => '[0-9]*',
'answer' => '(answer)',
],
'defaults' => [
'controller' => 'Main\Controller\Main',
'action' => 'surveyquestions'
]
]
],
This is the Form code which calls the FormElement:
/**
* Init
*/
public function init()
{
/**
* Survey Answer
*/
$this->add(
[
'type' => 'Main\Form\Element\SurveyAnswerRadio',
'name' => 'survey_answer',
'options' => [
'label' => 'survey_answer'
],
'attributes' => [
'id' => 'survey_answer'
]
]
);
}
The following is the code from the Form Element. Where I have hard coded 'sqReference' => '1' the 1 needs to be replaced with the value of survey_question_reference from the route.
namespace Main\Form\Element;
use Doctrine\ORM\EntityManager;
use Zend\Form\Element\Radio;
/**
* Class SurveyAnswerRadio
*
* #package Main\Form\Element
*/
class SurveyAnswerRadio extends Radio
{
/**
* #var EntityManager $entityManager
*/
protected $entityManager;
/**
* #param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* Get Value Options
*
* #return array
*
* #throws \Exception
*/
public function getValueOptions()
{
$array = [];
$result = $this->entityManager
->getRepository('AMDatabase\Entity\TheVerse\SA')
->findBy(
[
'sqReference' => '1'
],
[
'surveyAnswer' => 'ASC'
]
);
if (is_array($result) && count($result) > '0') {
/**
* #var \AMDatabase\Entity\TheVerse\SA $val
*/
foreach ($result as $val) {
$array[$val->getReference()] = $val->getSurveyAnswer();
}
}
return $array;
}
}
What you're looknig for is to inject the survey_question_reference parameter to your FormElement. You could do that as suggested by #kuldeep.kamboj in his answers. But if you don't want to change your approach and keep your custom SurveyAnswerRadio element, you have to make some fiew changes in your code :
Make SurveyAnswerRadio implements Zend\ServiceManager\ServiceLocatorAwareInterface so that you could implement setServiceLocator and getServiceLocator, which are required by the ServiceManager to automatically inject the service locator when the element is instantiated.
Your form should also implements Zend\ServiceManager\ServiceLocatorAwareInterface.
Implement the getFormElementConfig method in your Module.php.
Let’s look through the code now. You'll have something like this :
SurveyAnswerRadio :
class SurveyAnswerRadio extends Radio implements ServiceLocatorAwareInterface
{
//Add these two methods
public function setServiceLocator(ServiceLocatorInterface $sl)
{
$this->serviceLocator = $sl;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function getValueOptions()
{
$array = [];
$serviceManager = $this->serviceLocator->getServiceLocator();
$em = $serviceManager->get('Doctrine\ORM\EntityManager');
$sqReference = $serviceManager->get('application')->getMvcEvent()
->getRouteMatch()->getParam('survey_question_reference');
$result = $em->getRepository('AMDatabase\Entity\TheVerse\SA')
->findBy(
['sqReference' => $sqReference],
['surveyAnswer' => 'ASC']
);
if (is_array($result) && count($result) > '0') {
foreach ($result as $val) {
$array[$val->getReference()] = $val->getSurveyAnswer();
}
}
return $array;
}
}
Module.php :
Implement the getFormElementConfig method as follows. This allows the class ModuleName\Form\Element\SurveyAnswerRadio to be instantiated, or invoked, with the alias SurveyAnswerRadio.
class Module implements FormElementProviderInterface
{
// other stuff .....
public function getFormElementConfig()
{
return array(
'invokables' => array(
'SurveyAnswerRadio' => 'ModuleName\Form\Element\SurveyAnswerRadio'
)
);
}
}
No changes needed in the Form init method let it as it is.
Note that in your controller, you'll have to instantiate the Form via the FormElementManager :
$formManager = $this->serviceLocator->get('FormElementManager');
$form = $formManager->get('ModuleName\Form\YourForm');
Please see more details in the documentation
See also this post which exaplains how to manage dependencies within a custom Select Element in Form.
I will suggest to change approach. First do not try to extends Radio Element which is not necessary at all. You can do same in your Form Class. Second your entity manager also not work in Radio/Form class until your find mechanism to pass.
So I would suggest solutions like below.
First register your form class into as factory in module.config.php
'form_elements' => array(
'factories' => array(
'Main\Form\YourFormName' => function($sm) {
$form = new Form\YourFormName();
$form->setEntityManager($sm->getServiceLocator()->get('Doctrine\ORM\EntityManager'));
$form->setServiceLocator($sm->getServiceLocator());
return $form;
},
),
),
Then implement entityManager and serviceLocator into your form class.
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class YourFormName extends Form implements ObjectManagerAwareInterface, ServiceLocatorAwareInterface
{
protected $entityManager;
protected $serviceLocator;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function setEntityManager(ObjectManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function getEntityManager()
{
return $this->entityManager;
}
Then in init method you have serviceLocator/entityManager is already initialized.
public function init()
{
$routeMatch = $this->getServiceLocator()->get('Application')->getMvcEvent()->getRouteMatch();
$array = [];
$result = $this->entityManager
->getRepository('AMDatabase\Entity\TheVerse\SA')
->findBy(
[
'sqReference' => $routeMatch->getParam('survey_question_reference')
],
[
'surveyAnswer' => 'ASC'
]
);
if (is_array($result) && count($result) > '0') {
/**
* #var \AMDatabase\Entity\TheVerse\SA $val
*/
foreach ($result as $val) {
$array[$val->getReference()] = $val->getSurveyAnswer();
}
}
$this->add(
[
'type' => 'Zend\Form\Element\Radio',
'name' => 'survey_answer',
'options' => [
'label' => 'survey_answer',
'value_options' => $array,
],
'attributes' => [
'id' => 'survey_answer',
]
]
);
I setup a basic proof of concept involving Musicians and Albums for binding a form with a form collection in Zend Framework.
Here is the Musician Class:
<?php
namespace Application\Entity;
class Musician {
protected $name;
protected $albums;
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getName()
{
return $this->name;
}
public function setAlbums($album)
{
$this->album = $album;
return $this;
}
public function getAlbums()
{
return $this->albums;
}
Here is the Album Class:
<?php
namespace Application\Entity;
class Album {
protected $name;
protected $releaseYear;
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getName()
{
return $this->name;
}
public function setReleaseYear($releaseYear)
{
$this->releaseYear = $releaseYear;
return $this;
}
public function getReleaseYear()
{
return $this->releaseYear;
}
}
Album Fieldset:
Album Field Set:
<?php
namespace Application\Form\Music;
use Zend\Form\Fieldset;
use Zend\Stdlib\Hydrator\ClassMethods;
use Zend\Validator;
use Zend\Form\Element;
use Application\Entity\Album;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\InputFilter\InputFilterProviderInterface;
class AlbumFieldSet extends Fieldset implements InputFilterProviderInterface, ServiceLocatorAwareInterface
{
public function __construct()
{
parent::__construct('album');
$this->setObject(new Album());
$this->setHydrator(new ClassMethods());
$this->add(array(
'type' => 'Text',
'name' => 'name',
'options' => [
]
));
$this->add(array(
'type' => 'Text',
'name' => 'releaseYear',
'options' => [
]
));
}
public function init()
{
}
/**
* Should return an array specification compatible with
* {#link Zend\InputFilter\Factory::createInputFilter()}.
*
* #return array
*/
public function getInputFilterSpecification()
{
return [
'name' => array(
'required' => true,
'validators' => array(
)
),
];
}
/**
* Set service locator
*
* #param ServiceLocatorInterface $serviceLocator
*/
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->sl = $serviceLocator;
}
/**
* Get service locator
*
* #return ServiceLocatorInterface
*/
public function getServiceLocator()
{
return $this->sl;
}
}
Here is the Musician Form
<?php
namespace Application\Form\Music;
use Application\Entity\Album;
use Zend\Form\Form;
use Zend\Form\Element\Collection;
use Zend\Stdlib\Hydrator\ClassMethods;
use Zend\Stdlib\Hydrator\ObjectProperty;
use Zend\Validator;
use Zend\Form\Element;
use Application\Form\Music\AlbumFieldset;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\InputFilter\InputFilterProviderInterface;
class MusicianForm extends Form implements InputFilterProviderInterface, ServiceLocatorAwareInterface
{
public function __construct()
{
parent::__construct('');
}
public function init()
{
}
public function setMusician($musician) {
$this->setHydrator(new ClassMethods());
$this->add(array(
'type' => 'Text',
'name' => 'name',
'options' => [
]
));
$this->buildFields();
$this->bind($musician);
}
public function buildFields() {
$fs = new AlbumFieldSet();
$fs->setHydrator(new ObjectProperty());
$fs->setObject(new Album());
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'albums',
'options' => array(
'label' => 'Form Values',
'count' => 2,
'allow_add' => false,
'allow_remove' => false,
'should_create_template' => false,
'target_element' => $fs,
'use_as_base_fieldset' => true,
),
));
}
/**
* Should return an array specification compatible with
* {#link Zend\InputFilter\Factory::createInputFilter()}.
*
* #return array
*/
public function getInputFilterSpecification()
{
return [
'name' => array(
'required' => true,
'validators' => array(
)
),
];
}
/**
* Set service locator
*
* #param ServiceLocatorInterface $serviceLocator
*/
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->sl = $serviceLocator;
}
/**
* Get service locator
*
* #return ServiceLocatorInterface
*/
public function getServiceLocator()
{
return $this->sl;
}
}
Controller Code:
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Application\Entity\Musician as Musician;
use Application\Entity\Album as Album;
class MusiciansController extends AbstractActionController
{
public function createMusicianAction()
{
$musician = new Musician();
$albumOne = new Album();
$albumTwo = new Album();
$albumOne->setName('The White Album');
$albumTwo->setName('Sgt. Pepper');
$albumOne->setReleaseYear('1974');
$albumTwo->setReleaseYear('1967');
$albums = array(
$albumOne,
$albumTwo
);
$musician->setName('The Beatles');
$musician->setAlbums($albums);
$form = $this->getServiceLocator()->get('FormElementManager')->get('MusicianForm');
$form->setMusician($musician);
return new ViewModel([
]);
}
}
when I attempt to bind the form, I end up with the following error:
Zend\Form\Element\Collection::setObject expects an array or Traversable object argument; received "Application\Entity\Musician"
I attempted to implement iterator in the musician class, but the solution there seems to be complicated and isn't quite clear. How get this bind to work properly?
I figured it out!
The problem here was that Zend Framework requires all entities related to the form to have their own fieldset for bind to properly work!
In this specific example, I created a musician fieldset, set it as the base fieldset in the musician form, and created the album form collection within the musician fieldset. Voila! Everything populates quite nicely.