Reputation: 261
The latest update of zend-mvc has caused a break in compatibility due to phasing out ServiceLocatorAwareInterface. I use the servicelocator within a controller to dynamically load dependencies, eg:
class IndexController extends AbstractActionController
/**
*
* @return \UserManagement\Form\User\Details
*/
protected function getUserDetailsForm(){
return $this->getFormManager()->get('User\Details');
}
/**
*
* @return FormElementManager
*/
protected function getFormManager(){
return $this->getServiceLocator()->get('FormElementManager');
}
}
This is now raising an exception (E_USER_DEPRECEATED) with the following message:
You are retrieving the service locator from within the class User\Controller\IndexController. Please be aware that ServiceLocatorAwareInterface is deprecated and will be removed in version 3.0, along with the ServiceLocatorAwareInitializer. You will need to update your class to accept all dependencies at creation, either via constructor arguments or setters, and use a factory to perform the injections.
My question is, what is the best way of getting the forms into the controller? My service layer and other controller-specific dependencies are injected into the constructor, but i don't really want to polluate a constructor with all the forms that a controller may need, nor do i want the overhead of creating form objects that will not be used. The forms cannot be created in the controller ie $form = new Form() as they are also created dynamically eg: Module.php
public function getFormElementConfig ()
{
return array(
'factories' => array(
'User\Details' => function($sm){
$userMapper = $sm->getServiceLocator()->get('Model\User\Mapper');
$form = new \User\Form\Details($userMapper);
return $form;
}
)
);
}
Upvotes: 1
Views: 1431
Reputation: 2507
The crux of the problem is the service locator hiding the dependencies of the controller class.
The Zend framework is instructing you to move away from the ServiceRepository pattern and use proper dependency injection using DI containers or using constructor injection or setter injection. You can use a factory to inject the dependencies as well.
Please read about Service Repository which many see it as an anti-pattern.
Upvotes: 0
Reputation: 9857
There are few solutions.
Make controllers smaller. Less methods, less dependencies. However, more factories.
Use the ZF2 proxy manager. It essentially replaces expensive object instantiation with proxy objects.
Another option would be to add a wrapper or container that lazy loads the forms via the form element manager. You can then inject this container into the controller or service layer. Using a service locator in this way isn't "ideal" as you loose the explicitly defined class dependencies.
I have a FormElementLazyProvider
class and its associated factory that might be worth checking out.
For example.
$elementConfig = [
'create' => 'MyModule\Form\CreateForm',
'edit' => 'MyModule\Form\EditForm',
'delete' => 'MyModule\Form\DeleteForm',
];
$factory = function($serviceName, $elementName) use ($formElementManager) {
if ($formElementManager->has($serviceName)) {
return $formElementManager->get($serviceName);
}
return false;
};
$provider = new \ArpForm\Service\FormElementLazyProvider($factory, $elementConfig);
$provider->hasElement('create'); // true
$provider->getElement('edit'); // Callback executed and form object return
Upvotes: 0
Reputation: 70863
Have more and more specific controllers.
That way you can instantiate one controller and then have to inject exactly all the objects that are definitely needed to perform any task you may need.
There is no use to combine actions into one single controller class if all they share is a common URL path fragment. From the software design point of view, a class that does plenty of independent things in different methods is just doing too much. And doing too much is reflected in the number of dependencies you have to inject.
Upvotes: 1