PhalconPHP set default controller and default action - php

How do I set default controller and default action in PhalconPHP?
I have used this code without success:
$di->set('router', function () {
$router = new \Phalcon\Mvc\Router();
$router->notFound(['controller' => 'Index', 'action'=> 'index']);
$router->setDefaultController('Bookmarks');
$router->setDefaultAction('index');
return $router;
});

It can fail because you did not set the default namespace for the controller. I also had some trouble with the case of the controller name when i switched from windows to linux. Using lower case name seemed to solve the issue.
$router = new Phalcon\Mvc\Router\Annotations(false);
$router->removeExtraSlashes(true);
$router->setDefaultNamespace('App\Controllers\\');
$router->setDefaultController('index');
$router->setDefaultAction('index');
/**
* Standard MVC routes
*/
$router->add('/', []);
$router->add(
'/:controller',
[
'controller' => 1
]
);
$router->add(
'/:controller/:action/:params',
[
'controller' => 1,
'action' => 2,
'params' => 3
]
);
return $router;
Also, notFound method did not work for me. So i attached a listener to the eventsManager to handle this issue.
$di->set('dispatcher', function () use ($di) {
// ERROR 404 - Page not found
$evManager = $di->getShared('eventsManager');
$evManager->attach(
"dispatch:beforeException",
function ($event, $dispatcher, $exception) {
switch ($exception->getCode()) {
case Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
case Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
$dispatcher->forward(
[
'controller' => 'error',
'action' => 'show404'
]
);
return false;
}
}
);
$dispatcher = new Dispatcher();
$dispatcher->setEventsManager($evManager);
return $dispatcher;
}, true);

Related

Routing issues with subcontroller structure in Phalcon

