phpdependency-injectionzend-framework2servicemanagermodule-management

ZF2 load service config from module


I am still struggling in instantiating a service from a ZF2 module outside of Zend Framework (in a blank .php).

I want to achieve:

Instantiate + invoke a ZF2 service method from outside ZF by the use of the ServiceManager and possibly DI.

What I have now: (UPDATED 4/10/2013)

Following up on the comments below I have done more research,particularly:

I've opted to trim out all the DI and ModuleManager things and try to autoload (works fine now) and instantiate (does not) a service.

1 - Autoload the requested classes using a Classmap and instantiate servicemanager in a stand-alone .PHP file

// Autoload ZF and ProductImage module via classmap
Zend\Loader\AutoloaderFactory::factory(array(
        'Zend\Loader\StandardAutoloader'   => array(
            'autoregister_zf' => TRUE,
        ),
        'Zend\Loader\ClassMapAutoloader'   => array(
            '/home/frequency/domains/scrftcdn/public_html/ft/shop/php/zendframework/module/ProductImage/autoload_classmap.php',
        )
    )
)

// Hard-coded servicemanager configuration (will come from $module->getConfig once this works)
$smc = new \Zend\ServiceManager\Config(
    array(
        'service_manager' => array(
            'factories'       => array(
                'ProductImage\Model\ProductImage'   => 'ProductImage\Factory\ProductImageFactory',

            )
        ),
    )
);

// Instantiate the service manager
$sm = new \Zend\ServiceManager\ServiceManager($smc);

//Load the service via the service manager
$service = $sm->get('ProductImage\Model\ProductImage'); // <throws exception
die();

2 - The exception

 [error] [client 192.168.6.52] PHP Fatal error: 
 Uncaught exception 'Zend\\ServiceManager\\Exception\\ServiceNotFoundException' with message 'Zend\\ServiceManager\\ServiceManager::get was unable to fetch or create an instance for ProductImage\\Model\\ProductImage' in /usr/lib/zendframework/library/Zend/ServiceManager/ServiceManager.php:495
Stack trace:\n#0 /home/frequency/domains/wpfreqad/public_html/wp-content/themes/frequency/manage-product-images/functions.inc.php(48): Zend\\ServiceManager\\ServiceManager->get('ProductImage\\Mo...')
    #1 /home/frequency/domains/wpfreqad/public_html/wp-content/themes/frequency/functions.inc.php(14): require_once('/home/frequency...')\n
    #2 /home/frequency/domains/wpfreqad/public_html/wp-content/themes/frequency/functions.php(14): require_once('/home/frequency...')\n
    #3 /home/frequency/domains/wpfreqad/public_html/wp-settings.php(293): include('/home/frequency...')\n
    #4 /home/frequency/domains/wpfreqad/public_html/wp-config.php(90): require_once('/home/frequency...')\n
    #5 /home/frequency/domains/wpfreqad/public_html/wp-load.php(29): require_onc in /usr/lib/zendframework/library/Zend/ServiceManager/ServiceManager.php on line 495

3 - ProductImage\autoload_classmap.php

    <?php
    // Generated by ZF2's ./bin/classmap_generator.php
    return array(
        'ProductImageTest\Service\ProductImageServiceTest'         => __DIR__ . '/test/ProductImageTest/Service/ProductImageServiceTest.php',
        'ProductImage\Module'    => __DIR__ . '/Module.php',
        'ProductImage\Factory\ProductImageFactory'                => __DIR__ . '/src/ProductImage/Factory/ProductImageFactory.php',
        'ProductImage\Model\ProductImage'                          => __DIR__ . '/src/ProductImage/Model/ProductImage.php',

    );

4 - ProductImage\Module.php

class Module implements \Zend\ModuleManager\Feature\ConfigProviderInterface
{
    /* Invoked by Module Manager */
    public function getConfig()
    {
        return include __DIR__ . '/config/module.config.php';
    }
}

5 - ProductImage\config\module.config.php

<?php
return array(
    'service_manager' => array(
        'factories'  => array(
            'ProductImage\Model\ProductImage'           =>  'ProductImage\Factory\ProductImageFactory',
        ),
    ),
);

I hope that's the right approach and not too far off the right way..


Solution

  • I've finally found a solution. Jurian's hints to using the actual application have put me on the right track! :)

    1 - /zendframework/config/application.config.php.

    Everything is default, just make sure the module is added. I commented the 'application' module as I don't see any use for it (as of now). I also had to change the path to the config files from './module' to __DIR__ . '../module' as it was looking in the wrong directory (took me a while to find that one).

    <?php
    return array(
       // ...
     'modules' => array(
            'ProductImage', /* ProductImage module */
    //        'Application',
        ),
    // ...
    'module_listener_options' => array(
        'module_paths' => array(
            __DIR__ . '/../module',
            __DIR__ . '/../vendor',
        ),
    

    2 - configuration

    make sure the modules are configured right, and also that ZF2 Path is set up correctly. In my case, run through the quick start on RTD (http://zf2.readthedocs.org/en/latest/ref/installation.html). I had the ZF2_PATH exception and change the httpd.conf via WHM.

    3 - Read more on RTD

    In particular on how you can bootstrap the application: http://zf2.readthedocs.org/en/latest/modules/zend.mvc.intro.html#zend-mvc-intro

    Which after very little debugging produced me the following code to access a neatly configured $sm instance.

    //wherever the ZF2 application skeleton is, include the autoloader
    require_once '/home/path/to/the/ZF2/application/directory/init_autoloader.php';
    
    use Zend\Loader\AutoloaderFactory;
    use Zend\Mvc\Application;
    use Zend\Mvc\Service\ServiceManagerConfig;
    use Zend\ServiceManager\ServiceManager;
    
    // setup autoloader
    AutoloaderFactory::factory();
    
    // get application stack configuration
    $configuration = include '/home/path/to/the/ZF2/application/directory/config/application.config.php';
    
    //var_export($configuration);
    // The init() method does something very similar with the previous example.
    $app = Application::init($configuration);
    $sm = $app->getServiceManager();
    $pi =  $sm->get('ProductImage\Service\ProductImageService');
    var_export($pi);
    die();
    

    I do not like the fact that the configuration needs to be specified in addition to the init_autoloader path. I avoid this implementation from being copied and pasted all over the place, I am considering integrating the $sm instantiation into the init_autoloader.php in the future so that the path of the configuration file does not have to be specified whenever a ProductImage service needs to be invoked.