I have two examples where I would like to use the service manager to get a service and have it resolve the dependencies through configs, apart from one dependency/parameter which is variable (pseudo code because I realise 'get' only allows one argument):
in controller, to get an entity from a database:
$sm = $this->getServiceLocator();
$myObject = $sm->get('Model', $id);
in mapper (the service manager should get the corresponding adapter):
$sm = $this->getServiceLocator();
$tableGateway = $sm->get('TableGateway', $table);
What would be best practice to achieve this?
What you're describing is not really the use-case that the ServiceManager was designed for. Like it says on the tin, it's a system to manage services. Using it to grab individual entities isn't really appropriate. Instead, use the servicemanager to grab a repository, and use that repository to grab an entity:
$entity = $this->getServiceLocator()->get('FooRepository')->find($id);
In your second example, it seems like you've got some kind of Factory that creates TableGateways. A sensible approach would be:
$gateway = $this->getServiceLocator()->get('TableGatewayFactory')->get($table);
Or, you could define individual services for each gateway:
$gateway = $this->getServiceLocator()->get('FooTableGateway');
To my knowledge this is not directly possible, but I read somewhere that intializer are meant for this type of scenario.
initializer can be any class which implement InitializerInterface, which has a method initialize(), the initialize() method is called with the object (Ex.instance of Model) and the service manager instance, you need to write condition for each of your object that need to be initialized. after building the initializer class you need to include entries in the service manager configuration(module.config.php) or getServiceConfig() method
Though not fully cover your question but may be helpful to move in the right direction
Edit
What would be best practice to achieve this?
To answer this
If your object has no dependencies you can achevie this as
$myObject = new Model();
$myObject->find($id); which would initialize the model
if your model has dependencies and also if you need to replace Model with some other object later in the stage of development you can isolate the object creation process with service factory configuration of the servicelocator, to achieve this define the service inside getServicConfig() method in Module.php (you can also define your own factory code and refer it in the configuration but in most use case simple factory definition suffice)
public function getServiceConfig(){
return array(
'factories' => array(
'Model' => function(ServiceLocatorInterface $sm) {
return new Model($dependency1, $dependency2...);
},
),
);
}
Then model access code will be
$myObject = $this->serviceLocator()->get('Model');
$myObject->find($id); which would initialize the model
the best practice would be define an interface which include find($id) and all other method that need to be called and implement this interface in the Model class, this way you can replace your factory code with any other object that implement the interface and you don't need to touch your usage code of the Model object.
For the use case two I assume you try to reuse or reduce repeat of code. If you have finite set of combination you can achieve it like below
define the service factory like this
public function getServiceConfig(){
return array(
'factories' => array(
'Model/table1' => function(ServiceLocatorInterface $sm) {
return new TableGateway('table1', $sm->get('db'));
},
'Model/table2' => function(ServiceLocatorInterface $sm) {
return new TableGateway('table2', $sm->get('db'));
},
.
.
.
.
),
);
}
access table1 as
$tableGateway = $this->serviceLocator()->get('Model/table1');
Note: 'Model/table1' is to namespace and avoid overwrite of config, internally '/' will be removed by service manager, and the names will be made lowercase, on both registration and get.
The Zend\ServiceManager\ServiceManager stores what it calls canonicalNames
in a protected property. This is an array of namespaces mapped to the cannonical names, which are the namespace with the slashes stripped and made lowercase.