I've implemented a router, securityplugin (for ACL) and notfoundplugin.
I want my site to be set up with a subcontroller structure: Link
The website is set up in the following main parts:
Index
Admin
Tickets
Account
The issues I have:
When calling: domain/admin/actionofadmin
Returns custom 404: "Page doesn't exist" instead of the normal action result
When calling: domain/admin/actionofadmin. (<-- mind the dot at the end)
Returns index action of index controller instead of the normal action result
Why does the router return theses results? How can they be fixed?
Extra questions:
How does Phalcon know where to find a view and link it to the correct controller? Example: A dashboardController resides in a folder "admin" inside the folder "controllers".
How does Phalcon know that in the SecurityPlugin ACL it needs to search for the correct controller while it doesn't get a namespace? Example: When I want to allow controller Tickets of namespace app\controllers\admin to be only viewed by admin users.
Extra information:
Phalcon 3.0.3
PHP 5.6
Let me know if you need any more information / files or if I should post it somewhere else for ease of reading.
Files:
/app/controllers/AdminController.php
<?php
namespace Ontrack\Controllers;
class AdminController extends ControllerBase
{
public function indexAction(){
}
public function testAction(){
echo "test";
}
}
/app/config/services.php Excerpt
//This makes sure the routes are correctly handled for authorized/unauthorized
/**
* MVC dispatcher
*/
$di->set("dispatcher", function () use ($di) {
// Create an events manager
$eventsManager = $di->getShared('eventsManager');
/**
*Check if the user is allowed to access certain action using the SecurityPlugin
*Listen for events produced in the dispatcher using the Security plugin
*/
$eventsManager->attach("dispatch:beforeDispatch", new SecurityPlugin());
// Handle exceptions and not-found exceptions using NotFoundPlugin
$eventsManager->attach("dispatch:beforeException", new NotFoundPlugin());
$dispatcher = new Dispatcher();
$dispatcher->setDefaultNamespace('Ontrack\Controllers');
// Assign the events manager to the dispatcher
$dispatcher->setEventsManager($eventsManager);
return $dispatcher;
}
);
/app/config/loader.php
<?php
$loader = new \Phalcon\Loader();
/**
* We're a registering a set of directories taken from the configuration file
*/
$loader->registerDirs(
[
$config->application->controllersDir,
$config->application->modelsDir,
$config->application->pluginsDir
]
)->register();
$loader->registerNamespaces(
[
'Ontrack\Controllers' => APP_PATH . '/controllers/',
'Ontrack\Controllers\Admin' => APP_PATH . '/controllers/admin',
'Ontrack\Models' => APP_PATH . '/models/'
]
)->register();
/app/config/routes.php
<?php
$router = new Phalcon\Mvc\Router(false);
$router->setDefaults(
[
"controller" => "index",
"action" => "index",
]
);
$router->add('/:controller/:action/:params', [
'namespace' => 'Ontrack\Controllers',
'controller' => 1,
'action' => 2,
'params' => 3,
]);
$router->add('/:controller/:action', [
'namespace' => 'Ontrack\Controllers',
'controller' => 1,
'action' => 2,
]);
$router->add('/:controller', [
'namespace' => 'Ontrack\Controllers',
'controller' => 1,
]);
$router->add('/admin/:controller/:action/:params', [
'namespace' => 'Ontrack\Controllers\Admin',
'controller' => 1,
'action' => 2,
'params' => 3,
]);
$router->add('/admin/:controller/:action', [
'namespace' => 'Ontrack\Controllers\Admin',
'controller' => 1,
'action' => 2,
]);
$router->add('/admin/:controller', [
'namespace' => 'Ontrack\Controllers\Admin',
'controller' => 1,
]);
$router->removeExtraSlashes(true);
return $router;
/app/plugins/SecurityPlugin.php
<?php
use Phalcon\Acl;
use Phalcon\Acl\Role;
use Phalcon\Acl\Adapter\Memory as AclList;
use Phalcon\Acl\Resource;
use Phalcon\Events\Event;
use Phalcon\Mvc\User\Plugin;
use Phalcon\Mvc\Dispatcher;
class SecurityPlugin extends Plugin
{
/**
* Returns an existing or new access control list
*
* #returns AclList
*/
public function getAcl()
{
if (!isset($this->persistent->acl)) {
$acl = new AclList();
$acl->setDefaultAction(Acl::DENY);
// Register roles
$roles = [
'admins' => new Role(
'admins',
'Website administrators'
),
'users' => new Role(
'users',
'Member privileges, granted after sign in.'
),
'guests' => new Role(
'guests',
'Anyone browsing the site who is not signed in is considered to be a "Guest".'
)
];
foreach ($roles as $role) {
$acl->addRole($role);
}
//Private area resources
$privateResources = array(
'account' => array('*')
);
$privateResourcesAdmin = array(
'admin' => array('*'),
'tickets' => array('*')
);
//Public area resources
$publicResources = array(
'index' => array('*'),
'register' => array('*'),
'errors' => array('show401', 'show404', 'show500'),
'register' => array('*'),
'login' => array('*'),
'logout' => array('*')
);
foreach ($privateResources as $resource => $actions) {
$acl->addResource(new Resource($resource), $actions);
}
foreach ($privateResourcesAdmin as $resource => $actions) {
$acl->addResource(new Resource($resource), $actions);
}
foreach ($publicResources as $resource => $actions) {
$acl->addResource(new Resource($resource), $actions);
}
//Grant access to public areas to users, admins and guests
foreach ($roles as $role) {
foreach ($publicResources as $resource => $actions) {
foreach ($actions as $action){
$acl->allow($role->getName(), $resource, $action);
}
}
}
//Grant access to private area to role Users
foreach ($privateResources as $resource => $actions) {
foreach ($actions as $action){
$acl->allow('users', $resource, $action);
}
}
foreach ($privateResourcesAdmin as $resource => $actions) {
foreach ($actions as $action){
$acl->allow('admins', $resource, $action);
}
}
//The acl is stored in session, APC would be useful here too
$this->persistent->acl = $acl;
}
return $this->persistent->acl;
}
/**
* This action is executed before execute any action in the application
*
* #param Event $event
* #param Dispatcher $dispatcher
* #return bool
*/
public function beforeDispatch(Event $event, Dispatcher $dispatcher){
$auth = $this->session->has('auth');
if (!$auth){
$role = 'guests';
} else {
$authSession = $this->session->get("auth");
if($authSession['account_type'] == 99){
$role = 'admins';
} else {
$role = 'users';
}
}
$controller = $dispatcher->getControllerName();
$action = $dispatcher->getActionName();
$acl = $this->getAcl();
if (!$acl->isResource($controller)) {
$dispatcher->forward([
'controller' => 'errors',
'action' => 'show404'
]);
return false;
}
$allowed = $acl->isAllowed($role, $controller, $action);
if (!$allowed) {
if($controller === 'admin'){
$dispatcher->forward(array(
'controller' => 'errors',
'action' => 'show404'
));
} else {
$dispatcher->forward(array(
'controller' => 'errors',
'action' => 'show401'
));
}
return false;
}
}
}
/app/plugins/NotFoundPlugin.php
<?php
use Phalcon\Events\Event;
use Phalcon\Mvc\User\Plugin;
use Phalcon\Dispatcher;
use Phalcon\Mvc\Dispatcher\Exception as DispatcherException;
use Phalcon\Mvc\Dispatcher as MvcDispatcher;
/**
* NotFoundPlugin
*
* Handles not-found controller/actions
*/
class NotFoundPlugin extends Plugin
{
/**
* This action is executed before execute any action in the application
*
* #param Event $event
* #param MvcDispatcher $dispatcher
* #param Exception $exception
* #return boolean
*/
public function beforeException(Event $event, MvcDispatcher $dispatcher, Exception $exception)
{
error_log($exception->getMessage() . PHP_EOL . $exception->getTraceAsString());
if ($exception instanceof DispatcherException) {
switch ($exception->getCode()) {
case Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
case Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
$dispatcher->forward(array(
'controller' => 'errors',
'action' => 'show404'
));
return false;
}
}
$dispatcher->forward(array(
'controller' => 'errors',
'action' => 'show500'
));
return false;
}
}
When calling: domain/admin/actionofadmin(as i understand domain is for example www.google.pl) phalcon is expecting ActionofadminController according to your routes. Are you sure there is such controller? Don't sure why with with dot it's hitting index controller and index action though.
How does Phalcon know where to find a view and link it to the correct controller? Example: A dashboardController resides in a folder "admin" inside the folder "controllers".
It's getting this info from dispatcher. Mvc application if you don't render view your self is implicit automatically rendering. Check this source: https://github.com/phalcon/cphalcon/blob/master/phalcon/mvc/application.zep#L348 And other view classes.
About Acl Plugin - it doesn't check for namespace at all. So if you have two controllers with same name but other namespace this wil cause obviously a problem. You just need to change security plugin to your needs.

