This is for clarification
In ZF3, if you are creating any classes that need in your application, make them serviceable, make them available in your application via ServiceManager. ServiceManager implements a container which stores registered services. So how is that? ZF uses a method called factory (in short, it creates object). It helps store services into container. We can then pull services from that container using ServiceManager. Let's see how?
ServiceManager is itself a service.
So using a factory let's make ServiceManager instance available in a controller (For example, IndexController). So that we can get any service using it.
Application\Controller\IndexControllerFactory
<?php
namespace Application\Controller;
// This is the container
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
class IndexControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = NULL)
{
$serviceManager = $container->get('ServiceManager');
return new IndexController($serviceManager);
}
}
Let's register the IndexControllerFactory as a factory for IndexController so that we can use it. Make the following change in the module.config.php
'controllers' => [
'factories' => [
Controller\IndexController::class => Controller\IndexControllerFactory::class,
],
],
Once the IndexController is instantiated by IndexControllerFactory (by above configurations) the ServiceManager instance becomes available through IndexController's constructor.
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\ServiceManager\ServiceManager;
class IndexController extends AbstractActionController
{
protected $serviceManager;
public function __construct(ServiceManager $serviceManager)
{
// Here we set the service manager instance
$this->serviceManager = $serviceManager;
}
public function indexAction()
{
// Use this as you want
$config = $this->serviceManager->get('config');
return new ViewModel();
}
What if we need something from config service inside another class instead of the controller? For example, we want to upload images into a specific destination. So how would we fix the upload path? See the following example.
We will upload images through RenameUpload filter. It has an option named target which specifies the destination of upload path. Let's create another factory for upload filter.
Application\Controller\Form\Filter\UploadFilterFactory
<?php
namespace Application\Form\Filter;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Application\Form\Filter\UploadFilter;
class UploadFilterFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = NULL)
{
$config = $container->get('config');
// Look! here we fix the upload path
$uploadPath = $config['module_config']['upload_path'];
// Here we're injecting that path
return new UploadFilter($uploadPath);
}
}
Do the same for the UploadForm if you need. This will be UploadFormFactory
Put the following two snippets in the module.config.php. This is for UploadFilterFactory.
'service_manager' => [
'factories' => [
// UploadForm::class => UploadFormFactory::class,
UploadFilter::class => UploadFilterFactory::class,
],
// Make an alias so that we can use it where we need
// it could be uploadAction() inside any controller
// $inputForm = $this->serviceManager->get('UploadForm');
// $inputFilter = $this->serviceManager->get('UploadFilter');
// $uploadForm->setInputFilter($inputFilter), for example
'aliases' => [
// 'UploadForm' => UploadForm::class,
'UploadFilter' => UploadFilter::class,
],
],
and this one for the upload path wherever you want to upload.
'module_config' => [
// Set the path as you want
'upload_path' => __DIR__ . '/../data/upload',
],
This is the Application\Form\Filter\UploadFilter.
<?php
namespace Application\Form\Filter;
use Zend\InputFilter\InputFilter;
use Zend\Filter\File\RenameUpload;
class UploadFilter extends InputFilter
{
protected $uploadPath;
public function __construct(string $uploadPath)
{
// We're assigning here so that we can use it
// on the filter section.
$this->uploadPath = $uploadPath;
$this->prepareFilters();
}
public function prepareFilters()
{
$this->add(array(
'name' => 'image',
'required' => true,
'filters' => array(
array(
'name' => RenameUpload::class,
'options' => array(
// Thus here we use it
'target' => $this->uploadPath,
'overwrite' => true,
'randomize' => true,
'use_upload_extension' => true,
),
),
),
'validators' => array(),
));
}
}
This is a one way of making things serviceable. So why is ServiceManager? This is for making scattered uses of objects stop. It removes hidden dependencies. This makes code clean and easier to understand. The principle is Good Design.