Welcome to Abdul Malik Ikhsan's Blog

Zend Framework 2 : Automatic Controller Invokables via Abstract Factories

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on December 23, 2012

zf2-zendframework2 Before you continue reading, you should know that this is just to handle if you forgot to mention/register your controller in invokables/factories. Being explicit is more secure and reliable.

Ok then, let’s start. One of the Zend Framework Service Managers keys is abstract_factories. An abstract factory can be considered a “fallback” – if the service does not exist in the manager, it will then pass it to any abstract factories attached to it until one of them is able to return an object. For example, we want to automatic register class of our controllers of our application if we forgot to register in invokables.
1. Create an abstract factories

//module/SanCommons/src/SanCommons/Services/CommonControlAppAbstractFactory.php
namespace SanCommons\Services;

use Zend\ServiceManager\AbstractFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class CommonControlAppAbstractFactory implements AbstractFactoryInterface
{
    public function canCreateServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
    {
        if (class_exists($requestedName.'Controller')){
            return true;
        }

        return false;
    }

    public function createServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
    {
        $class = $requestedName.'Controller';
        return new $class;
    }
}

2. Register to getControllerConfig()

//module/SanCommons/Module.php
namespace SanCommons;

class Module
{
    public function getControllerConfig()
    {
	return array(
	    'abstract_factories' => array(
		'SanCommons\Services\CommonControlAppAbstractFactory'
	    ),
	);
    }
    public function getAutoloaderConfig(){ /*common code*/}
    public function getConfig(){ /*common code*/}
}

Remember, ‘controllers’ and ‘ModuleManager’ are the services that composed automatically by mvc stack. So, if route found a match ‘/:controller’ segment, ServiceManager will find a Service that named the Name of The Controller that pass in url in ‘controllers’ => ‘invokables’ key.

//module/SanCommons/config/module.config.php
return array(
    'controllers' => array(
       'invokables' => array(
         'YourModule\Controller\A' => 'YourModule\Controller\AController',
         'YourModule\Controller\B' => 'YourModule\Controller\BController',
         /*.. etc ...*/
       ),
     ),
),

The controllers services named ‘YourModule\Controller\A’, etc. Whenever you forgot to mention/register that in ServiceManager, ServiceManager will find it in the ‘Limbo’ (abstract_factories). If the abstract factory returns true to the canCreateServiceWithName method the service manager will create a service via createServiceWithName method.

For example : If we just created a controller with end with ‘Controller’, for example, TestAutoController, and forgot to register it into invokables, The Controller will automatically getted because service automatically created.

What if we want other service that not in Mvc Stack, and need to be called by ServiceLocator with ‘hand’ 😀 ? create abstract factory and register into abstract_factories under getServiceConfig().

Done !

References :
1. http://akrabat.com/zend-framework-2/zendservicemanager-configuration-keys/
2. http://zf2.readthedocs.org/en/latest/modules/zend.service-manager.intro.html
3. http://www.stephenrhoades.com/?p=513
4. http://www.framework.zend.com/manual/2.0/en/user-guide/routing-and-controllers.html
5. http://framework.zend.com/manual/2.0/en/modules/zend.mvc.quick-start.html#create-a-route