Phalcon - Module exception if does not exist

I have set the Route:
$router->add('/:module/:controller/:action/:params', [
'module' => 1,
'controller' => 2,
'action' => 3,
'params' => 4
]);
When I enter URL to the browser, for example: auth/login/index and module under this URL does not exist, so it throws an exception:
Phalcon\Mvc\Application\Exception: Module 'auth' isn't registered in the application container
How can I catch this exception?
SOLUTION:
$router->add('/:module/:controller/:action/:params', [
'module' => 1,
'controller' => 2,
'action' => 3,
'params' => 4
])->beforeMatch(function($uri) use ($application) {
$modules = $application->getModules();
$moduleName = array_filter(explode('/', $uri))[1];
if(!isset($modules[$moduleName]))
return false;
return true;
});
In beforeMatch method I check If module exist.
For second param you can use closure and check via
if ($di->has('modulename'))
Update1
As I can see https://github.com/phalcon/cphalcon/blob/master/phalcon/mvc/application.zep#L232
you can use event manager and return false from beforeStartModule if module not found in DI
if typeof eventsManager == "object" {
if eventsManager->fire("application:beforeStartModule", this, moduleName) === false {
return false;
}
}
Update2
Also you can use dispatcher setting:
// Initialize the Dispatcher
$di->setShared('dispatcher', function() use ($eventsManager) {
$dispatcher = new \Phalcon\Mvc\Dispatcher;
// Attach a listener for type "dispatch:beforeException"
$eventsManager->attach('dispatch:beforeException', function($event, $dispatcher, $exception) {
/**
* #var \Phalcon\Mvc\Dispatcher\Exception $exception
* #var \Phalcon\Mvc\Dispatcher $dispatcher
*/
switch ($exception->getCode()) {
case \Phalcon\Mvc\Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
case \Phalcon\Mvc\Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
case ANY OTHER CODE HERE:
$dispatcher->forward([
'controller' => 'error',
'action' => 'show404'
]);
return false;
}
});
// Setting up the Dispatcher component
$dispatcher->setDefaultNamespace('your_default_namespace_here');
// Obtain the Events Manager from the DI and bind the eventsManager to the module dispatcher
$dispatcher->setEventsManager($eventsManager);
return $dispatcher;
});

How do I change security redirect behavior in Silex?

