How to extend moduleManager in Zend Framework 2? - php

I want to extend standard Zend\ModuleManager\ModuleManager, is it possible?
For example I want to load modules list from database and I want to add some methods for working with modules.
If I set factory to serviceManager:
'service_manager' => array(
'factories' => array(
'moduleManager' => 'Path/To/My/ModuleManager',
),
),
There is error "A service by the name or alias "modulemanager" already exists and cannot be overridden, please use an alternate name"

The module manager in Zend Framework 2 is created via a service factory class. ModuleManager is mapping to Zend\Mvc\Service\ModuleManagerFactory. I advice you to have a look on this ModuleManagerFactory and see what object are injected in the module manager on createService().
If you want to extend and use your own module manager you must create a class that extends ModuleManager but also create a service manager factory that overwrites the Zend\Mvc\Service\ModuleManagerFactory. You are on the right way with the following code but it is important to put this code in the /config/application.config.php file because this is the config file that Zend\Mvc uses to create the main services.
// config/application.config.php
'service_manager' => array(
'factories' => array(
'ModuleManager' => 'Path/To/My/ModuleManagerFactory', // <-- Path to MM factory
),
),
The link below will give you good information about what default services are run with \Zend\Mvc and how and where this is happening:
https://zf2.readthedocs.org/en/latest/modules/zend.mvc.services.html
Hope this helps, feedback will be appreciated :)
Stoyan

Related

Registering a custom validator in Zend 2

I create a custom date Validator. But I keep getting this error message:
"Zend\Validator\ValidatorPluginManager::get was unable to fetch or
create an instance for date_validator"
I did some research and tried adding in module.config.php after the view manager array.
'validators' => array(
'invokables' => array(
'date_validator' => 'Administrativo\InputFilter\Date_Validator'
),
),
What am I missing here?
Your configuration seems okay.
Are you sure you have a file (invokable class) with the name Date_Validator in a folder Administrativo\InputFilter in your module?
Is the namespace inside this class Administrativo\InputFilter and is the class name Date_Validator.
It is important that both the class name and namespace have to be correct as well as the folder names and file name. Otherwise the PluginManager will not be able to find your file.
UPDATE
If all that is fine then there is something else you should check.
If you build your input filters through config array then you should make sure that the factory inside your InputFilter class holds a correctly populated InputFilterPluginManager instance (with both ValidatorManager and FilterManager on board). Otherwise it will not inject those classes in the setInputFilterManager method and it will later create a new empty plugin manager instance which will obviously not hold your validator class.

Doctrine custom hydrators with Zend Framework 2 - How to specify in config file?

I've successfully created a custom Doctrine Hydrator, and can use it by calling the
$entityManager->getConfiguration()->addCustomHydrationMode('...', '...')
method.
However, I'm injecting dependencies into constructors via factories, so the entity manager isn't available in the classes themselves - which makes inheritance a bit more annoying (factory inheritance anyone?).
What I would prefer is to be able to configure my custom hydrators in the zf2 config file (where all the routes, services, other doctrine stuff etc is configured) - but I can't find any documentation on how to specify this.
Is this even possible?
What are the magic incantations necessary?
Cheers
It's possible to register hydrators in doctrine via configuration. For example to register the DoctrineModule\Stdlib\Hydrator\DoctrineObject hydrator, in your module configuration array which is loaded by Application\Module::getConfig you can add the following code.
'doctrine' => array(
'configuration' => array(
'orm_default' => array(
'customHydrationModes' => array(
'DoctrineModule' => 'DoctrineModule\Stdlib\Hydrator\DoctrineObject',
),
),
),
),
This registers DoctrineModule\Stdlib\Hydrator\DoctrineObject under an hydration mode called DoctrineModule. Now you can obtain this hydrator from the entity manager.
$entityManager->newHydrator('DoctrineModule');
For more information about the DoctrineModule see https://github.com/doctrine/DoctrineModule/blob/master/docs/hydrator.md.
Found the way to specify custom doctrine hydrators in the config file:
['doctrine']['configuration']['orm_default']['customHydrationModes']
From the class docs (\DoctrineORMModule\Options\Configuration):
Keys must be the name of the custom hydration method and the value must be the class name for the custom hydrator

Zend Framework 2 - Setting serviceLocator in HydratorPluginManager not working

I am using a HydratorPluginManager (Zend\Stdlib\Hydrator\HydratorPluginManager) to manage my hydrators in one single spot and to let the manager take care of validation of my hydrators (meaning checking if my hydrators properly implement the HydratorInterface).
I have some hydrators that I created using factories like this:
'service_manager' => array(
'factories' => array(
'My\Hydrators\SomeHydrator' => 'My\Hydrators\SomeHydratorFactory'
)
)
After registering the factories in my config file these hydrators are without any problem available in my ServiceManager using $serviceManager->get($name). Now I would like to connect my ServiceManager to my HydratorManager so that if I ask for a certain hydrator using:
$hydratorPluginManager->get($name)
where $name is the alias used for registering. So in this example it would be:
$hydratorPluginManager->get('My\Hydrators\SomeHydrator');
My idea was that if I connect my ServiceManager like this:
$hydratorPluginManager->setServiceLocator($serviceManager)
It should work. But it doesn't and I am very confused why this is not working...
Am I missing something here?
You could just put the hydrators in the plugin manager directly. The hydrator plugin manager is configured in the same way it is for the service manager (invokables, factories, etc), only the config key is different, hydrators instead of service_manager
You just need to move your factories from service_manager to hydrators ...
'hydrators' => array(
'factories' => array(
'My\Hydrators\SomeHydrator' => 'My\Hydrators\SomeHydratorFactory'
)
),

