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;
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)
{
$this->serviceManager = $serviceManager;
}
public function indexAction()
{
$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');
$uploadPath = $config['module_config']['upload_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' => [
UploadFilter::class => UploadFilterFactory::class,
],
'aliases' => [
'UploadFilter' => UploadFilter::class,
],
],
and this one for the upload path wherever you want to upload.
'module_config' => [
'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)
{
$this->uploadPath = $uploadPath;
$this->prepareFilters();
}
public function prepareFilters()
{
$this->add(array(
'name' => 'image',
'required' => true,
'filters' => array(
array(
'name' => RenameUpload::class,
'options' => array(
'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.