Use of single module multiple times - php

I have created Auth and Acl modules using zend-framework2, And these two are working good. Now I want to use these modules multiple times with different configuration.
Basically, I have two sections in my project -
User Section
Admin Section
And both have different session variables and different database tables.
I have Auth configuration file as following (module.config.php) -
return array(
'auth' => array(
'db' => array(
'table' => 'user',
'identity' => 'email',
'credential' => 'password',
'credential_treatment' => array(
'class' => '\My\Common',
'method' => 'encrypt'
),
'status' => array(
'is_enabled = true',
),
),
'view' => array(
'label' => array(
'identity' => 'Email',
),
),
'route' => array(
'login' => 'home',
'logout' => 'home',
),
'whitelist' => array(
'home',
)
),
....
....
);
I want to use the same module for both Admin section and User section, But with different configuration settings, Like different database tables and session variables.
Is it possible to do so or I have to create different modules for different section?
Let me know if you need more details.

Here's an simple approach for your use case. I didn't test it, so expect some typos :)
I hope you get the basic idea. If not, let me know.
config/autoload/auth.global.php
return [
'service_manager' => [
'factories' => [
'YourAuthNamespace\Config' => 'YourAuthNamespace\Service\ConfigServiceFactory',
'YourAuthNamespace\AbstractAuthFactoryFactory' => 'YourAuthNamespace\Service\AbstractAuthFactoryFactory',
],
'abstract_factories' => [
'YourAuthNamespace\AbstractAuthFactoryFactory'
]
]
];
src/YourAuth/Service/AbstractAuthFactoryFactory.php
namespace YourAuth\Service;
class AbstractAuthFactoryFactory implements \Zend\ServiceManager\AbstractFactoryInterface
{
public function canCreateServiceWithName(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocator, $name, $requestedName)
{
$key = $this->getConfigKeyFromServiceName($name);
$config = $this->getConfig($serviceLocator);
return array_key_exists($key, $config);
}
private function getConfig(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocator)
{
return $serviceLocator->get('YourAuthNamespace\Config');
}
public function createServiceWithName(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocator, $name, $requestedName)
{
$key = $this->getConfigKeyFromServiceName($name);
$config = $this->getConfig($serviceLocator);
return new YourAuthClass($config[$key]);
}
private function getConfigKeyFromServiceName($name)
{
return preg_replace('#^YourAuthNamespace\Auth\#i', '', $name);
}
}
src/YourAuth/Service/ConfigServiceFactory.php
namespace YourAuth\Service;
use Zend\ServiceManager\FactoryInterface;
class ConfigServiceFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$config = $serviceLocator->get('Config');
return $config['yourauth'];
}
}
module/MyModuleA/config/module.config.php
return [
'yourauth' => [
'ModuleA' => [ // 'ModuleA' is also the key used by the abstract factory
'db' => [
'table' => 'module_a_auth_table',
// ...
],
'session' => [
'namespace' => 'module_a_session_namespace'
// ...
],
]
],
'service_manager' => array(
'factories' => array(
'MyModuleA\Auth' => function ($sm) {
return $sm->get('YourAuthNamespace\Auth\ModuleA');
}
),
),
];
module/MyModuleA/src/AuthClient.php
namespace MyModuleA;
class AuthClient implements \Zend\ServiceManager\ServiceLocatorAwareInterface
{
public function doSomethingWithAuth()
{
if ($this->getServiceLocator()->get('MyModuleA\Auth')->isAuthorized()) {
// blah...
}
}
}

Related

creating dynamic navigation in zf3