If I make an AJAX request to a secured area, it sends a 302 header and then redirects to the login page in that request. Is there a way to configure the firewall to just give a different header and NOT redirect? Is that something I would need to handle in a ->before call? Or would a better way be to override the authentication entry point, as suggested in Symfony security return 401 response instead of redirect? That is for Symfony so not sure if Silex has some better sugar.
Tried this and other things:
$app->register(new SecurityServiceProvider(), array(
'security.firewalls' => array(
'default' => array(
'pattern' => '^/',
'anonymous' => true,
'oauth' => array(
'failure_path' => '/login',
'with_csrf' => true
),
'logout' => array(
'logout_path' => '/logout',
'with_csrf' => true
),
)
),
));
$app['security.authentication.entry_point.default.oauth'] = $app->share(function ($app) {
return new AuthenticationEntryPointComponent();
});
$app['security.authentication.entry_point.default.form'] = $app->share(function ($app) {
return new AuthenticationEntryPointComponent();
});
default is the name of the key in security.firewalls. Still getting the 302.
I had to dig around so much code but got it:
My controller:
$app['security.entry_point.form._proto'] =$app->protect(function ($name, array $options) use ($app) {
return $app->share(function () use ($app, $options) {
$login_path = isset($options['login_path']) ? $options['login_path'] : '/login';
return new MyCustomClass($app['security.http_utils'], $login_path);
});
});
The class:
namespace Yours;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class MyCustomClass implements AuthenticationEntryPointInterface {
protected $_login_path = '/login';
protected $_http_tools;
/**
* #param string $loginPath The path to the login form
*/
public function __construct(HttpUtils $http_utils, $login_path = '') {
$this->_http_tools = $http_utils;
if ( $login_path ) {
$this->_login_path = $login_path;
}
}
/**
* {#inheritDoc}
*/
public function start(Request $request, AuthenticationException $authException = null) {
if ($request->isXmlHttpRequest()) {
$response = new Response(json_encode([]), 401);
$response->headers->set('Content-Type', 'application/json');
return $response;
}
return $this->_http_tools->createRedirectResponse($request, $this->_login_path);
}
}
I was also facing this problem and was surprised that SecurityServiceProvider does not support entry_point option in firewal's configuration (just like Symfony security configuration does).
I've resolved it in different way. Instead of overriding security.entry_point.form._proto I followed SecurityServiceProviderbuilt-in authentication factories and in my custom service provider's register() I wrote like this:
$app['security.authentication_listener.factory.MY_CUSTOM_FIREWALL'] = $app->protect(function ($name, array $options) use ($app) {
$app['security.authentication_provider.'.$name.'.anonymous'] = $app['security.authentication_provider.anonymous._proto']($name);
$app['security.authentication_listener.'.$name.'.anonymous'] = $app['security.authentication_listener.anonymous._proto']($name, $options);
$app['security.entry_point.'.$name.'.MY_CUSTOM_FIREWALL'] = $app->share(function() use ($app) {
return new MyCustomFirewallEntryPoint($app['url_generator']);
});
return [
'security.authentication_provider.'.$name.'.anonymous',
'security.authentication_listener.'.$name.'.anonymous',
'security.entry_point.'.$name.'.MY_CUSTOM_FIREWALL',
'anonymous'
];
});
and then in security config:
$app->register(new Silex\Provider\SecurityServiceProvider(), [
'security.firewalls' => [
// All other routes require valid user
'secured' => [
'pattern' => '^.*$',
'anonymous' => true,
// It will search for custom factory
'MY_CUSTOM_FIREWALL' => true
],
],
]);

Phalcon: not found page error handler