Making stand-alone ZF module invokable in non-ZF application via Service Layer

I have created a Zend Framework Module which manages a set of DB and filesystem operations. I have managed to get a 100% Test Coverage (with mocked databases) and I would like to test the integration of this module into a testing environment.
I have a Service class in /modules/MyModule/src/MyModule/Service/MyModuleService.php which is currently not implementing any interfaces. The documentation doesn't hint that this is something I'd need to do.
class MyModuleService
{
public function doA(){...}
public function doB(){...}
.....
}
I have not changed anything in the application folder from the default application skeleton. I have coded up Model classes which do a lot of database and filesystem manipulation. The Service class is ultimately triggering these actions.
Having done the basic configuration according to the tutorial it seems like ZF doesn't register my classes when I try to access them outside of the ZF filesystem.
//dependencies for auto loader
require_once 'Zend/Loader/AutoloaderFactory.php';
use Zend\Loader\AutoloaderFactory;
//NEW registering the autoloader (as per Tomdarkness' response)
Zend\Loader\AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'autoregister_zf' => TRUE,
'namespaces' => array(
'MyModule'=> '/path/to/zendframework/module/src/MyModule',
)
)
)
);
//what factories are auto-loaded?
var_export(AutoloaderFactory::getRegisteredAutoloaders());
The var_export now returns the following:
array(
'Zend\\Loader\\StandardAutoloader' => Zend\Loader\StandardAutoloader::__set_state(
array('namespaces' =>
array('Zend\\' => '/usr/lib/zendframework/library/Zend/',
'MyModule\\' => '/path/to/zendframework/module/src/MyModule/',
), 'prefixes' => array(),
'fallbackAutoloaderFlag' => FALSE,
)
)
);
In my module.php I do declare the service as follows:
/* Configure DB Service Managers */
public function getServiceConfig()
{
return array(
// ...
'invokables' => array(
'myModuleService'=> function ($sm){
$myModuleTable = $sm->get('MyModuleTable\Model\MyModuleTable');
return new MyModuleService($myModuleTable);
}
),
}
How can I now instantiate a MyModuleService from this autoloaded class?
You call:
var_dump(AutoloaderFactory::getRegisteredAutoloaders());
But you've not actually registered any autoloaders. To register the standard autoloader you need to run:
Zend\Loader\AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'autoregister_zf' => true
)
));
You can also register the Classmap autoloader via the factory method if you have generated classmaps.
Note, if you are using composer then you'll want to actually just include vendor/autoload.php in your file, rather than using the AutoloaderFactory. However, remember you need your module namespaces to be registered in the autoload section of composer.json if you use this method.

Include PHP Library in Zend

I have stored a third party php library in /vendor/library folder. Now i need to import it to my Zend app and use it inside controller action.
require_once ('/vendor/library/client.php');
Is this correct ? Or there is other way to to handle this ?
Use the ZF autoloader, then forget about include/require.
http://framework.zend.com/manual/1.12/en/zend.loader.autoloader.html
It means though that your class names and file names have to follow their naming conventions - which may be more trouble than it is worth.
But if you are developing your own library to work within ZF, then it is a good idea.
Adding a Composer ready 3rd party library to a ZF2 instance
The correct way to add a 3rd party library is to use Composer.
E. g. if you wish to add ZfcUser to your Zend Framework 2 application use the following command:
composer require zf-commons/zfc-user:dev-master
This will download the code from github and you just need to add the module name to your: /config/application.config.php.
Adding other 3rd party library to a ZF2 instance
If your 3rd party library is not Composer ready, you can add it to your Zend Framework 2 instance by creating a Module for it.
Step 1
/vendor/MyModule/Module.php
<?php
namespace MyModule;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
class Module implements AutoloaderProviderInterface
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
}
What this basically provides is a way for you to register your 3rd party code within a file called autoload_classmap.php:
Step 2
/vendor/MyModule/autoload_classmap.php
<?php
return array(
'MyModule\VendorLibrary' => __DIR__ . '/src/MyModule/VendorLibrary.php',
);
Step 3
Your 3rd party code should reside in:
/vendor/MyModule/src/MyModule/VendorLibrary.php and could read something like this:
<?php
namespace MyModule;
class VendorLibrary
{
public function sayHi($name)
{
return "Hi there, $name!";
}
// your 3rd party code ...
}
Step 4
Add your new module to application.config.php:
/config/application.config.php
<?php
return array(
'modules' => array(
// your other modules here ...
'MyModule'
),
'module_listener_options' => array(
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),
'module_paths' => array(
'./module',
'./vendor',
),
),
);
Usage
In your Controller you now use your vendor class like:
$vendor = new \MyModule\VendorLibrary();
$hi = $vendor->sayHi('John');
While it is a lot easier to use require_once(), it is not advisable because:
it does not provide predictability and structure of your class hierarchy and location
you also need to take care of include paths and make sure require_once is present in all controllers that need the 3rd party features
it does not allow for overriding classes (Magento-style)
etc.
Hope this helps!

Categories