Phalcon Routing for Admin controllers - php

I'm a newbie at Phalcon programming, I have admin/backend and frontend controllers
Admin will be served at '/admin/:controller/:action' and frontend will be served at '/:controller/:action'
Admin controllers (KalkuRitel\Controllers\Admin namespace) are located under
app/
controllers/
admin/
and frontend controllers (KalkuRitel\Controllers\Frontend namespace) are located under
app/
controllers/
frontend/
How do I accomplish this?
And how to serve 404 page within admin and frontend controllers with their own layout?

I would recommend to create modules
app/
modules/
admin/
...
frontend/
...
api/
...
register modules:
$application->registerModules(array(
'frontend' => array(
'className' => 'Application\Frontend\Module',
'path' => __DIR__ . '/../modules/frontend/Module.php'
),
'admin' => array(
'className' => 'Application\Admin\Module',
'path' => __DIR__ . '/../modules/admin/Module.php'
),
'api' => array(
'className' => 'Application\Api\Module',
'path' => __DIR__ . '/../modules/api/Module.php'
)
));
define properly Module.php files and than set route somewhat close to this:
use Phalcon\Mvc\Router as Router;
use Phalcon\CLI\Router as CliRouter;
/**
* Registering a router
*/
$di->setShared('router', function () use ($application, $config) {
if($application instanceof Phalcon\CLI\Console) {
return new CliRouter();
}
$router = new Router(false);
$router->setDefaultModule("frontend");
$router->setUriSource(\Phalcon\Mvc\Router::URI_SOURCE_GET_URL);
$router->removeExtraSlashes(TRUE);
foreach($application->getModules() as $key => $module) {
$prefix = $key == 'frontend' ? '' : ('/' . $key);
$router->add($prefix . '/:params', array(
'module' => $key,
'controller' => 'index',
'action' => 'index',
'params' => 1
))->setName($key);
$router->add($prefix . '/:controller/:params', array(
'module' => $key,
'controller' => 1,
'action' => 'index',
'params' => 2
));
$router->add($prefix . '/:controller/:action/:params', array(
'module' => $key,
'controller' => 1,
'action' => 2,
'params' => 3
));
}
return $router;
});
More in manual:
https://docs.phalconphp.com/pl/latest/reference/routing.html and
https://docs.phalconphp.com/pl/latest/reference/applications.html

The best way to accomplish this is to create a multiple module application.
You can learn more about multiple module application setups here:
https://docs.phalconphp.com/en/latest/reference/applications.html#multi-module
Once you have the structure for this in place you can set the routes something like this:
/*
* Dependency Injector
*/
$di = new \Phalcon\Di\FactoryDefault();
/**
* Register a router
*/
$di->setShared('router', function () {
$router = new \Phalcon\Mvc\Router();
$router->setDefaultModule('frontend');
$router->setDefaultNamespace('Multiple\Frontend\Controllers');
/*
* Frontend Routes
*/
$router->add('/:controller/:action', array(
'namespace' => 'Multiple\Frontend\Controllers',
'module' => 'frontend',
'controller' => 1,
'action' => 2
));
/*
* Backend Routes
*/
$backendRoutes = new \Phalcon\Mvc\Router\Group(array(
'namespace' => 'Multiple\Backend\Controllers',
'module' => 'backend'
));
$backendRoutes->setPrefix('/admin');
$backendRoutes->add('/:controller/:action', array(
'controller' => 1,
'action' => 3
));
$router->mount($backendRoutes);
return $router;
});
This is a quick answer and may require tweaking for your individual situation but should give you a good idea of how to accomplish your goal.

Related

Phalcon route doesn't work