how to create 404 error page for manual bootstrap for example in this app ? http://album-o-rama.phalconphp.com/
i use this dispatcher :
$di->set(
'dispatcher',
function() use ($di) {
$evManager = $di->getShared('eventsManager');
$evManager->attach(
"dispatch:beforeException",
function($event, $dispatcher, $exception)
{
switch ($exception->getCode()) {
case PhDispatcher::EXCEPTION_HANDLER_NOT_FOUND:
case PhDispatcher::EXCEPTION_ACTION_NOT_FOUND:
$dispatcher->forward(
array(
'controller' => 'error',
'action' => 'show404',
)
);
return false;
}
}
);
$dispatcher = new PhDispatcher();
$dispatcher->setEventsManager($evManager);
return $dispatcher;
},
true
);
Try this in your index.php:
$di->set('dispatcher', function() {
$eventsManager = new \Phalcon\Events\Manager();
$eventsManager->attach("dispatch:beforeException", function($event, $dispatcher, $exception) {
//Handle 404 exceptions
if ($exception instanceof \Phalcon\Mvc\Dispatcher\Exception) {
$dispatcher->forward(array(
'controller' => 'index',
'action' => 'show404'
));
return false;
}
//Handle other exceptions
$dispatcher->forward(array(
'controller' => 'index',
'action' => 'show503'
));
return false;
});
$dispatcher = new \Phalcon\Mvc\Dispatcher();
//Bind the EventsManager to the dispatcher
$dispatcher->setEventsManager($eventsManager);
return $dispatcher;
}, true);
The recommended functionality here is:
http://docs.phalconphp.com/en/latest/reference/routing.html#not-found-paths
and maybe
routing.html#dealing-with-extra-trailing-slashes
For manual bootstrap, instead of use dispatcher, you could set a router
/**
* Registering a router
*/
$di->set('router', require __DIR__.'/../common/config/routes.php');
Then add this route rule at 'common/config/routes.php'.
$router->notFound(array(
'module' => 'frontend',
'namespace' => 'AlbumOrama\Frontend\Controllers\\',
'controller' => 'index',
'action' => 'route404'
));
Finally define a controller and a view to capture this action.
And voilĂ : 404 error page!
Just for comment, I pull request this solution for the app you mentioned:
https://github.com/phalcon/album-o-rama/pull/5/files
For new version of Phalcon you can handle error using routes by adding this code to service.php
$di->set('router',function() use($Config){
$router = new \Phalcon\Mvc\Router();
$router->notFound(array(
"controller" => "error",
"action" => "error404"
));
return $router;
});
public function show404Action()
{
$this->response->setStatusCode(404, 'Not Found');
$this->view->pick('error/show404');
}

A multi module MVC structure in phalconphp

Hi I'm trying to implement a Multi Module MVC for frontend and backend like what is in phalconphp documentations. But I can't make it work. it's about an hour but I really can not understand where is the problem.
Could anyone guide me how can I make a skeleton for a multi module mvc for frontend and backend.
what should I put in Moudle.php for frontend and backend
And also what should I put in bootstrap file located in public/index.php
And any extra file or information I need.
The code in the phalcon/mvc repository on GitHub will help. You can find it here:
https://github.com/phalcon/mvc/tree/master/multiple
More specifically, you'll be interested in:
https://github.com/phalcon/mvc/blob/master/multiple/public/index.php
https://github.com/phalcon/mvc/blob/master/multiple/apps/backend/Module.php
I tend to use this in my index.php:
$application = new \Phalcon\Mvc\Application($di);
// Register the installed modules
$application->registerModules(
array(
'web' => array(
'className' => 'Apps\Web\Module',
'path' => '../apps/web/Module.php',
)
)
);
echo $application->handle()->getContent();
And in my Module.php:
<?php
namespace Apps\Web;
use Phalcon\Loader;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Mvc\View;
use Phalcon\Mvc\ModuleDefinitionInterface;
class Module implements ModuleDefinitionInterface
{
/**
* Register a specific autoloader for the module
*/
public function registerAutoloaders()
{
$loader = new Loader();
$loader->registerNamespaces(
array(
'Apps\Web\Controllers' => '../apps/web/controllers/',
)
);
$loader->register();
}
/**
* Register specific services for the module
* #param \Phalcon\DI\FactoryDefault $di
*/
public function registerServices($di)
{
//Registering a dispatcher
$di->set(
'dispatcher',
function() use ($di) {
$eventsManager = $di->getShared('eventsManager');
$dispatcher = new Dispatcher();
$dispatcher->setDefaultNamespace('Apps\Web\Controllers');
$eventsManager->attach(
'dispatch:beforeException',
function($event, $dispatcher, $exception) use ($di) {
/* #var $dispatcher \Phalcon\Mvc\Dispatcher */
switch ($exception->getCode()) {
case Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
case Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
$di->set('lastException', $exception);
$dispatcher->forward(
array(
'module' => 'web',
'controller' => 'error',
'action' => 'notFound',
)
);
return false;
default:
$di->set('lastException', $exception);
$dispatcher->forward(
array(
'module' => 'web',
'controller' => 'error',
'action' => 'uncaughtException',
)
);
return false;
}
}
);
$dispatcher->setEventsManager($eventsManager);
return $dispatcher;
}
);
}
}
Hopefully this helps!

Categories