I am using zend framework3 in my project. I am able to create static navigation by following the docs link
Now I have to fetch the menu data from database then create the navigation.
For this i am using i have provide the configuration into the module.config.php which is config file of the album module.
<?php
namespace Album;
use Zend\Router\Http\Literal;
use Zend\Router\Http\Segment;
use Zend\ServiceManager\Factory\InvokableFactory;
use Zend\Navigation\Service\DefaultNavigationFactory;
use Album\Navigation\AlbumNavigationFactory;
return [
'controllers' => [
'factories' => [
Controller\AlbumController::class => Factory\AlbumControllerFactory::class,
Controller\IndexController::class => InvokableFactory::class,
],
],
// Add this section:
'service_manager' => [
'factories' => [
'navigation' => Navigation\AlbumNavigationFactory::class,
Model\AlbumTable::class => Factory\AlbumTableFactory::class,
],
],
// The following section is new and should be added to your file:
'router' => [
'routes' => [
'album' => [
'type' => Segment::class,
'options' => [
'route' => '/album[/:action[/:id]]',
'constraints' => [
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
],
'defaults' => [
'controller' => Controller\AlbumController::class,
'action' => 'index',
],
],
],
'index' => [
'type' => Segment::class,
'options' => [
'route' => '/index[/:action[/:id]]',
'constraints' => [
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
],
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
],
],
],
'view_manager' => [
'template_path_stack' => [
'album' => __DIR__ . '/../view',
],
],
];
In zend framework2 we simple pass a navigation key with factory class as
return array(
'factories' => array(
'Navigation' => 'Album\Navigation\AlbumNavigationFactory'
),
);
In zend framework3 i am doing the same thing as below
'service_manager' => [
'factories' => [
'navigation' => Navigation\AlbumNavigationFactory::class,
Model\AlbumTable::class => Factory\AlbumTableFactory::class,
],
],
I am using Navigation\AlbumNavigationFactory::class to call factory for fetching the data.
but i am not be able to get the navigation. Any help would be appreciated.
here is part of my code. I think will help. Work perfect.
In Module.php
public function getServiceConfig()
{
return array(
'factories' => array(
'ItemsFromDatabase::class => Navigation\BlogNavigationFactory::class,
)
);
}
public function getViewHelperConfig() {
return[
'factories' => [
'AddItemsInNavigation' => function($helpers) {
$navigation = $helpers->get('Application')->getServiceManager()->get('Zend\Navigation\Default')->findOneByLabel('Blog');
$newItems = $helpers->get(ItemsFromDatabase::class);
return new View\Helper\AddItemsInNavigation($navigation, $newItems);
},
],
Blog\View\Helper\AddItemsInNavigation.php
<?php
namespace Blog\View\Helper;
use Zend\View\Helper\AbstractHelper;
class AddItemsInNavigation extends AbstractHelper {
protected $navigation;
protected $newItems;
public function __construct($navigation, $newItems) {
$this->navigation = $navigation;
$this->newItems = $newItems;
}
public function addItems() {
return $this->navigation->addPages($this->newItems);
}
}
In layout
<?php
$this->AddItemsInNavigation()->addItems(); //plugin
$nawDef = $this->navigation('Zend\Navigation\Default')->menu();
echo $nawDef->setMinDepth(0)->setMaxDepth(4)->setUlClass('nav navbar-nav');
?>
W Blog\Navigation\BlogNavigationFactory.php
<?php
namespace Blog\Navigation;
use Interop\Container\ContainerInterface;
use Zend\Navigation\Navigation;
use Zend\Navigation\Service\DefaultNavigationFactory;
class BlogNavigationFactory extends DefaultNavigationFactory {
protected $pages;
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) {
return new Navigation($this->getPages($container));
}
protected function getPages(ContainerInterface $container) {
$navigation = array();
if (null === $this->pages) {
$navigation[] = array ( //for exemple
'label' => 'Jaapsblog.nl',
'uri' => 'http://www.jaapsblog.nl'
);
$mvcEvent = $container->get('Application')
->getMvcEvent();
$routeMatch = $mvcEvent->getRouteMatch();
$router = $mvcEvent->getRouter();
$pages = $this->getPagesFromConfig($navigation);
$this->pages = $this->injectComponents(
$pages, $routeMatch, $router
);
}
return $this->pages;
}
}
cd.
In module.config.php
'navigation' => array(
'default' => array(
'blog' => array(
'label' => 'Blog',
'route' => 'blog-front',
'controller' => 'blog',
'action' => 'index',
)
)
)
I don't know if it's that are you looking for, but i recommend to take a look on this page:
https://github.com/fabiopaiva/zf2-navigation-bootstrap3

ZF2 add elemets to form on the fly from database

In ZF2, I get the form from the controller factory like this:
class SomeControllerFactory implements FactoryInterface
{
public function CreateService(SeviceLocatorInterface $serviceLocator)
{
$realServiceLocator = $serviceLocator->getServiceLocator();
// other things from service manager
$registrationForm = $realServiceLocator->get('FormElementManager')
->get('Path\To\My\Form\RegistrationForm');
}
return new SomeController(
// controller dependencies, including $registrationForm
);
}
In the RegistrationForm, I have MultiCheckBox:
$this->add([
'type' => 'Zend\Form\Element\MultiCheckBox',
'name' => 'partyRoleIds',
'options' => [
'label' => 'Отношение',
'value_options' => [
[
'value' => '1',
'label' => 'client',
],
[
'value' => '2',
'label' => 'prospect'],
[
'value' => '6',
'label' => 'contractor',
],
],
],
]);
I want to populate value_options from a db query that returns an array like [1 => 'client', 2 => 'prospect'...]. Populating is not an problem, but I don't know how to pass this array as a dependency into the RegistrationForm because in the call $registrationForm = $realServiceLocator->get('FormElementManager')->get('Path\To\My\Form\RegistrationForm');, I don't have any place to add the dependency.
How could I do this?
PS: rewritten the question, please forgive my initial brevity.
In form classes you add the method :
public function setValueOptions($element, array $values_options)
{
$e = $this->get($element);
$e->setValueOptions($values_options);
return $this;
}
In your controller, if your form is $registrationForm you write :
$registrationForm->setValueOptions('partyRoleIds', $valueOptions);
where $valueOptions is an array like your sample.

ZF2 - How to add a new Form in an existing Controller?

I have a login Form LoginForm.php with its Filter LoginFilter.php, that has a View /login/index.phtml, a Controller LoginController.php, two Factory LoginControllerFactory.php & LoginFormFactory.php and it is called in the config.module.php and works perfect. The Form is correctly displayed.
I have a ViewController.php that has a method idAction that shows a post by its id passed by parameter from the homepage in a View called /view/id.phtml. I want to display this Form I created within this View and I don't know how. First, I created the Form exactly as I created the login Form, but I realized that I already configured my id child-route, inside of view route with a Factory in module.config.php.
Then, I tried to set the form in the idAction method, exactly as I did in indexAction in LoginController.php Controller, but I'm receiving the following error: An exception was raised while creating "Rxe\Factory\ViewController"; no instance returned.
I will now show you what I did to try to display this new Form.
First, the Form itself:
class CommentForm extends Form
{
public function buildForm()
{
$this->setAttribute('method', 'POST');
$this->setAttribute('id', 'add-comment-form');
$this->add(array(
'name' => 'comment',
'type' => 'textarea',
'options' => array(
'label' => 'Category'
),
'attributes' => array(
'class' => 'form-control'
)
));
$this->add(array(
'name' => 'submit',
'type' => 'submit',
'attributes' => array(
'class' => 'btn btn-success',
'value' => 'Comment'
)
));
}
}
Form's CommentFormFactory.php calling its Filter and building the Form:
class CommentFormFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$form = new CommentForm();
$form->setInputFilter($serviceLocator->get('Rxe\Factory\CommentFilter'));
$form->buildForm();
return $form;
}
}
The ViewControllerFactory.php calling the CommentFormFactory.php, just like in LoginControllerFactory.php:
class ViewControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$serviceManager = $serviceLocator->getServiceLocator();
$viewController = new ViewController();
$viewController->setPostsTable($serviceManager->get('Rxe\Factory\PostsTable'));
$viewController->setCommentsTable($serviceManager->get('Rxe\Factory\CommentsTable'));
$viewController->setCommentForm($serviceManager->get('Rxe\Factory\CommentForm'));
return $viewController;
}
}
The ViewController.php, calling the form within its idAction's ViewModel:
class ViewController extends AbstractActionController
{
use PostsTableTrait;
use CommentsTableTrait;
private $commentForm;
function setCommentForm($commentForm)
{
$this->commentForm = $commentForm;
}
public function indexAction()
{
$category = $this->params()->fromRoute('category');
return new ViewModel(array(
'posts' => $this->postsTable->getPostsByCategory($category),
'categories' => $category
));
}
public function idAction()
{
$id = $this->params()->fromRoute('id');
$viewModel = new ViewModel(array(
'commentForm' => $this->commentForm,
'commentParams' => $this->params()->fromPost(),
'messages' => $this->flashMessenger()->getMessages(),
'posts' => $this->postsTable->getPostById($id),
'posts' => $this->commentsTable->getNumberOfCommentsByPost($id),
'comments' => $this->commentsTable->getCommentsByPost($id)
));
$viewModel->setTemplate('rxe/view/id.phtml');
if ($this->getRequest()->isPost()) {
$this->commentForm->setData($this->params()->fromPost());
if ($this->commentForm->isValid()) {
$this->flashMessenger()->addMessage('Thank you for your comment. :)');
} else {
$this->flashMessenger()->addMessage('Your comment wasn\'t sent.');
}
}
return $viewModel;
}
}
And finally my module.config.php
'controllers' => array(
'invokables' => array(
'Rxe\Controller\Index' => 'Rxe\Controller\IndexController',
'Rxe\Controller\View' => 'Rxe\Controller\ViewController',
'Rxe\Controller\Login' => 'Rxe\Controller\LoginController'
),
'factories' => array(
'Rxe\Factory\LoginController' => 'Rxe\Factory\LoginControllerFactory',
'Rxe\Factory\ViewController' => 'Rxe\Factory\ViewControllerFactory',
'Rxe\Factory\IndexController' => 'Rxe\Factory\IndexControllerFactory'
)
),
'service_manager' => array(
'factories' => array(
'Rxe\Factory\LoginForm' => 'Rxe\Factory\LoginFormFactory',
'Rxe\Factory\LoginFilter' => 'Rxe\Factory\LoginFilterFactory',
'Rxe\Factory\CommentForm' => 'Rxe\Factory\CommentFormFactory',
'Rxe\Factory\CommentFilter' => 'Rxe\Factory\CommentFilterFactory',
'Rxe\Factory\PostsTable' => 'Rxe\Factory\PostsTableFactory',
'Rxe\Factory\CategoriesTable' => 'Rxe\Factory\CategoriesTableFactory',
'Rxe\Factory\CommentsTable' => 'Rxe\Factory\CommentsTableFactory',
'Zend\Db\Adapter\AdapterService' => 'Zend\Db\Adapter\AdapterServiceFactory'
)
),
Please, let me know if you need me to show you more codes. Thank you in advance.
EDIT #1
If I remove the line that calls the Form in the ViewControllerFactory.php, I get the following error: Fatal error: Call to a member function prepare() on a non-object in /home/vol12_3/byethost4.com/b4_16354889/htdocs/module/Rxe/view/rxe/view/id.phtml on line 31
The id.phtml is:
<!-- Comment form -->
<div id="comment-form-area" class="col-xs-3">
<?php $this->commentForm->prepare() ?>
<?php echo $this->form()->openTag($this->commentForm); ?>
<div class="form-group comment-area">
<?php echo $this->formRow($this->commentForm->get('comment_content')); ?>
</div>
<div class="form-group">
<?php echo $this->formRow($this->commentForm->get('submit')); ?>
</div>
<?php echo $this->form()->closeTag(); ?>
</div>
<!-- /Comment form -->
Try removing these lines
'invokables' => array(
'Rxe\Controller\Index' => 'Rxe\Controller\IndexController',
'Rxe\Controller\View' => 'Rxe\Controller\ViewController',
'Rxe\Controller\Login' => 'Rxe\Controller\LoginController'
),
If it doesn't work, have a look at this tutorial how to create proper controller factories and pass dependencies. https://samsonasik.wordpress.com/2015/03/31/zend-framework-2-using-__invokepluginmanager-manager-in-services-factory/
An example how I build my forms:
namespace Admin\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
class ContentForm extends Form implements InputFilterProviderInterface
{
public function __construct()
{
parent::__construct("content");
}
public function init()
{
$this->setAttribute('method', 'post');
$this->add([
'type' => 'Zend\Form\Element\Text',
'name' => 'title',
'attributes' => [
'required' => true,
'size' => 40,
'id' => "seo-caption",
'placeholder' => 'Title',
],
'options' => [
'label' => 'Title',
],
]);
$this->add([
'type' => 'Zend\Form\Element\Text',
'name' => 'text',
'attributes' => [
'class' => 'ckeditor',
'rows' => 5,
'cols' => 80,
],
'options' => [
'label' => 'Text',
],
]);
}
public function getInputFilterSpecification()
{
return [
[
"name"=>"title",
"required" => true,
'filters' => [
['name' => 'StripTags'],
['name' => 'StringTrim'],
],
'validators' => [
['name' => 'NotEmpty'],
[
'name' => 'StringLength',
'options' => [
'encoding' => 'UTF-8',
'min' => 1,
'max' => 200,
],
],
],
],
[
"name"=>"text",
"required" => true,
'filters' => [
['name' => 'StripTags'],
['name' => 'StringTrim'],
],
'validators' => [
['name' => 'NotEmpty'],
[
'name' => 'StringLength',
'options' => [
'encoding' => 'UTF-8',
'min' => 1,
],
],
],
],
];
}
}
Than I create a Factory
namespace Admin\Factory\Controller;
use Admin\Controller\ContentController;
use Zend\Mvc\Controller\ControllerManager;
class ContentFormFactory
{
/**
* #{inheritDoc}
*/
public function __invoke(ControllerManager $controllerManager)
{
return new ContentController(
$controllerManager->getServiceLocator()->get('FormElementManager')->get('Admin\Form\ContentForm')
);
}
}
Inside module.config.php I have this code
'controllers' => [
'factories' => [
'Admin\Controller\Content' => "Admin\Factory\Controller\ContentFormFactory",
],
'invokables' => [
...
],
],
Please, show us some more code.