My phalcon app has worked fine with standard MVC route convention.
However, I want to handle some variable via URL, then I have a route:
$router = new \Phalcon\Mvc\Router();
$router->add("/timesheet/some/{year:[0-9]+}/{month:[0-9]{2}}/{day:[0-9]{2}}", "Timesheet::some");
$router->add("/timesheet/getreport/{type:[a-z]}/{year:[0-9]+}/{month:[0-9]{2}}/{day:[0-9]{2}}", "Timesheet::getreport");
$router->addPost("/user/auth", "User::auth");
return $router;
The first route (timesheet/some) worked fine, I can access to "year", "month" variable using $year = $this->dispatcher->getParam("year");, however the second route (timesheet/getreport) doesn't work. In this case, $year = $this->dispatcher->getParam("year"); return null.
If I changed to
$router = new \Phalcon\Mvc\Router(false);
$router->add("/:controller/:action", array(
"controller" => 1,
"action" => 2,
));
$router->add("/timesheet/some/{year:[0-9]+}/{month:[0-9]{2}}/{day:[0-9]{2}}", "Timesheet::some");
$router->addPost("/timesheet/getreport/{type:[a-z]}/{year:[0-9]+}/{month:[0-9]{2}}/{day:[0-9]{2}}", "Timesheet::getreport");
$router->addPost("/user/auth", "User::auth");
return $router;
every request will be routed to index/index. My project URL is localhost/fpas, and I already try both route /fpas/timesheet/some and /timesheet/some but it always redirect to index/index. What's wrong with it? (security/auth is commented out, so it's not result from authentication).
From my understand, the default route, $router = new \Phalcon\Mvc\Router(); only allow you to follow MVC convention, while $router = new \Phalcon\Mvc\Router(false); do but you have to specific all routes for every controller/action. Can I keep the convention for most of the action, while have specific rewrite routes for some action. How can I do that?
Thank you very much.
It works for me:
$router = new Phalcon\Mvc\Router();
$router->add("/", array(
'controller' => 'index',
'action' => 'setLanguage',
));
$router->add("/{language:[a-z]{2}}", array(
'controller' => 'index',
'action' => 'index',
'language' => 1
));
this one get's default routing just with language in the beginning
$router->add("/{language:[a-z]{2}}/:controller/:action", array(
'controller' => 2,
'action' => 3,
'language' => 1
));
with default action "index" when it's not in url
$router->add("/{language:[a-z]{2}}/:controller", array(
'controller' => 2,
'action' => 'index',
'language' => 1
));
some other routes
$router->add("/{language:[a-z]{2}}/:controller/:action/:params", array(
'controller' => 2,
'action' => 3,
'language' => 1,
'params' => 4
));
$router->add("/{language:[a-z]{2}}/question/add/{type}", array(
'language' => 1,
'controller' => 'question',
'action' => 'add',
));

MultiLingual Site in Zend Framework 2 - set value of setLocale() from URL param

Below is what I have done so far -
In Application Module - module.config.php -
'service_manager' => array(
[....],
'aliases' => array(
'translator' => 'MvcTranslator',
),
),
'translator' => array(
'locale' => 'en_US',
'translation_file_patterns' => array(
array(
'type' => 'gettext',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s.mo',
),
),
),
In Album Module - module.config.php -
'router' => array(
'routes' => array(
'album' => array(
'type' => 'segment',
'options' => array(
'route' => '[/:lang]/album[/:action][/:id]',
'constraints' => array(
'lang' => '[a-zA-Z]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Album\Controller\Album',
'action' => 'index',
),
),
),
),
),
In Album Module - Module.php -
public function onBootstrap(MvcEvent $e) {
$sm = $e->getApplication()->getServiceManager();
$router = $sm->get('router');
$request = $sm->get('request');
$matchedRoute = $router->match($request);
$params = $matchedRoute->getParams();
if(isset($params['lang']) && $params['lang'] !== '') {
$translator = $e->getApplication()->getServiceManager()->get('translator');
//or
//$translator = $e->getApplication()->getServiceManager()->get('MvcTranslator');
if($params['lang'] == 'en')
$translator->setLocale('en_US');
elseif($params['lang'] == 'fr')
$translator->setLocale('fr_FR');
else
$translator->setLocale('en_US');
}
}
In view -
echo $this->translate('Home');
Note: URL - http://zf2-tutorial.localhost/fr/album, works just fine. The translation is successful.
Query -
$translator->setLocale('en'); seems to be working with ZF1 but not with ZF2.
I need to know whether there is any way to directly set the URL param value like $translator->setLocale($params['lang']); rather then long if-else or switch statements.
Some sites might be in 20 or more languages.
Thanks in advance.
Simple answer: use SlmLocale. I wrote the module to implement locale detection DRY and for different use cases.
Don't try to fit the localization into your routes. This allows you to have translatable routes as you detect the locale before routing. Also, it helps you enormously to change locale with different URIs.
A mistake you made in your process is that you grab the route match from the event during bootstrap. However, bootstrap occurs before routing, so you can't get the route match parameters at that stage.
TL;DR: checkout SlmLocale, it should help you with all your problems.

How router/roites could be injected in application in phalcon framework?

Here, in docs, is written about how to create routes:
http://docs.phalconphp.com/en/latest/reference/routing.html
But i can't find how i can inject them in application.
What i need to do, to make my application use defined routes?
Should i inject router (or how?)
The router can be registered in the DI (in your public/index.php) this way:
$di->set('router', function() {
$router = new \Phalcon\Mvc\Router();
$router->add("/login", array(
'controller' => 'login',
'action' => 'index',
));
$router->add("/products/:action", array(
'controller' => 'products',
'action' => 1,
));
return $router;
});
Also is possible to move the registration of routes to a separate file in your application (i.e app/config/routes.php) this way:
$di->set('router', function(){
require __DIR__.'/../app/config/routes.php';
return $router;
});
Then in the app/config/routes.php file:
<?php
$router = new \Phalcon\Mvc\Router();
$router->add("/login", array(
'controller' => 'login',
'action' => 'index',
));
$router->add("/products/:action", array(
'controller' => 'products',
'action' => 1,
));
return $router;
Examples: https://github.com/phalcon/php-site/blob/master/public/index.php#L33 and https://github.com/phalcon/php-site/blob/master/app/config/routes.php

