Reputation: 1047
I'm not sure how to formulate the question, so feel free to edit it.
My current situation is as following: I have a factory class which instantiates a form class. Dependency Injection (DI) is done via constructor injection. My problem is, that this form element has a Doctrine ObjectMultiCheckbox which requires a findby-method. For this findby-method I need the ID of a certain entity, but I cannot pass the ID through the factory class to the form.
My Question is, how can I deal with this situation? What is the best approach?
Let's say this is my factory class:
class CustomerFormFactory implements FactoryInterface
{
/**
* Create service
*
* @param ServiceLocatorInterface $serviceLocator
* @return Form
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$em = $serviceLocator->get('Doctrine\ORM\EntityManager');
return new CustomerForm($em);
}
}
And I get the form via the service locator like this:
$customerForm = $this->getServiceLocator()->get('CustomerForm');
How can I pass the ID to the service locator? And if the form element requires a certain ID, doesn't it break the purpose of DI and services? Should I go for the "classic" way and instantiate the form element by myself like this:
$customerForm = new CustomerForm(EntityManager $em, int $id);
I'm really not sure what I should do or what is the best way to handle this.
Upvotes: 1
Views: 126
Reputation: 1382
In order to insert options into your form you could use the CreationOptions
of the factory class.
So lets start by setting up our configurations for the FormElementManager (a serviceLocator for our Form Elements).
Within your Module.php
:
use Zend\ModuleManager\Feature\FormElementProviderInterface;
class Module implements FormElementProviderInterface
{
// your module code
public function getFormElementConfig()
{
return [
'factories' => [
'myForm' => \Module\Form\MyFormFactory::class
]
];
}
}
After we've set up the configruation we should create our Factory, which returns the Form including it's dependencies. We also insert the options which we can re-use within our form class.
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\MutableCreationOptionsTrait;
use Zend\ServiceManager\ServiceLocatorInterface;
class MyFormFactory implements FactoryInterface
{
use MutableCreationOptionsTrait;
/**
* Create service
*
* @param ServiceLocatorInterface $serviceLocator
*
* @return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
return new MyForm(
$serviceLocator->getServiceLocator()->get('Doctrine\ORM\EntityManager'),
'MyForm',
$this->getCreationOptions()
);
}
}
When using ZF3 it is better to use
\Zend\ServiceManager\Factory\FactoryInterface
instead of the\Zend\ServiceManager\FactoryInterface
as this is the way ZF3 is going with using factories. In the example above I used the ZF2 (v2.7.6 zendframework/zend-servicemanager) version. See the comment on the class Zend\ServiceManager\FactoryInterface::class to replace it with the ZF3 version.
So now when we call ::get('myForm', ['id' => $id])
on the FormElementManager
class you will get a MyForm
instance and the options of the form will contain the options we've passed along.
So your form might look something similar:
class MyForm extends \Zend\Form\Form
{
public function __construct(
\Doctrine\Common\Persistence\ObjectManager $entityManager,
$name = 'myForm',
$options = []
) {
parent::__construct($name, $options);
$this->setEntityManager($entityManager);
}
public function init () {
/** add form elements **/
$id = $this->getOption('id');
}
}
You can also create the form and set the entityManager, but that is all up to you. You don't need to use constructor injection.
So an exmaple for your controller:
$myForm = $this->getServiceManager()->get('FormElementManager')->get('myForm', ['id' => 1337]);
$options = $myForm->getOptions();
// your options: ['id' => 1337]
You might not have the ServiceManager or Locator within your Controller as you're using ZF2.5+ or ZF3 so you've got to inject the FormElementManager or the Form class into your Controller by factory.
In case you don't have any other dependencies within your form but you want to set the options, you don't need to create a factory for each class. You can re-use the InvokableFactory::class
as this will also inject the creationOptions.
Upvotes: 3