in my Module.php i have the fallowing methods that i would like to move them in a factory class so that i wont clutter the Module class:
public function getControllerConfig()
{
return array(
'factories' => array(
'account-index' => function ($controllerManager) {
$serviceManager = $controllerManager->getServiceLocator();
$accountService = $serviceManager->get('account-service');
return new Controller\IndexController($accountService);
}
)
);
}
public function getServiceConfig()
{
return array(
'factories' => array(
'account-service' => function ($serviceManages) {
return new Service\Account;
}
)
);
}
right now i have:
and where shall i put this factory class, maybe in a Factory folder?
any ideas?
I usually put my factories into ../module/yourmodule/src/yourmodule/Factory.
in your ../module/yourmodule/config/module.config.php you then have to configure your service_manager like so:
'service_manager' => array(
'factories' => array(
'yourfactory' => 'yourmodule\Factory\yourfactory',
),
),
in yourfactory.php You then have to implent the FactoryInterface and set the service locator. Once you done this you should be able to call the service the usual way for controllers, forms etc.
namespace Application\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class yourfactory implements FactoryInterface
{
private $config;
private $serviceLocator;
public function createService(ServiceLocatorInterface $serviceLocator)
{
return $servicelocator->get('Your\Service');
}
After that you can just define functions in your yourfactory.php. In your Controller you call functions like so $serviceManager->get('yourfactory')->yourfunction(yourarguments);
Related
I'm using this to build zend application. http://github.com/zendframework/ZendSkeletonApplication
I'm trying to get the config data I put inside the config/autoload/global.php and config/local.php.dist with the bottom line but it returns
Zend\ServiceManager\Exception\ServiceNotFoundException
and also
A plugin by the name "getServiceLocator" was not found in the plugin manager Zend\Mvc\Controller\PluginManager
Any idea how I can get the config?
$config = $this->getServiceLocator()->get('config');
The Master branch of ZendSkeletonApplication at the moment using Zend Framework 3. And getServiceLocator() in controller had been remove in Zend Framework 3.
So, if you wanna pass some variables from service to controller, you should create a factory. And pass the variables when instantiate the controller in factory.
Example:
Your controller name is IndexController from Application Module. And the factory class is IndexControllerFactory.
Application\Controller\IndexControllerFactory
<?php
namespace Application\Controller;
use Zend\ServiceManager\Factory\FactoryInterface;
use Interop\Container\ContainerInterface;
class IndexControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$config = $container->get("Config");
return new IndexController($config);
}
}
Application\Controller\IndexController
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
class IndexController extends AbstractActionController
{
private $config;
public function __construct(array $config)
{
$this->config = $config;
}
public function indexAction()
{
// use $this->config here
}
}
and here the configuration in module.config.php
'controllers' => [
'factories' => [
Controller\IndexController::class => Controller\IndexControllerFactory::class
],
],
Hope this help
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.
In order to do that you need to inject the config, as the getServiceLocator (and all other locators) have been removed from ZF3.
In your module configuration you have this:
'controllers' => [
'factories' => [
Controller\IndexController::class => InvokableFactory::class,
],
],
You can change the factory to create your own.
Controller\IndexController::class => Controller\IndexControllerFactory::class,
Here's the code:
final class IndexControllerFactory
{
public function __invoke(Container $container) : IndexController
{
$config = $container->get('config');
if (!isset($config['stuff']['stuff']) {
throw new \Exception('Please add the stuff.stuff parameter in the config');
}
$myParam = $config['stuff']['stuff'];
return new IndexController($myParam);
}
}
Container is a PSR container.
In your controller add a constructor to receive the config you need:
public function __construct(string $param)
{
$this->param = $param;
}
And here you have your config in your class, as an attribute.
I try to create a module Users I am inspire from the Blog Exemple of zend 2.4, I build a factory to instantiate my controller UsersController, but i get this error:
An exception was raised while creating "Users\Controller\Users"; no instance returned
Actually the problem arise when I bring the service into the controler
class UsersController extends AbstractActionController {
protected $userService;
public function __construct(UserServiceInterface $userService){
$this->userService = $userService ;
}
}
Then I defined UserServiceInterface and create an instance of the 'Users\Controller\UserController' and change the configuration:
'controllers' => array(
'factories' => array(
'Users\Controller\Users' => 'Users\Factory\UserControllerFactory'
)
),
And Writing a Factory Class UserControllerFactory :
class UserControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator){
$realServiceLocator = $serviceLocator->getServiceLocator();
$userService= $realServiceLocator->get('Users\Service\UserServiceInterface');
return new UsersController($userService);
}
}
The UserService has a dependency :
'service_manager' => array(
'factories' => array(
'Users\Service\UserServiceInterface' => 'Users\Factory\UserServiceFactory',
'Users\Mapper\PostMapperInterface' => 'Users\Factory\ZendDbSqlMapperFactory',
)
),
I get also
Previous exceptions:
Zend\ServiceManager\Exception\ServiceNotCreatedException
File:
D:\wamp\www\AppZend2\vendor\zendframework\zend-servicemanager\src\ServiceManager.php:1101
Message:
While attempting to create usersserviceuserserviceinterface(alias: Users\Service\UserServiceInterface) an invalid factory was registered for this instance type.
this is my UserServiceFactory :
use Users\Service\UserService;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class UserServiceFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
return new UserService(
$serviceLocator->get('Users\Mapper\PostMapperInterface')
);
}
}
I have 2 factories.
The first is a Controller Factory:
<?php
namespace Blog\Factory;
use Blog\Controller\ListController;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ListControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$realServiceLocator = $serviceLocator->getServiceLocator();
$postService = $realServiceLocator->get('Blog\Service\PostServiceInterface');
return new ListController($postService);
}
}
The second is a Post ServiceFactory:
<?php
namespace Blog\Factory;
use Blog\Service\PostService;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class PostServiceFactory implements FactoryInterface
{
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
* #return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
return new PostService(
$serviceLocator->get('Blog\Mapper\PostMapperInterface')
);
}
}
Here is my module config:
<?php
return array(
'service_manager' => array(
'factories' => array(
'Blog\Service\PostServiceInterface' => 'Blog\Factory\PostServiceFactory'
)
),
'controllers' => array(
'factories' => array(
'Blog\Controller\List' => 'Blog\Factory\ListControllerFactory'
)
),
'router' => array(
// Open configuration for all possible routes
'routes' => array(
// Define a new route called "post"
'post' => array(
// Define the routes type to be "Zend\Mvc\Router\Http\Literal", which is basically just a string
'type' => 'literal',
// Configure the route itself
'options' => array(
// Listen to "/blog" as uri
'route' => '/blog',
// Define default controller and action to be called when this route is matched
'defaults' => array(
'controller' => 'Blog\Controller\List',
'action' => 'index',
)
)
)
)
),
'view_manager' => array(
'template_path_stack' => array(
__DIR__ . '/../view',
),
)
);
In the controller factory, I have to call getServiceLocator against the ServiceLocatorInterface, followed by the get call. however in the post service factory i just call get. I did a dump and it looks like both are the Zend\ServiceManager\ServiceManager classes. When I tried performing the getServiceLocator call against the post service factory service locator it errored no method found.
Im not quite understanding whats going on?
Controller factories are called in a different way than casual service factories. The ServiceLocator passed to createService is actually not the ServiceManager you are looking for but an instance of ControllerManager.
If you try this:
public function createService(ServiceLocatorInterface $serviceLocator)
{
$realServiceLocator = $serviceLocator->getServiceLocator();
$postService = $realServiceLocator->get('Blog\Service\PostServiceInterface');
var_dump(get_class($serviceLocator));
return new ListController(postService );
}
you'll get the output:
string(37) "Zend\Mvc\Controller\ControllerManager"
while the same dump in your PostServiceFactory will give you:
string(34) "Zend\ServiceManager\ServiceManager"
From the Zend 2 documentation:
http://framework.zend.com/manual/current/en/in-depth-guide/services-and-servicemanager.html#writing-a-factory-class
When using a Factory-Class that will be called from the ControllerManager it will actually inject itself as the $serviceLocator. However, we need the real ServiceManager to get to our Service-Classes. This is why we call the function getServiceLocator() who will give us the real ServiceManager.
I am creating a website using Zend Framework 2, and I'm using as an example the exercise from the official course of Zend Technology, Zend Framework 2: Fundamentals.
I have a table called posts and I want to show the table content in my home page, ordered by id. These are the codes I have written:
Controller/PostsTableTrait.php
trait PostsTableTrait
{
private $postsTable;
public function setPostsTable($postsTable)
{
$this->postsTable = $postsTable;
}
}
Controller/IndexController.php
class IndexController extends AbstractActionController
{
use PostsTableTrait;
public function indexAction()
{
return new ViewModel(array(
'post' => $this->postsTable->getPosts()
));
}
}
Factory/IndexControllerFactory.php
class IndexControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$serviceManager = $serviceLocator->getServiceLocator()->get('ServiceManager');
$indexController = new IndexController();
$indexController->setPostsTable($serviceManager->get('Rxe\Factory\PostsTable'));
return $indexController;
}
}
Factory/PostsTableFactory.php
class PostsTableFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
return new PostsTable(PostsTable::$tableName, $serviceLocator->get('Zend\Db\Adapter\AdapterService'));
}
}
Model/PostsTable.php
class PostsTable extends TableGateway
{
public static $tableName = "posts";
public function getPosts()
{
$select = new Select(self::$tableName);
$select->columns(array(
'date',
'title',
'text',
'category'
));
$select->order('id DESC');
return $select;
}
}
config/module.config.php
'controllers' => array(
'invokables' => array(
'Rxe\Controller\Index' => 'Rxe\Controller\IndexController',
'Rxe\Controller\Panel' => 'Rxe\Controller\PanelController'
),
'factories' => array(
'Rxe\Factory\PanelController' => 'Rxe\Factory\PanelControllerFactory'
)
),
'service_manager' => array(
'factories' => array(
'Rxe\Factory\PanelForm' => 'Rxe\Factory\PanelFormFactory',
'Rxe\Factory\PanelFilter' => 'Rxe\Factory\PanelFilterFactory',
'Rxe\Factory\PostsTable' => 'Rxe\Factory\PostsTableFactory',
'Zend\Db\Adapter\AdapterService' => 'Zend\Db\Adapter\AdapterServiceFactory'
)
),
I don't know if the error could be in the getPosts() method. I have tried many different ways to return the query but none of them made any difference, not even showed another error.
You have registered the controller as an 'invokable'. When the the controller manager creates IndexController it will do so without using the IndexControllerFactory; therefore the Rxe\Factory\PostsTable dependency is never set.
To fix this, update module.config.php and register the index controller with your factory class.
'controllers' => [
'factories' => [
'Rxe\Controller\Index' => 'Rxe\Factory\IndexControllerFactory',
],
],
Also (not an error as such) but the IndexControllerFactory calls ->get('ServiceManager') using the service manager.
You could update it to be like this.
class IndexControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $controllerManager)
{
// #var \Zend\ServiceManager\ServiceManager
$serviceManager = $controllerManager->getServiceLocator();
$indexController = new IndexController();
$indexController->setPostsTable($serviceManager->get('Rxe\Factory\PostsTable'));
return $indexController;
}
}
I was trying to create a plugin controller, like this:
Application\src\Application\Controller\Plugin\Controlador.php
namespace Application\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPluginManager;
use Biblioteca\Mvc\Db\TableGateway;
class Controlador extends AbstractPluginManager
{
protected function getTable($table){
$sm = $this->getServiceLocator();
$dbAdapter = $sm->get('DbAdapter');
$tableGateway = new TableGateway($dbAdapter, $table, new $table);
$tableGateway->initialize();
return $tableGateway;
}
protected function getService($service)
{
return $this->getServiceLocator()->get($service);
}
}
And in my module.config.php i put this:
'controllers' => array(
'invokables' => array(
'Application\Controller\Index' => 'Application\Controller\IndexController',
'Controlador' => 'Application\Controller\Plugin\Controlador'
),
),
And in my indexController.php like this:
namespace Application\Controller;</br>
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Biblioteca\ActionController;
class IndexController extends AbstractActionController{
public function indexAction()
{
$controlador = $this->Controlador();
return new ViewModel(array(
'posts' => $controlador->getTable('Application\Model\Post')->fetchAll()->toArray()
));
}
}
and when i execute the code i get this message: "Zend\Mvc\Controller\PluginManager::get was unable to fetch or create an instance for Controlador"
someone can help me ?
You are registering your plugin at the controllerManager which is responsible for creating controller instances. You need to define it at using 'controller_plugins' key in your module config.
return array(
'controller_plugins' => array(
'invokables' => array(
'Controlador' => 'Application\Controller\Plugin\Controlador'
)
)
);
Also you need to inherit AbstractPlugin. Now you are inheriting AbstractPluginManager which you'll use to create your own plugin manager.
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
class Controlador extends AbstractPlugin
{