Here is my service configuration:
public function getServiceConfig()
{
return array(
'factories' => array(
'Squiddle\Designpackage' => function($sm){
$designPkg = new Designpackage($sm);
return $designPkg;
}
)
);
}
It's being used within a function that gets triggered on the dispatch event.
Here is it's usage:
$e->getApplication()->getServiceManager()->get('Squiddle\Designpackage');
My error is that the designpackage is being constructed with null instead of a service manager.
Not sure whats wrong here;
It could be a problem in the Designpackage constructor
I created a service layer AbcService in order to allow modules to access common lines of code. But I need to use database to extract values in my AbcService. So, I need to call getAbcTable() which calls $service->getServiceLocator(). When I try this, I get an error saying 'Call to undefined method getServiceLocator().
public function getAbcTable()
{
if (!$this->abcTable) {
$sm = $this->getServiceLocator();
$this->abcTable = $sm->get('Abc\Model\AbcTable');
}
return $this->abcTable;
}
You're trying to call a method that presumably doesn't exist. If you need AbcTable in your service, you should pass it in as a dependency.
Create a factory for your service that does this, in Module.php:
public function getServiceConfig()
{
return array(
'factories' => array(
'AbcService' => function($sm) {
$abcTable = $sm->get('Abc\Model\AbcTable');
$abcService = new AbcService($abcTable);
return $abcService;
},
);
}
and modify the constructor for your service to accept the table as a paramter:
class AbcService
{
protected $abcTable;
public function __construct($abcTable)
{
$this->abcTable = $abcTable;
}
// etc.
}
then, wherever you need AbcService, either inject it in, or grab it from the service locator:
public function indexAction()
{
$abcService = $this->getServiceLocator()->get('AbcService');
}
and the service will have the table class in it.
It looks like it has been touched several times already, but i still can't get it work. I set up an JSON-RPC server in a separate module, it works fine. Its functionality is in a new class Rpcapi. Now I want reuse DB related functions that already implemented in another module from that class. According to ZF2 docs my Rpcapi class has to be ServiceLocator-aware and it looks like I made it that way. Unfortunatelly still can't get it working. Please help keeping in mind that I'm new with ZF2 :)
Rpccontroller.php
namespace Rpc\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Json\Server\Server;
use Zend\Json\Server\Smd;
use Rpc\Model\Rpcapi;
class RpcController extends AbstractActionController
{
public function indexAction()
{
header('Content-Type: application/json');
$jsonrpc = new Server();
$jsonrpc->setClass(new Rpcapi);
$jsonrpc->getRequest()->setVersion(Server::VERSION_2);
if ($this->getRequest()->getMethod() == "GET") {
$smd = $jsonrpc->getServiceMap()->setEnvelope(Smd::ENV_JSONRPC_2);
echo $smd;
} else {
$jsonrpc->handle();
}
}
}
module.config.php for Rpc module
'service_manager' => array(
'invokables' => array(
'rpcapi' => 'Search\Model\SiteTable',
),
),
Rpcapi.php
namespace Rpc\Model;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class Rpcapi implements ServiceLocatorAwareInterface
{
protected $services;
protected $siteTable;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->services = $serviceLocator;
}
public function getServiceLocator()
{
return $this->services;
}
public function getSiteTable()
{
if (!$this->siteTable) {
$sm = $this->getServiceLocator();
$this->siteTable = $sm->get('rpcapi');
}
return $this->siteTable;
}
/**
* Returns list of all sites
*
*
* #return array
*/
public function getAllSites()
{
$results = $this->getSiteTable()->fetchAll();
$r = array ('1' => '1', '2' => 2); //Just to return something for now
return $r;
}
}
All I could get out is: Fatal error: Call to a member function get() on a non-object in /var/www/html/AmeriFluxZF2/module/Rpc/src/Rpc/Model/Rpcapi.php on line 28. Line 28 is:
$this->siteTable = $sm->get('rpcapi');
Any help is much appreciated!
Making the class service locator aware tells the ZF2 that the service locator should be injected into your class upon instantiation. However, you still need to use the service locator to instantiate this class, rather than creating an instance of it yourself, or this will never happen.
Your probably want to add a new entry to invokables for your Rpcapi class, and then grab this from the service locator instead of doing new Rpcapi in your controller.
PS: The naming of your classes is very confusing - you have an Rpcapi class, and an invokable called rpcapi, yet this invokable creates an instance of a completely different class?
If you want serviceLocator to be injected by the service manager in your Rpcapi, you must get it via the service manager itself :
'service_manager' => array(
'invokables' => array(
'rpcapi' => 'Search\Model\SiteTable',
'Rpc\Model\Rpcapi' => 'Rpc\Model\Rpcapi',
),
),
the action :
public function indexAction()
{
header('Content-Type: application/json');
$jsonrpc = new Server();
$jsonrpc->setClass($this->getServiceLocator()->get('Rpc\Model\Rpcapi'));
$jsonrpc->getRequest()->setVersion(Server::VERSION_2);
if ($this->getRequest()->getMethod() == "GET") {
$smd = $jsonrpc->getServiceMap()->setEnvelope(Smd::ENV_JSONRPC_2);
echo $smd;
} else {
$jsonrpc->handle();
}
}
And this is where you can see that your 'rcpai' name for SiteTable is not a good choice... ;)
guys,
At this point i am close to start pulling hair out of my head. I don't find a way to achieve this.
I have a custom class that belongs to a custom folder i created under my WebServices Module src folder. I need to be able to instantiate this class from inside another module/controller but when i do that and dump the services member it contains null.
How can i have the service manager accesible from inside my ApiAuthentication class.
Any help will be appreciated. Thanks
<?php
namespace WebServices\Services;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ApiAuthenticationService extends \Zend\Soap\Client implements ServiceLocatorAwareInterface{
public $services;
function __construct($options = null){
parent::__construct('http://tinysoa.local/soap/security/api_authentication?wsdl',$options);
}
public function setServiceLocator(ServiceLocatorInterface $locator)
{
$this->services = $locator;
}
public function getServiceLocator()
{
return $this->services;
}
}
When i call this from inside another module/controller it dumps a null value:
class IndexController extends AbstractActionController
{
public function indexAction()
{
$a = new \WebServices\Services\ApiAuthenticationService();
var_dump($a->services);
Responding with my own answer to add-on to Adrian's, and the question you asked in response.
If your service has dependencies of it's own, you just use a factory instead of going the invokable route.
Say your service needs a cache adapter and database adapter. Also imagine that it can optionally be configured with some other service (FooService, below):
<?php
public function getServiceConfig()
{
return array(
'factories' => array(
'my_service' => function($sm){
$cache = $sm->get('Cache');
$dbAdapter = $sm->get('DefaultDbAdapter');
$fooService = $sm->get('FooService');
// instantiate your service with required dependencies
$mySvc = new \My\Shiny\Service($cache, $dbAdapter);
// inject an optional dependency
$mySvc->setFooService($fooService);
// return your shiny new service
return $mySvc;
}
)
);
}
Side Note: It's generally bad design to inject the ServiceManager all over the place. You're better off managing your dependencies more explicitly, like above.
This stuff is covered quite well in the Quick Start, if you haven't already read that.
Register your Service in Service Config and access it through getServiceLocator() method in controller.
Module.php
public function getServiceConfig()
{
return array(
'invokables' => array(
'my_service' => 'WebServices\Services\ApiAuthenticationService'
)
);
}
Controller
public function indexAction()
{
$service = $this->getServiceLocator()->get('my_service');
}
I need to get the adapter from the form, but still could not.
In my controller I can recover the adapter using the following:
// module/Users/src/Users/Controller/UsersController.php
public function getUsersTable ()
{
if (! $this->usersTable) {
$sm = $this->getServiceLocator();
$this->usersTable = $sm->get('Users\Model\UsersTable');
}
return $this->usersTable;
}
In my module I did so:
// module/Users/Module.php
public function getServiceConfig()
{
return array(
'factories' => array(
'Users\Model\UsersTable' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$uTable = new UsersTable($dbAdapter);
return $uTable;
},
//I need to get this to the list of groups
'Users\Model\GroupsTable' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$gTable = new GroupsTable($dbAdapter);
return $gTable;
},
),
);
}
Could someone give me an example how to get the adapter to the table from the group form?
I have followed this example to my form users:
http://framework.zend.com/manual/2.0/en/modules/zend.form.collections.html
EDITED from here...
Maybe I expressed myself wrong to ask the question.
What I really need to do is populate a select (Drop Down) with information from my table groups.
So I need to get the services inside my userForm class by ServiceLocatorAwareInterface (see this link) implemented because By default, the Zend Framework MVC registers an initializer That will inject into the ServiceManager instance ServiceLocatorAwareInterface Implementing any class.
After retrieving the values from the table groups and populate the select.
The problem is that of all the ways that I've tried, the getServiceLocator() returns this:
Call to a member function get() on a non-object in
D:\WEBSERVER\htdocs\Zend2Control\module\Users\src\Users\Form\UsersForm.php
on line 46
I just wanted to do this in my UserForm...
namespace Users\Form;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Form\Element;
use Zend\Form\Form;
class UsersForm extends Form implements ServiceLocatorAwareInterface
{
protected $serviceLocator;
public function getServiceLocator ()
{
return $this->serviceLocator;
}
public function setServiceLocator (ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
public function __construct ($name = null)
{
parent::__construct('users');
$this->setAttribute('method', 'post');
$sm = $this->getServiceLocator();
$groups = $sm->get('Users\Model\GroupsTable')->fetchAll(); // line 46
$select = new Element\Select('groups');
$options = array();
foreach ($groups as $group) {
$options[$group->id] = $group->name;
}
$select->setValueOptions($options);
$this->add($select);
// and more elements here...
The other various answers here generally correct, for ZF < 2.1.
Once 2.1 is out, the framework has a pretty nice solution. This more or less formalizes DrBeza's solution, ie: using an initializer, and then moving any form-bootstrapping into an init() method that is called after all dependencies have been initialized.
I've been playing with the development branch, it it works quite well.
This is the method I used to get around that issue.
firstly, you want to make your form implement ServiceLocatorInterface as you have done.
You will then still need to manually inject the service locator, and as the whole form is generated inside the contrstuctor you will need to inject via the contructor too (no ideal to build it all in the constructor though)
Module.php
/**
* Get the service Config
*
* #return array
*/
public function getServiceConfig()
{
return array(
'factories' => array(
/**
* Inject ServiceLocator into our Form
*/
'MyModule\Form\MyForm' => function($sm) {
$form = new \MyModule\Form\MyFormForm('formname', $sm);
//$form->setServiceLocator($sm);
// Alternativly you can inject the adapter/gateway directly
// just add a setter on your form object...
//$form->setAdapter($sm->get('Users\Model\GroupsTable'));
return $form;
},
),
);
}
Now inside your controller you get your form like this:
// Service locator now injected
$form = $this->getServiceLocator()->get('MyModule\Form\MyForm');
Now you will have access to the full service locator inside the form, to get hold of any other services etc such as:
$groups = $this->getServiceLocator()->get('Users\Model\GroupsTable')->fetchAll();
In module.php I create two services. See how I feed the adapter to the form.
public function getServiceConfig()
{
return array(
'factories' => array(
'db_adapter' => function($sm) {
$config = $sm->get('Configuration');
$dbAdapter = new \Zend\Db\Adapter\Adapter($config['db']);
return $dbAdapter;
},
'my_amazing_form' => function ($sm) {
return new \dir\Form\SomeForm($sm->get('db_adapter'));
},
),
);
}
In the form code I use that feed to whatever:
namespace ....\Form;
use Zend\Form\Factory as FormFactory;
use Zend\Form\Form;
class SomeForm extends Form
{
public function __construct($adapter, $name = null)
{
parent::__construct($name);
$factory = new FormFactory();
if (null === $name) {
$this->setName('whatever');
}
}
}
We handle this in the model, by adding a method that accepts a form
public function buildFormSelectOptions($form, $context = null)
{
/**
* Do this this for each form element that needs options added
*/
$model = $this->getServiceManager()->get('modelProject');
if (empty($context)){
$optionRecords = $model->findAll();
} else {
/**
* other logic for $optionRecords
*/
}
$options = array('value'=>'', 'label'=>'Choose a Project');
foreach ($optionRecords as $option) {
$options[] = array('value'=>$option->getId(), 'label'=>$option->getName());
}
$form->get('project')->setAttribute('options', $options);
}
As the form is passed by reference, we can do something like this in the controller where the form is built:
$builder = new AnnotationBuilder();
$form = $builder->createForm($myEntity);
$myModel->buildFormSelectOptions($form, $myEntity);
$form->add(array(
'name' => 'submitbutton',
'attributes' => array(
'type' => 'submit',
'value' => 'Submit',
'id' => 'submitbutton',
),
));
$form->add(array(
'name' => 'cancel',
'attributes' => array(
'type' => 'submit',
'value' => 'Cancel',
'id' => 'cancel',
),
));
Note: The example assumes the base form is build via annotations, but it doesn't matter how you create the initial form.
An alternative method to the other answers would be to create a ServiceManager Initializer.
An example of an existing Initializer is how the ServiceManager is injected if your instance implements ServiceLocatorAwareInterface.
The idea would be to create an interface that you check for in your Initialiser, this interface may look like:
interface FormServiceAwareInterface
{
public function init();
public function setServiceManager(ServiceManager $serviceManager);
}
An example of what your Initializer may look like:
class FormInitializer implements InitializerInterface
{
public function initialize($instance, ServiceLocatorInterface $serviceLocator)
{
if (!$instance instanceof FormServiceAwareInterface)
{
return;
}
$instance->setServiceManager($serviceLocator);
$instance->init();
}
}
Anything that happens in init() would have access to the ServiceManager. Of course you would need to add your initializer to your SM configuration.
It is not perfect but it works fine for my needs and can also be applied to any Fieldsets pulled from the ServiceManager.
This is the way I used get around that issue.
firstly, In Module.php, create the service (just as you have done):
// module/Users/Module.php
public function getServiceConfig()
{
return array(
'factories' => array(
'Users\Model\UsersTable' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$uTable = new UsersTable($dbAdapter);
return $uTable;
},
//I need to get this to the list of groups
'Users\Model\GroupsTable' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$gTable = new GroupsTable($dbAdapter);
return $gTable;
},
),
);
}
Then in the controller, I got a reference to the Service:
$users = $this->getServiceLocator()->get('Test\Model\TestGroupTable')->fetchAll();
$options = array();
foreach ($users as $user)
$options[$user->id] = $user->name;
//get the form element
$form->get('user_id')->setValueOptions($options);
And viola, that work.