This question is not explicitly about ZF2, but I often take ques from ZF2 for my code. That said, most ZF2 examples I have seen process input inside a Controller Action.
Example:
class YourController extends AbstractActionController
{
public function doStuffAction()
{
// ZF2's way to get input from $_GET variable
$product = $this->getEvent()->getRouteMatch()->getParam('product');
// Process
$processor = (new ProcessorFactory())->getProcessor($product);
$output = $processor->processInput($data);
}
}
Now, I would like to inject a Processor
into my Controller. Not create it inside the controller like I am doing above. But since Processor
depends on knowing the $product
, which is only gotten from $_GET
, I do not see any other way.
If I want to inject Processor
into Controller, I have to move the line that populates $product
variable outside of the Controller as well.
How can I do so without breaking OOP, ZF2, design patterns badly? As in, I am under the impression that anything to do with $_GET
is to be done inside a Controller
, and not inside a ControllerFactory
. Unless perhaps I can break this pattern?
If you just want to apply the Dependency Inversion principle. Applying the D of SOLID acronym, only a few changes are needed.
class YourController
{
/**
* @var ProcessorFactory
*/
protected $processorFactory;
public function __construct(ProcessorFactory $processorFactory)
{
$this->processorFactory = $processorFactory;
}
public function doStuffAction()
{
$product = $this->getEvent()->getRouteMatch()->getParam('product');
$processor = $this->processorFactory->getProcessor($product);
}
}
You could improve by typehinting to an Interface (SOLID)
class YourController
{
/**
* @var ProcessorFactoryInterface
*/
protected $processorFactory;
public function __construct(ProcessorFactoryInterface $processorFactory)
{
$this->processorFactory = $processorFactory;
}
public function doStuffAction()
{
$product = $this->getEvent()->getRouteMatch()->getParam('product');
$processor = $this->processorFactory->getProcessor($product);
}
}
Now, if you want don't want your Controller to be responsible of initiating the creating process (SOLID), you can split it up some more.
class YourController
{
/**
* @var ProcessorInterface
*/
protected $processor;
public function __construct(ProcessorInterface $processor)
{
$this->processor = $processor;
}
public function doStuffAction()
{
$processor = $this->processor;
}
}
class ControllerFactory
{
/**
* @var ProcessorFactory
*/
protected $processorFactory;
public function __construct(ProcessorFactory $processorFactory)
{
$this->processorFactory = $processorFactory;
}
public function create()
{
return new YourController($this->processorFactory->getProcessor());
}
}
class ProcessorFactory
{
/**
* @var RouteMatch
*/
protected $routeMatch;
public function __construct(RouteMatch $routeMatch)
{
$this->routeMatch = $routeMatch;
}
public function getProcessor()
{
$processor = $this->createProcessor();
// do stuff
return $processor;
}
protected function createProcessor()
{
$product = $this->routeMatch->getParam('product');
// create processor
return $processor;
}
}
The following code would get you your controller.
$controllerFactory = new ControllerFactory(new ProcessorFactory(new RouteMatch()));
$yourController = $controllerFactory->create();
Now above code is more general code and not adapted for ZF2. A good move would then to involve the ZF2's servicemanager.
class YourController extends AbstractActionController
{
/**
* @var ProcessorInterface
*/
protected $processor;
public function __construct(ProcessorInterface $processor)
{
$this->processor = $processor;
}
public function doStuffAction()
{
$processor = $this->processor;
}
}
class YourControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $controllers)
{
$services = $controllers->getServiceLocator();
$processorFactory = $services->get('ProcessorFactory');
return new YourController($processorFactory->getProcessor());
}
}
class ProcessorFactory
{
/**
* @var RouteMatch
*/
protected $routeMatch;
public function __construct(RouteMatch $routeMatch)
{
$this->routeMatch = $routeMatch;
}
public function getProcessor()
{
$processor = $this->createProcessor();
// do stuff
return $processor;
}
protected function createProcessor()
{
$product = $this->routeMatch->getParam('product');
// create processor
return $processor;
}
}
class ProcessorFactoryFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $services)
{
return new ProcessorFactory($services->get('RouteMatch'));
}
}
Above services/controllers and their factories should be registered with their ServiceManager/ControllerManager
$config = [
'controllers' = [
'factories' [
'YourController' => 'YourControllerFactory',
],
],
'service_manager' = [
'factories' [
'ProcessorFactory' => 'ProcessorFactoryFactory',
],
],
];
When a request gets dispatch to YourController, the ControllerManager returns a YourController instance with a Processor injected. Which Processor it gets depends on the request (a parameter inside RouteMatch).