phpzend-framework2zend-dbservicemanager

ZendFramework 2.0.0beta4 Service Manager Configuration - Difference between sub-keys


EDIT: a few weeks after I posted this question Evan Coury wrote an excellent blog post on the topic of the ZF2 ServiceManager, which is where I found the best answers to my questions: http://blog.evan.pro/introduction-to-the-zend-framework-2-servicemanager

--

I'm working on a project using ZendFramework 2.0.0beta4 and am having trouble using the Zend\ServiceManager to handle dependencies. Here is the current ZF2 ServiceManager documentation

It lists 6 sub-keys to use when registering classes with the ServiceManager for use in our modules: abstract_factories, aliases, factories, invokables, services, and shared. If I just want to register a model class which I'm going to use in my controller to pull data from a database, which one is best? I'm specifically trying to adapt an example from the ZF2 Skeleton Application shown below to my own application (DashboardTable is a model), and this example uses the factories way.

public function getServiceConfiguration()
{
    return array(
        'factories' => array(
            'album-table' => function($sm) {
                $dbAdapter = $sm->get('db-adapter');
                $table = new DashboardTable($dbAdapter);
                return $table;
            },
            'test-model' => Dashboard\Model\TestModel(),
        ),
    );
}

However, I don't know how 'db-adapter' is getting into the ServiceManager ($sm) in my separate working example from the SkeletonApplication - it has to do with an entry in the autoloaded global.php config file which has a 'db' entry containing the DB info. Because I don't know exactly how that's getting from the config file to ServiceManager, I created the simple entry below that to reduce the problem to its base components - "test-model". When I comment out the 'dashboard-table' entry and call a function from TestModel in my controller which simply outputs some text. Below is the ServiceManager config from my Module.php

<?php

namespace Dashboard\Model;

class TestModel {

public function testMethod()
{
    $testResult = "Hello";
    return $testResult;
}

}

Which is then passed from my controller to the view:

<?php

namespace Dashboard\Controller;

use Zend\Mvc\Controller\ActionController;
use Zend\View\Model\ViewModel;
use Dashboard\Model\AlbumTable;
use Dashboard\Model\TestModel;
use Dashboard\Model\Dashboard;

class DashboardController extends ActionController
{
    public function indexAction()
    {   
        return new ViewModel(array(
            'users' => $this->getTestModel()->testMethod(),
        ));
    }

    public function getAlbumTable()
    {
        if (!$this->albumTable) {
            $sm = $this->getServiceLocator();
            $this->albumTable = $sm->get('album-table');
        }
        return $this->albumTable;
    }

    public function getTestModel()
    {
        if (!$this->testModel) {
            $sm = $this->getServiceLocator();
            $this->testModel = $sm->get('test-model');
        }
        return $this->testModel;
    }

}

This code gives me a completely blank page, no errors. When I comment out the ServiceManager config from Module.php and just render a new ViewModel without any passing any arguments in my DashboardController.php file the page renders normally - loading layout.phtml and index.phtml.

I believe I'm misunderstanding a fundamental piece of how to use the ServiceManager or possible ZF2 in general, and will greatly appreciate any insight anybody can give. This is also my first question on StackOverflow so I welcome any advice on formatting my question. Thanks.


Solution

  • There are two good options to get factories from service managers. One is the creation of factory classes, which happens most time in the Zend Framework code itself. The second one is using closures, as you are doing.

    Make sure you do not type things like:

    'test-model' => Dashboard\Model\TestModel(),
    

    But a real closure like your first one is a good example. Secondly, the Service Manager always gives an exception when you try to get a service which fails to instantiate. Note this exception does not include the message why: the class might not be found or an exception is thrown during instantiation (for example because the service manager cannot instantiate a dependency of the service you are trying to get).

    A last remark is you do not need to import FQCN (fully qualified class names) with use statements at the location you are trying to get. But you need to import the FQCNs when you are trying to instantiate.

    So this works:

    <?php
    
    class MyClass
    {
      protected $sm;
    
      public function setServiceManager($sm)
      {
        $this->sm = $sm;
      }
    
      public function doSomething()
      {
        $this->sm->get('some-special-key');
      }
    }
    

    And this too:

    <?php
    
    use Foo\Bar\Baz;
    
    $serviceConfig = array(
      'factories' => array(
        'some-special-key' => function($sm) {
          return new Baz;
        }
      ),
    );
    

    But this not (if you try to get a Foo\Bar\Baz):

    <?php
    
    $serviceConfig = array(
      'factories' => array(
        'some-special-key' => function($sm) {
          return new Baz;
        }
      ),
    );
    

    You might want to checkout my SlmCmfKernel repository. In my Module.php I include a service configuration file, which is put in a separate location. In another part of the code I get a service from the manager.