Lithium Views in Sub-directories

I am working on a website using the Lithium framework for PHP, and I need to have two sub-directories (ie. for admin and blog) with my controllers and views:
-controllers
-admin
HomeController.php
...
-blog
HomeController.php
...
HomeController.php
...
-views
-admin
-home
index.html.php
...
...
-blog
-home
index.html.php
...
...
-layouts
default.html.php
admin.html.php
blog.html.php
So far, I have discovered the way to allow the use of sub-domains in the controller using the Dispach::config() method:
Dispatcher::config(array('rules' => array(
'admin' => array('controller' => 'app\controllers\admin\{:controller}Controller'),
'blog' => array('controller' => 'app\controllers\blog\{:controller}Controller'),
)));
This works when you use the following routing:
$options = array('continue' => true);
Router::connect('/admin', array(
'admin' => true,
'controller' => 'Home'
), $options);
Router::connect('/admin/{:args}', array(
'admin' => true
), $options);
Router::connect('/blog', array(
'blog' => true,
'controller' => 'Home'
), $options);
Router::connect('/blog/{:args}', array(
'blog' => true
), $options);
Now the problem I am having is that I can't figure out how to set it to automatically use the admin/blog template and admin/blog view folders.
You could override default templates paths thanks to Media.
The filter above sets different paths for admin requests (in config/bootstrap/media.php).
Dispatcher::applyFilter('_callable', function($self, $params, $chain) {
$next = $chain->next($self, $params, $chain);
if ($params['request']->admin) {
Media::type('default', null, array(
'view' => 'lithium\template\View',
'paths' => array(
'layout' => '{:library}/views/layouts/{:layout}.{:type}.php',
'template' => '{:library}/views/admin/{:controller}/{:template}.{:type}.php'
)
));
}
return $next;
});

How to create a generic module/controller/action route in Zend Framework 2?

I would like to create a generic module/controller/action route in Zend Framework 2 to be used with ZF2 MVC architecture.
In ZF1 the default route was defined like /[:module][/:controller][/:action] where module would default to default, controller would default to index and action to index.
Now, ZF2 changed the way modules are intended, from simple groups of controllers and views, to real standalone applications, with explicit mapping of controller name to controller class.
Since all controller names must be unique across all modules, I was thinking to name them like modulename-controllername but I would like the URL to look like /modulename/controllername without the need to create specific routes for each module, using something like the old default route for ZF1 described above.
Yes it is very possible, but you will have to do a little work. Use the following config:
'default' => array(
'type' => 'My\Route\Matcher',
'options' => array(
'route' => '/[:module][/:controller[/:action]]',
'constraints' => array(
'module' => '[a-zA-Z][a-zA-Z0-9_-]*',
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
),
'defaults' => array(
'module' => 'default',
'controller' => 'index',
'action' => 'index',
),
),
),
Then you have to write your own My\Route\Matcher to create a Routemap object that the MVC can use. It's not hard, look at the other route matchers already in the framework and you'll get the idea.
If you use the Zend Skeleton Application you have already configured this default controller.
See here https://github.com/zendframework/ZendSkeletonApplication/blob/master/module/Application/config/module.config.php
To have a general/standard routing system for a zf2 module, this is my solution for just one controller "module\controller\index" ( default controller ) :
'router' => array(
'routes' => array(
'default' => array(
'type' => 'Literal',
'options' => array(
'route' => '/', // <======== this is take the first step to our module "profil"
'defaults' => array(
'module' => 'profil',
'controller' => 'profil\Controller\Index',
'action' => 'index',
),
),
),
'profil' => array(
'type' => 'Segment',
'options' => array(
'route' => '/[profil][/:action]', // <======== this is take the next steps of the module "profil"
'constraints' => array(
'module' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
),
'defaults' => array( // force the default one
'module' => 'profil',
'controller' => 'profil\Controller\Index',
'action' => 'index',
),
),
),
),
),
then in our controller "profil\Controller\Index" we have three actions "index" "home" "signout" :
public function indexAction()
{
if ($this->identity()) {
return $this->redirect()->toRoute('profil',array('action'=>'home'));
} else {
// ......
$authResult = $authService->authenticate();
if ($authResult->isValid()) {
//......
return $this->redirect()->toRoute('profil',array('action'=>'home'));
} else {
// ......
}
} else {
$messages = $form->getMessages();
}
}
return new ViewModel();
}
}
public function homeAction()
{
if (!$this->identity()) {
return $this->redirect()->toRoute('profil',array('action'=>'signout'));
}
}
public function signoutAction()
{
if ($this->identity()) {
$authService = $this->getServiceLocator()->get('Zend\Authentication\AuthenticationService');
$authService->clearIdentity();
}
$this->redirect()->toRoute('profil');
}
and thank you anyway :)

Categories