DRY zf2 Module.php - zend-framework2

Is there a standard way to DRY Module.php? I have several simple Module.php files like this
<?php
namespace SolImporter;
class Module
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
}
Only the namespace is different and of course PHPCPD complains about this.

You can do this. However, you create another dependency which is not really needed for the Module. And furthermore, you cannot get the __DIR__ of your child class without reflection. And reflection can harm your performance, especially if you use this for every module class (and every module class is, by its principle, loaded on every request).
But here it goes:
namespace MyCommon\Module;
class AbstractModule
{
public function getAutoloaderConfig()
{
$dir = $this->getDir();
$namespace = $this->getNamespace();
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
$namespace => $dir . '/src/' . $namespace,
),
),
);
}
public function getConfig()
{
$dir = $this->getDir();
return include $dir . '/config/module.config.php';
}
protected function getDir()
{
$reflection = new ReflectionClass($this);
return dirname($reflection->getFileName());
}
protected function getNamespace()
{
return str_replace('\Module', '', get_class($this));
}
}
Use it like:
namespace MyModule;
use MyCommon\Module\AbstractModule;
class Module extends AbstractModule
{
}

Related

Error on Injecting dependencies into your ZF2 controllers

Hi I am having errors trying to inject dependencies on my controllers.
Warning: Missing argument 1 for User\Controller\LoginController::__construct(), called in /var/www/html/engsvc_dev/vendor/zendframework/zendframework/library/Zend/ServiceManager/AbstractPluginManager.php on line 170 and defined in /var/www/html/engsvc_dev/module/User/src/User/Controller/LoginController.php on line 23
Module.php
public function getControllerConfig(){
return array(
'factories' => array(
'Login' => function ($sm) {
$locator = $sm->getServiceLocator();
$controller = new LoginController($locator->get("Config"));
return $controller;
},
),
);
}
Controller
class LoginController extends AbstractActionController{
protected $globalConfig;
protected $UserModuleSetup;
public function __construct($config){
}
module.config.php
"invokables" => array(
"User" => "User\Controller\LoginController",
"Login" => "User\Controller\LoginController"
),
Module.php
public function getControllerConfig(){
return array(
'factories' => array(
'Login' => function ($sm) {
$locator = $sm->getServiceLocator();
$controller = new User\Controller\LoginController($locator->get("Config"));
return $controller;
},
),
);
}
Controller
class LoginController extends AbstractActionController{
protected $globalConfig;
protected $UserModuleSetup;
public function __construct($config){
}
module.config.php
"invokables" => array(
"User" => "User\Controller\LoginController",
),

Zend Framework 2 Service Manager Dependency Injection

My application is a collection of POPO's and I'm trying to wire these POPO's up using the Zend Framework 2 Service Manager.
To illustrate my problem, take the following example:
$config = array(
'services' => array(
'my.app.serviceA' => new \My\App\Services\ServiceA(),
'my.app.serviceB' => new \My\App\Services\ServiceB(),
'my.app.manager.task' => new \My\App\Manager\TaskManager(),
),
);
My TaskManager class looks something like this:
class TaskManager {
protected $serviceA;
protected $serviceB;
public function setServiceA( \My\App\Service\ServiceA $serviceA )
{
$this->serviceA = $serviceA;
}
public function setServiceB( \My\App\Service\ServiceB $serviceB )
{
$this->serviceB = $serviceB;
}
}
As you can see, the TaskManager class has dependencies on both ServiceA and ServiceB. How do inject those services into my.app.manager.task using the Service Manager configuration using the service names defined for both ServiceA and ServiceB?
UPDATE:
I'm beginning to believe that I shouldn't be using the ServiceManager component for my purposes at all but that I should be using the Zend DI component instead.
I get the impression that the ServiceManager is a ZF2 "framework" component whereas Zend\DI seems to be more of a generic all purpose DiC. Hence, this might be the reason of ServiceManager's tied relationship with the MVC and ModuleManager components (which also seem to be "framework" components).
Maybe someone could clarify?
in module.config.php The Service Manager can be configured in 7 different ways:
return array(
// instantiate the class for you when needed
'invokables' => array(
'commentController' => '\Comment\Controller\CommentController';
),
// Aliasing a name to a known service name
'aliases' => array(
'Comment\Service' => 'commentService';
),
// configure the instance of the object
'factories' => array(
'commentController' => function ($sm) {
$locator = $sm->getServiceLocator();
$controller = $locator->get('commentController');
$controller->setCommentService($locator->get('Comment\Service'));
return $controller;
}
),
// register already instantiated objects
'services' => array(
'commentController' => new \Comment\Controller\CommentController(),
),
//factory instance that can create multiple services based on the name supplied to the factory.
'abstract_factories' => array(
'SomeModule\Service\FallbackFactory',
),
// initialize the service whenever service created
'initializers' => array(
function ($instance, $sm) {
if ($instance instanceof \Comment\Controller\CommentController){
$instance->setCommentService($sm->get('Comment\Service'));
}
}
),
// indicating whether or not a service should be shared
'shared' => array(
'commentController' => false,
),
);
and in Module.php
public function getControllerConfig() {
return array(
'factories' => array(
'commentController' => function ($sm) {
$controller = new \Comment\Controller\CommentController();
$locator = $sm->getServiceLocator();
$controller->setCommentForm($locator->get('commentForm'));
$controller->setCommentService($locator->get('commentService'));
return $controller;
}
)
);
}
and simple use in controller :
$commentService = $this->serviceLocator->get('Comment\Service');
you put this in getter or in init() method
ZF2's New Controller::init() :: phly, boy, phly
in Controller ;
$yourService = $this->getServiceLocator()->get('your_service_alias');
in View Helper :
you should send in from Module.php by constructor of viewHelper
public function getViewHelperConfig() {
return array(
'factories' => array(
'loginHelper' => function($sm) {
return new LoginHelper($sm);
}
)
}
in a calss
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
public class UseCaseBO implements ServiceLocatorAwareInterface {
protected $serviceLocator;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
$this->serviceLocator = $serviceLocator;
}
public function getServiceLocator() {
return $this->serviceLocator;
}
// now instance of Service Locator is ready to use
public function someMethod() {
$table = $this->serviceLocator->get('your_service_alias');
//...
}
}
for me, the best way is to create a class factory and use the factoryInterface, like this :
return array(
'service_manager' => array(
'factories' => [
'Task' => 'Application\TaskFactory',
],
'invokables' => array(
'Task'=> 'Application\Task',
'ServiceA'=> 'Application\ServiceA',
'ServiceB' => 'Application\ServiceB'
)
),
);
And a factory class :
class TaskFactory implements FactoryInterface {
/** #var ServiceLocatorInterface $serviceLocator */
var $serviceLocator;
public function createService(ServiceLocatorInterface $serviceLocator) {
$sl = $this->serviceLocator = $serviceLocator;
// you can get your registered services
$serviceA = $sl->get('ServiceA');
$serviceB = $sl->get('ServiceB');
// You can build your class using the class loader
$task = new Application\Task();
// Or the Service Locator Again
$task = $sl->get('Task');
return $task;
}
}
You can implement the factory interface on your Task class. I prefer to have control on what I'm building.

How to setup multiple authentication adapters with fallback

I am trying to setup ZfcUser to authenticate with a fallback to LDAP. My zfcuser.global.php contains:
'auth_adapters' => array(
100 => 'ZfcUser\Authentication\Adapter\Db',
99 => 'ZfcUser\Authentication\Adapter\Ldap',
),
I have created an Ldap.php as an AbstractAdapter with the following setup. I have eliminated function code for brevity:
namespace ZfcUser\Authentication\Adapter;
use DateTime;
use Zend\Authentication\Result as AuthenticationResult;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use Zend\ServiceManager\ServiceManager;
use Zend\Session\Container as SessionContainer;
use ZfcUser\Authentication\Adapter\AdapterChainEvent as AuthEvent;
use ZfcUser\Mapper\User as UserMapperInterface;
use ZfcUser\Options\AuthenticationOptionsInterface;
class Ldap extends AbstractAdapter implements ServiceManagerAwareInterface
{
protected $mapper;
protected $serviceManager;
protected $options;
public function authenticate(AuthEvent $e)
{
...
}
public function getMapper()
{
...
}
public function setMapper(UserMapperInterface $mapper)
{
...
}
public function getServiceManager()
{
return $this->serviceManager;
}
public function setServiceManager(ServiceManager $serviceManager)
{
$this->serviceManager = $serviceManager;
}
public function setOptions(AuthenticationOptionsInterface $options)
{
$this->options = $options;
}
public function getOptions()
{
...
}
}
I have also included it as invokable in the Module.php file:
public function getServiceConfig()
{
return array(
'invokables' => array(
'ZfcUser\Authentication\Adapter\Db' => 'ZfcUser\Authentication\Adapter\Db',
'ZfcUser\Authentication\Adapter\Ldap' => 'ZfcUser\Authentication\Adapter\Ldap',
...
),
...
}
If I flip the priority values it changes who can log in. Whichever adapter is executed first allows logins but the other adapter never gets used. Anyone know how to make ZfcUser fall back to the next adapter if the first one fails?
Issue fixed here https://github.com/SocalNick/ScnSocialAuth/issues/123
For me I just i followed matwright(github) response (See https://github.com/matwright/ScnSocialAuth) so I replace two files :
socalnick/src/ScnSocialAuth/Controller/Plugin/ScnSocialAuthProvider.php by ScnSocialAuthProvider.php
socalnick/src/ScnSocialAuth/Controller/UserController.php by UserController.php
And in zfcuser.global.php :
'auth_adapters' => array(
100 => 'ZfcUser\Authentication\Adapter\Db',
),
Hope this help...

View Module Exception Strategy

I have a controller that saves some data.
$pat = $sm->get('Tables\PaymentAttemptsTable');
$pat->save($post);
The module configuration has the following config:
public function onBootstrap(EventInterface $e)
{
$em = $e->getApplication()->getEventManager();
$em->attach('dispatch', array($this, 'loadConfiguration' ), 100);
}
public function loadConfiguration(EventInterface $e)
{
$sm = $e->getApplication()->getServiceManager();
//if this module
$exceptionstrategy = $sm->get('ViewManager')->getExceptionStrategy();
$exceptionstrategy->setExceptionTemplate('error/inserterror');
}
On the PaymentAttemptsTable module confi I have a similar strategy like this.
public function onBootstrap(EventInterface $e)
{
$eventManager = $e->getApplication()->getEventManager();
$eventManager->attach('dispatch', array($this, 'loadConfiguration' ), 100);
}
public function loadConfiguration(EventInterface $e)
{
$sm = $e->getApplication()->getServiceManager();
//if this module
$exceptionstrategy = $sm->get('ViewManager')->getExceptionStrategy();
$exceptionstrategy->setExceptionTemplate('error/saveerror');
}
On each one I have a view confi like this.
return array(
'view_manager' => array(
'exception_template' => 'error/index',
'template_map' => array(
'error/index' => __DIR__ . '/../view/error/index.phtml',
),
'template_path_stack' => array(
__DIR__ . '/../view',
),
),
);
The thing is that when I do
throw new SaveError('Table must be a string or instance of TableIdentifier.');
on the PaymentAttemptsTable class I get the template from the controller and not form the table class, is there a way to fix this?
if you look at the Controller and View Models section of
http://framework.zend.com/manual/2.0/en/modules/zend.view.quick-start.html
It shows you how to load different view templates, what you will need to do is change the view template to the one that needs to be loaded in the PaymentAttemptsTable class. This will need to be done in the calling controller.
Example from Zend.com
namespace Foo\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class BazBatController extends AbstractActionController
{
public function doSomethingCrazyAction()
{
$view = new ViewModel(array(
'message' => 'Hello world',
));
$view->setTemplate('foo/baz-bat/do-something-crazy');
return $view;
}
}

ZF2 User\Module.php cannot call $sm->get('Zend\Db\Adapter\Adapter')

I'm learning ZF2 by starting write a simple UserAuth Plugin for authentication but have got the exceptions:
An exception was raised while creating "userAuth"; no instance returned
Previous exceptions:
The supplied or instantiated driver object does not implement Zend\Db\Adapter\Driver\DriverInterface
error at this line: $sm->get('Zend\Db\Adapter\Adapter')
The following is my User\Module.php
class Module {
public function onBootstrap(MvcEvent $e)
{
$e->getApplication()->getServiceManager()->get('translator');
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getServiceConfig()
{
return array(
'invokables' => array(
),
'factories' => array(
'User\Form\Signin' => function($sm) {
$form = new Form\Signin();
$form->setInputFilter(new Form\SigninFilter);
return $form;
},
'User\Auth\Service' => function ($sm) {
return new \Zend\Authentication\AuthenticationService(
new \Zend\Authentication\Storage\Session(),
new \Zend\Authentication\Adapter\DbTable($sm->get('Zend\Db\Adapter\Adapter'))
);
},
),
);
}
public function getControllerPluginConfig()
{
return array(
'factories' => array(
'UserAuth' => function ($sm) {
$sl = $sm->getServiceLocator();
$authService = $sl->get('User\Auth\Service');
$authAdapter = new \Zend\Authentication\Adapter\DbTable($sm->get('Zend\Db\Adapter\Adapter'));
$controllerPlugin = new Controller\Plugin\UserAuth();
$controllerPlugin->setAuthService($authService);
$controllerPlugin->setAuthAdapter($authAdapter);
return $controllerPlugin;
},
),
);
}
}
Please help me to fix it.
Add:
use Zend\Db\Adapter\Adapter;
to the top of Controller\Plugin\UserAuth.php
and:
extends Adapter
after:
class UserAuth
in the same file. That will at least get you going in the right direction.

Resources