Class design with params and dependencies

I designed my database and cache layer after Zend Framework 1, like this:
class Cache
{
public static function create($adapter, array $params)
{
$class_name = 'Cache_Adapter_' . $adapter;
return new $class_name($params);
}
}
abstract class Cache_Adapter
{
public function __construct(array $params)
{
}
}
class Cache_Adapter_File extends Cache_Adapter
{
// ...
}
Usage example:
// config.php
return array(
'cache' => array(
'adapter' => 'file',
'params' => array(
'path' => '/path',
),
),
);
// bootstrap.php
$dic->cache = Cache::create($config['cache']['adapter'], $config['cache']['params']);
This approach is great, because each cache adapter can have different parameters,
for example, file cache need path to directory where to store cache files.
Then I wanted to create cache adapter for storing data in database and realized that instead of
scalar parameter array, database abstraction class dependency is needed.
Currently database connections are registered in dependency injection container:
// config.php
return array(
'db1' => array(
'adapter' => 'mysql',
'params' => array(
'user' => 'root',
'connect_timeout' => 5,
),
),
'db2' => array(
'adapter' => 'sqlsrv',
'params' => array(
'db' => 'foo',
),
),
);
// bootstrap.php
$dic->db1 = Site:Db::create($config['db1']['adapter'], $config['db1']['params']);
$dic->db2 = Site:Db::create($config['db2']['adapter'], $config['db2']['params']);
So I wanted to ask how in addition to scalar configuration parameter array, zero or more specific dependencies can be passed to cache adapters and this can be done in config.php.
class Cache_Adapter_Db extends Cache_Adapter
{
// Instead of abstract Cache_Adapter::__construct(array $params)
// something like this is needed:
// public function __construct(array $params, Db_Adapter $db)
public function __construct(array $params)
{
}
}
There are 2 steps involved: first your cache adapter should call its parent class in the correct way:
class Cache_Adapter_Db extends Cache_Adapter
{
public function __construct(array $params, Db_Adapter $db)
{
parent::__construct($params);
}
}
Second: your factory class Cache should accept more parameters:
class Cache
{
public static function create($adapter, array $params, $optparams = null )
{
$class_name = 'Cache_Adapter_' . $adapter;
return new $class_name($params, $optparams);
}
}
The config php would look like this:
// config.php
return array(
'db1' => array(
'adapter' => 'mysql',
'params' => array(
'user' => 'root',
'connect_timeout' => 5,
),
),
'db2' => array(
'adapter' => 'sqlsrv',
'params' => array(
'db' => 'foo',
),
'options' => 'extraoption'
),
);
and in bootstrap.php:
$dic->db2 = Site:Db::create(
$config['db2']['adapter'],
$config['db2']['params'],
$config['db2']['options']
);

