I have problem with the routing in ZF2. I want to make dynamic routing for the software, that I'm making.
For example:
This is the URL: http://localhost:8080/application/index.json/
And this is my module.config (router part):
'router' => array(
'routes' => array(
'home' => array(
'type' => 'Zend\Mvc\Router\Http\Segment',
'options' => array(
'route' => '/',
'defaults' => array(
'controller' => 'Application\Controller\Index',
),
),
),
'restful' => array(
'type' => 'Zend\Mvc\Router\Http\Segment',
'options' => array(
'route' => '/:module/[:controller[/:action][.:formatter][/:id]]',
'constraints' => array(
'module' => '[a-zA-Z][a-zA-Z0-9_-]*',
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'formatter' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[a-zA-Z0-9_-]*'
),
),
),
),
),
Everything is working fine, but when I create new controller, have to add it to controllers['invokables'] setting in module.config.
'controllers' => array(
'invokables' => array(
'index' => 'Application\Controller\IndexController',
'cloud' => 'Application\Controller\CloudController',
),
),
So the question is, how to automate the controllers['invokables'] to process requests dynamically, without describing every controller in it.
Fast and dirty, but you get the idea.
namespace Application;
use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;
class Module
{
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$eventManager->attach (MvcEvent::EVENT_ROUTE, function (MvcEvent $e) {
$controller_loader = $e->getApplication ()->getServiceManager ()->get ('ControllerLoader');
$controller = $e->getRouteMatch ()->getParam ('controller');
$controller_class = '\Application\Controller\\'.ucfirst ($controller).'Controller';
// Add service locator to the controller
$controller_object = new $controller_class;
$controller_object->setServiceLocator ($e->getApplication ()->getServiceManager ());
// ------------------------------------
$controller_loader->setService ($controller, $controller_object);
});
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
}
Related
I'm trying to create a simple CRUD in Zf2 to get to know it and I'm having problems routing the only controller I have. I have this error;
"The requested controller could not be mapped to an existing controller class".
I'm trying to call this route : http://zf2.local/Listapp
This is my structure :
module/Listapp/src/Listapp/Controller/ListappController.php
The namespace is namespace Listapp\Controller;
This is my autoloader config :
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
// Autoload Listapp classes
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
// Autoload ListappController classes
'ListappController' => __DIR__ . '/src/Listapp',
)
)
);
}
And this is my module.config.php :
return array(
'controllers' => array(
'invokables' => array(
'Listapp\Controller\Listapp' => 'Listapp\Controller\ListappController'
)
),
'router' => array(
'routes' => array(
'listapp' => array(
'type' => 'segment',
'options' => array(
'route' => '/[:controller[/:action][/:id]]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Listapp\Controller\Listapp',
'action' => 'index',
),
),
),
),
),
'view_manager' => array(
'template_path_stack' => array(
'Listapp' => __DIR__ . '/../view',
),
), );
Any help would be appreciated thanks !
EDIT:
This is the code in my controller (minus the other CRUD functions) :
namespace Listapp\Controller;
use Zend\Mvc\Controller\AbstractActionController;
class ListappController extends AbstractActionController
{
public function indexAction()
{
}
}
So just to further explain my comment, by including a :controller segment in your route, you've told ZF to try and match the first thing in your URL to something that the controller manager can load (in your case, one of the keys in you controller invokables). The controller default you defined in your route would only apply if you visited http://zf2.local/.
So for you, the quickest fix is to change your configuration to:
'controllers' => array(
'invokables' => array(
'Listapp' => 'Listapp\Controller\ListappController'
)
),
'Listapp' in the URL will then match this controller, and everything will work as you expect.
In general it makes things clearer if you avoid using :controller in routes and have at least one route per controller instead, e.g.:
'controllers' => array(
'invokables' => array(
'Listapp\Controller\Listapp' => 'Listapp\Controller\ListappController'
)
),
'router' => array(
'routes' => array(
'listapp' => array(
'type' => 'segment',
'options' => array(
'route' => '/listapp[/:action[/:id]]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Listapp\Controller\Listapp',
'action' => 'index',
),
),
),
),
),
I'm new with Zend Framework, i used to use framework CI and Laravel but in my project i should use ZF2. Honestly, i don't get it how to building app with ZF2. I followed the instructions from here : https://www.youtube.com/watch?v=OYkVHiXeGeY
but i got some error.
Zend\Mvc\Controller\ControllerManager::createFromInvokable: failed retrieving "csnusercontrolleruser(alias: CsnUser\Controller\User)" via invokable class "CsnUser\Controller\UserController"; class does not exist
my strucktur folder:
:: module
->CsnUser
>>config
->module.config.php
>>src
->CsnUser
->Controller
->UserController.php
>>view
->csn-user
->user
->index.phtml
>>Module.php
Module.php
<?php
namespace CsnUser;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
class Module{
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespace' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
}
module.config.php
<?php
return array(
'controllers' => array(
'invokables' => array(
'CsnUser\Controller\User' => 'CsnUser\Controller\UserController',
),
),
'router' => array(
'routes' => array(
'csn_user' => array(
'type' => 'Literal',
'options' => array(
'route' => '/csn-user',
'defaults' => array(
'__NAMESPACE__' => 'CsnUser\Controller',
'controller' => 'User',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
'default' => array(
'type' => 'Segment',
'options' => array(
'route' => '/[:controller[/:action[/:id]]]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]*',
),
'defaults' => array(),
),
),
),
),
),
),
'view_manager' => array(
/*'template_map' => array(
'layout/Auth' => __DIR__ . '/../view/layout/xxxx.phtml',
),*/
'template_path_stack' => array(
'csn_user' => __DIR__ . '/../view'
),
),
);
UserController.php
<?php
namespace CsnUser\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class UserController extends AbstractActionController{
public function indexAction()
{
return new ViewModel();
}
}
index.phtml
<h1>Welcome</h1>
Can anyone help me ?
Your getAutoLoaderConfig() method is incorrect.
Try this once:
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
Pay attention to the key namespaces in the array.
In your instruction video at 4:40 they also use the plural :)
I solved the problem by adding a string to composer.json:
"autoload": {
"psr-0": {
"MyModule": "module/MyModule/src/"
}
}
and then, in the console, I navigated to the project folder and ran:
php composer.phar update
First create a folder coinsproject then,
coinsproject-->config folder,src folder,view folder,autoload_classmap.php,Module.php
In Coinsproject/autoload_classmap.php
<?php
return array();
?>
In Coinsproject/Module.php
<?php
namespace Coinsproject;
class Module
{
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';
}
}
?>
In Coinsproject/config/module.config.php
<?php
return array(
'controllers' => array(
'invokables' => array(
'Coinsproject\Controller\Coinsproject' => 'Coinsproject\Controller\CoinsprojectController',
),
),
'router' => array(
'routes' => array(
'album' => array(
'type' => 'segment',
'options' => array(
'route' => '/coinsproject[/][:action][/:id]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Coinsproject\Controller\Coinsproject',
'action' => 'index',
),
),
),
),
),
'view_manager' => array(
'template_path_stack' => array(
'coinsproject' => __DIR__ . '/../view',
),
),
);
?>
In Coinsproject/src/Coinsproject/Controller/CoinsprojectController.php
<?php
namespace Coinsproject\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class CoinsprojectController extends AbstractActionController
{
public function indexAction()
{
return new ViewModel();
}
}
?>
In Coinsproject/view/coinsproject/coinsproject/index.phtml
<html>
my name is swapnil.
</html>
And after that i call my module in root/config/application.config.php but when i run it its not working anything.Where i did my mistakes?
In Coinsproject/config/module.config.php
'routes' => array(
'album' => array(
to
'routes' => array(
'coinsproject' => array(
you forget to change the route into coinsproject first, i don't know if it's the only correction to make, but it's a beginning.
may be you should put index.phtml in
Coinsproject/view/coinsproject/index.phtml
[Modulename]/view/[Controllername]/[actionname].phtml
not in
Coinsproject/view/coinsproject/coinsproject/index.phtml
and try to correct define view script (phtml file) in module.config.php in view_manager
'view_manager' => array(
'template_map' => array(
coinsproject/coinsproject/index => __DIR__ . '/../view/coinsproject/index.phtml
)
)
I'm currently learning Zend2. My first attempt is create secured application with basic login form. So my first idea was to create a common SecuredController, that checks for user identity in his constructor and redirects if necessary. I saw that solution for Zend1 and was working:
class SecuredController extends AbstractActionController
{
function __construct()
{
$auth = new AuthenticationService();
if ( $auth->hasIdentity() ) {
return $this->redirect()->toRoute("ts");
}
return $this->redirect()->toRoute( "login" );
}
}
Then extending some other controllers used throughout appliation:
class MainController extends SecuredController
{
public function indexAction()
{
return new ViewModel();
}
}
I omitted LoginController and IndexController(same as MainController now), but you get the idea how it is set up. Confing for module looks like this:
<?php
namespace Main;
return array(
'controllers' => array(
'invokables' => array(
'Main\Controller\Secured' => 'Main\Controller\Common\SecuredController',
'Main\Controller\Login' => 'Main\Controller\LoginController',
'Main\Controller\Main' => 'Main\Controller\MainController',
'Main\Controller\Index' => 'Main\Controller\IndexController',
),
),
'router' => array(
'routes' => array(
'home' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => '/',
'defaults' => array(
'controller' => 'Main\Controller\Index',
'action' => 'index',
),
),
),
'main' => array(
'type' => 'segment',
'options' => array(
'route' => '/ts[/][:action]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
),
'defaults' => array(
'controller' => 'Main\Controller\Main',
'action' => 'index',
),
),
),
'login' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => '/login',
'defaults' => array(
'controller' => 'Main\Controller\Login',
'action' => 'login',
),
),
),
'logout' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => '/logout',
'defaults' => array(
'controller' => 'Main\Controller\Login',
'action' => 'logout',
),
),
),
),
),
'view_manager' => array(
'display_not_found_reason' => true,
'display_exceptions' => true,
'doctype' => 'HTML5',
'not_found_template' => 'error/404',
'exception_template' => 'error/index',
'template_map' => array(
'layout/login' => __DIR__ . '/../view/layout/login.phtml',
'layout/layout' => __DIR__ . '/../view/layout/layout.phtml',
'application/index/index' => __DIR__ . '/../view/main/index/index.phtml',
'error/404' => __DIR__ . '/../view/error/404.phtml',
'error/index' => __DIR__ . '/../view/error/index.phtml',
),
'template_path_stack' => array(
__DIR__ . '/../view',
),
),
'doctrine' => array(
'driver' => array(
__NAMESPACE__ . '_driver' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array( __DIR__ . '/../src/' . __NAMESPACE__ . '/Entity' )
),
'orm_default' => array(
'drivers' => array(
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
)
)
)
),
);
But unfortunately its not working I have error:
Url plugin requires that controller event compose a router; none found
Anyone has a clue how implement my scenario? Securing whole aplication and redirecting to /login route users without identity.
Add in your super controller.
namespace Admin\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Authentication\AuthenticationService;
...
class AdminController extends AbstractActionController
{
public function onDispatch(\Zend\Mvc\MvcEvent $e)
{
/**
* Verifica se o usuario se encontra logado, caso contrario redirecion ele para o login
*/
$this->authService = new AuthenticationService();
if(!$this->authService->hasIdentity()){
$this->redirect()->toRoute("login");
}
return parent::onDispatch($e);
}
...
}
I believe this will work in your project design.
issue 1)
return $this->redirect()->toRoute("ts");
You don't have a route named 'ts' in your config, you need to setup the route when using toRoute().
issue 2)
You aren't setting up AuthenticationService properly. You need to specify an adapter to use this.
Instead of instantiating it in the controller, define it in your ServiceManager config.
config:
'My\AuthService' => function($sm) {
$auth = new \Zend\Authentication\AuthenticationService();
$auth->setAdatper(/** LDAP or What ever **/);
return $auth;
},
controller:
// already setup for you in the service manager
$authService = $this->getServiceLocator()->get('My\AuthService');
You could always try zfcUser module if you want something out of the box tyo allow registering users etc:
https://github.com/ZF-Commons/ZfcUser
Authentication Module width login page
I'm having trouble configuring multiple namespaces/classes under same module.
For example, I have a module called "Account", in which I'd like to include all account related classes (companies: 'accounts', users: 'users', external api: 'api' etc.. ). Module structure looks like this..
/Account
- Module.php
- /config
- /view
- /src
- /Account
- /Controller (AccountController.php)
- /Form (AccountForm.php)
- /Model (Account.php + AccountTable.php)
- /User
- /Controller (UserController.php)
- /Form (UserForm.php)
- /Model (User.php + UserTable.php)
- /Api
- Api.php (simple class)
Being new to ZF2, I decided to keep things simple and stupid and Not to try implementing complex routing to Account module. So, in order to trigger indexAction for UserController, url should be /user (!)
Here's the module class:
namespace Account;
use Account\Model\AccountTable;
use Account\Model\UserTable;
class Module
{
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 getServiceConfig()
{
return array(
'factories' => array(
'Account\Model\AccountTable' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$table = new AccountTable($dbAdapter);
return $table;
},
'Account\Model\UserTable' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$table = new UserTable($dbAdapter);
return $table;
},
),
);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
}
And the module.config file
return array(
'controllers' => array(
'invokables' => array(
'Account\Controller\Account' => 'Account\Controller\AccountController',
'Account\Controller\User' => 'Account\Controller\UserController',
),
),
'router' => array(
'routes' => array(
'account' => array(
'type' => 'segment',
'options' => array(
'route' => '/account[/:action[/:accountId]]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'accountId' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Account\Controller\Account',
'action' => 'index',
),
),
/*
'may_terminate' => true,
'child_routes' => array(
'user' => array(
'type' => 'literal',
'options' => array(
'route' => '/user[/:action[/:userId]]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'userId' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Account\Controller\User',
'action' => 'index'
)
)
)
),
*/
),
'user' => array(
'type' => 'segment',
'options' => array(
'route' => '/user[/:action[/:userId]]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'userId' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Account\Controller\User',
'action' => 'index',
),
),
)
),
),
'view_manager' => array(
'template_path_stack' => array(
'account' => __DIR__ . '/../view',
'user' => __DIR__ . '/../view',
),
),
);
But the error I'm getting is, "Class 'Account\Controller\UserController' not found". I am sure i've missed something. Any clue please?
Thanks
You must let the StandardAutoloader know about your new namespace:
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
// This is for the Account namespace
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
// And this is for the User namespace
'User' => __DIR__ . '/src/' . 'User',
),
),
);
}
In the module.config.php
return array(
'controllers' => array(
'invokables' => array(
'Account\Controller\Account' => 'Account\Controller\AccountController',
// The key can be what ever you want, but the value must be a valid
// class name. Your UserController lives in the User namespace,
// not in Account
'Account\Controller\User' => 'User\Controller\UserController',
),
),
/* ... */
);
The StandardLoader needs to know where to find the classes. You can define it with an option called namespaces which is an array that contains absolute (or relative to the current script) paths. It should look like this:
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__
)
)
__NAMESPACE__ is the name of the module, and __DIR__ the absolute path to the Module.php script
Check http://framework.zend.com/manual/2.0/en/modules/zend.loader.standard-autoloader.html
The ClassMapAutoloader is used for performance: you define the class key and its exactly path to the file, instead of a folder which zf2 has to browse its contents doing filesystem operations (StandardLoader's way).
Check http://framework.zend.com/manual/2.0/en/modules/zend.loader.class-map-autoloader.html