Can anyone provide some resource about unit testing of forms in Zend Framework?

How can I test the forms in zend framework?
I have a login form in my zend project, the Login.php is:
<?php
class DEMO_Form_Login extends Zend_Form {
public function init() {
$this
->setMethod('post')
->addElementPrefixPaths(array(
'decorator' => array('DEMO_Decorator' => '../application/decorators'),
));
$this
->addElement('text', 'username', array(
'label' => _T('USERNAME'),
'required' => true,
'value' => '',
'filters' => array('StringTrim'),
'decorators' => array('ViewHelper')
))
->addElement('password', 'password', array(
'label' => _T('PASSWORD'),
'required' => true,
'value' => '',
'decorators' => array('ViewHelper')
))
->addElement('submit', 'submit', array(
'label' => _T('LOG_INTO'),
'ignore' => true,
'decorators' => array(
array('Submit', array('separator'=>'<br />')))
));
}
}
How can I test it? Can anyone provide some resource about it?
I cannot think of any resource, but I can give you one example of how I would do it.
So, I would create a FormTestCase class as follows:
class FormTestCase extends PHPUnit_Framework_TestCase {
private $_form;
public function setUp() {
parent::setUp();
}
}
Then each form could be tested as follows:
class DemoFormTest extends FormTestCase {
public function setUp() {
parent::setUp();
$this->_form = new My_Form_Demo();
}
public function testCorrectData() {
$mockInputData = array(
'username' => 'somename',
'password' => 'somepass',
'submit' => 'LOG_INTO'
);
$this->assertTrue($this->_form->isValid($mockInputData));
}
public function testInCorrectData() {
$mockInputData = array(
'username' => 'somename',
// password not given
'submit' => 'LOG_INTO'
);
$this->assertFalse($this->_form->isValid($mockInputData));
}
// some other tests
}
In the above example My_Form_Demo is simplified version of your form. I needed to simplify it, because I do not have your custom decorators and I could not run the test. The setup that I used for this example, can be seen here (along with all my other tests).
Hope this will help you.

Categories