How to connect to the subdomains Phalcon:
city1.site.com
city2.site.com
city3.site.com
...
cityN.site.com
city - in the database
I am trying to do so
$router->add('{subdomain:\w+}.{domain:.+}', array(
'controller' => 'category',
'action' => 'categorySearch'
)
);
but does not work.
Phalcon's router doesn't match subdomains. You have to match $_SERVER['SERVER_NAME'] with a regular expression to create corresponding routers.
<?php
$di = new \Phalcon\Di\FactoryDefault();
$di->setShared('router', function() {
// Match subdomain with regular expression
if(preg_match("/^(\\w+)\\.site\\.com$/", $_SERVER['SERVER_NAME'], $matches) === 1) {
$subdomain = $matches[1];
}
// Create a router without default routes
$router = new \Phalcon\Mvc\Router(false);
if (isset($subdomain)) {
// Create routes for subdomains
$router->add('/category', array(
'controller' => 'category',
'action' => 'categorySearch'
));
} else {
// Create routes for main domain
}
return $router;
});
// Retrieve corresponding router at runtime
$di->getShared('router')->handle();
may be this can help you problem
$di['router'] = function()
{
$router = new Phalcon\Mvc\Router(false);
switch ($_SERVER['HTTP_HOST'])
{
case 'm.domain.com':
$router->add('/m/xxx/yyy', array(
'controller' => 'xxx',
'action' => 'yyy'
));
//...
break;
default:
$router->add('/xxx/yyy', array(
'controller' => 'xxx',
'action' => 'yyy'
));
break;
}
return $router;
};
Related
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.
I'm trying to set up routing in Phalcon, following this URL structure:
www.example.com/language/register/
This is my routes.php:
use Phalcon\Mvc\Router;
$router = new Router();
$router->add(
'/[a-z]{2}/register/',
array(
'controller' => 'index',
'action' => 'register',
'lang' => 1
)
);
return $router;
And this is my IndexController:
class IndexController extends ControllerBase
{
public $lang;
public function indexAction()
{
}
public function registerAction($lang)
{
$lang = $this->dispatcher->getParam('lang');
echo "Register ($lang)"; //test
}
}
Therefore, if I was to visit www.example.com/fr/register/ it would take me to the index controller, the register action, with the lang parameter fr.
However, it is not displaying the $lang variable on the page.
I can see in the Phalcon documentation that you cannot use the /:params placeholder anywhere but at the end of the route (URL), but this is a regular expression being named within the router?
You got it right, you just forgot your brackets around [a-z]{2}
$router->add(
'/([a-z]{2})/register/', // added brackets here
array(
'controller' => 'index',
'action' => 'register',
'lang' => 1
)
);
Now you can access your lang via
$lang = $this->dispatcher->getParam('lang');
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');
}
I'm developing a multilanguage Zend application with two modules (admin and public), I wanna pass the language code in the url so, in my bootstrap I have:
protected function _initAutoload() {
$this->bootstrap('frontController');
$this->_front = $this->getResource('frontController');
$autoLoader = new Zend_Loader_Autoloader_Resource(array(
'basePath' => APPLICATION_PATH,
'namespace' => '',
'resourceTypes' => array(
'form' => array(
'path' => 'admin/forms/',
'namespace' => 'Admin_Form_',
),
'model' => array(
'path' => 'models/',
'namespace' => 'Model_'
)
)
));
$autoLoader_ = new Zend_Application_Module_Autoloader(array(
'basePath' => APPLICATION_PATH . '/public/',
'namespace' => 'Public_',
'resourceTypes' => array(
'forms' => array(
'path' => 'forms/',
'namespace' => 'Public_Form_'
)
)
));
return $autoLoader;
}
protected function _initConfig() {
$config = new Zend_Config_Xml(BASE_PATH . '/config.xml', APPLICATION_ENV);
$registry = Zend_Registry::getInstance();
$registry->set('config', $config);
return $config;
}
protected function _initDb() {
$this->bootstrap('config');
$config = $this->getResource('config');
$db = Zend_Db::factory($config->database->adapter, $config->database);
$db->setFetchMode(Zend_Db::FETCH_OBJ);
$db->query("SET NAMES 'utf8';");
$db->query("SET CHARACTER SET 'utf8';");
Zend_Db_Table::setDefaultAdapter($db);
return $db;
}
protected function _initRoutes() {
$router = $this->_front->getRouter();
$router->removeDefaultRoutes();
$language = new Zend_Controller_Router_Route(':language', array('language' => 'es'));
$module = new Zend_Controller_Router_Route_Module(
array(
'module' => 'public',
'controller' => 'index',
'action' => 'index'
),
$this->_front->getDispatcher(),
$this->_front->getRequest()
);
$module->isAbstract(true);
$default = new Zend_Controller_Router_Route_Chain();
$default->chain($language);
$default->chain($module);
$router->addRoute('default', $default);
}
http://domain.com/es/admin/ => works
http://domain.com/admin/ => does not work
http://domain.com/es/ => works
http://domain.com/ => does not work
The thing is that I wanna it to work even when I do not specify the language. how can I do it? how can I extract the language code (en) for use it in _initLocale?
Thanks in advance
Here is my bootstrap init for routing. It contains all solutions for working with multilanguage routes and haveing modules and url halper working:
public function _initRoutes() {
$this->bootstrap('FrontController');
$this->_frontController = $this->getResource('FrontController');
$router = $this->_frontController->getRouter();
if (isset($_GET['lang'])) {
$lang = $_GET['lang'];
} else {
// auto recognition of language
$locale = new Zend_Locale();
$lang = $locale->getLanguage();
}
$langRoute = new Zend_Controller_Router_Route(
':lang/',
array(
'lang' => $lang
)
);
$defaultRoute = new Zend_Controller_Router_Route(
':controller/:action/*',
array(
'module' => 'default',
'controller' => 'index',
'action' => 'index'
)
);
$defaultRoute = $langRoute->chain($defaultRoute);
$adminRoute = new Zend_Controller_Router_Route(
'admin/:controller/:action/*',
array(
'module' => 'admin',
'controller' => 'index',
'action' => 'index'
)
);
$router->addRoute('langRoute', $langRoute);
$router->addRoute('defaultRoute', $defaultRoute);
$router->addRoute('adminRoute', $adminRoute);
}
You need to chain all the routes from the language route. See here:
http://robertbasic.com/blog/chaining-routes-in-zend-framework/
Two modules:
$defaultRoute = new Zend_Controller_Router_Route(
':module/:controller/:action',
This is a plugin that is universal and allow us to create your own routes without remembering about language. It's also setting Zend_Translator. This is a base class, to improve speed I'm suggesting to use Zend_Cache because code below impacts in code efficiency (I would say it's required if you have +100 routes).
<?php
class PsScripts_Controller_Plugin_Lang extends Zend_Controller_Plugin_Abstract {
private function initTranslator($locale){
$translate = new Zend_Translate(array('adapter' => 'tmx',
'content' => APPLICATION_PATH . '/configs/translations.tmx',
'locale' => $locale));
Zend_Registry::set('Zend_Translate', $translate);
}
public function routeStartup(\Zend_Controller_Request_Abstract $request) {
$locale = new Zend_Locale('pl_PL'); //default locale
if (preg_match('/\/([a-z]{2})([\/].*)/', $request->getRequestUri(),$matches)){ //if locale is found in request
$lang = $matches[1]; //obtain locale
/* #var $locale Zend_Locale */
switch ($lang){
case 'en':
$locale->setLocale('en_GB');
break;
}
Zend_Registry::set('Zend_Locale',$locale);
$this->initTranslator($locale);
$router = Zend_Controller_Front::getInstance()->getRouter();
/* #var $router Zend_Controller_Router_Rewrite */
$langRoute = new Zend_Controller_Router_Route(
':lang',
array(
'lang' => $lang
),
array(
'lang' => '[a-z]{0,2}'
)
);
$routes = $router->getRoutes();
foreach ($routes as $name=>$route){
if ($name != 'default'){
/* #var $route Zend_Controller_Router_Route */
$router->removeRoute($name);
/* #var $lang Zend_Controller_Router_Route */
$chain = new Zend_Controller_Router_Route_Chain();
$chain->chain($langRoute)->chain($route);
$router->addRoute($name,$chain);
}
}
$router->route($request);
} else {
$this->initTranslator($locale);
}
parent::routeStartup($request);
}
}
I'm trying to match URLs without a trailing slash to one router and want those with the trailing slash behave the normal way. I've tried:
$route = new Zend_Controller_Router_Route(
':redirectid',
array(
'redirectid' => false,
'controller' => 'redirect',
'action' => 'redirect'
),
array('redirectid' => '[a-z0-9]*')
);
and
$route = new Zend_Controller_Router_Route_Regex(
'([a-z0-9]*)',
array(
'controller' => 'redirect',
'action' => 'redirect'
)
);
and both behave exactly how I want for urls without trailing slash, yet they still match for urls with a trailing slash, too. Is there any way around this?
DISCLAIMER:
I would highly suggest against making http://somesite.com/page and http://somesite.com/page/ being different pages- it will become confusing for you and for your visitors.
If you're truly dedicated to this plan
you can create your own router you can that handles this by creating your own match() and assemble() functions that don't trim() the path based on trailing slashes.
class My_Route_Redirector implements Zend_Controller_Router_Route_Interface {
protected $_defaults;
public static function getInstance(Zend_Config $config) {
$defs = ($config->defaults instanceof Zend_Config) ? $config->defaults->toArray() : array();
return new self($defs);
}
public function __construct($defaults=array()) {
$this->_defaults = $defaults;
}
public function match($path, $partial = false) {
if (preg_match("#^/?([a-z0-9]+)$#i", $path, $matches)) {
// this is just an idea but what about if you had this test
// $matches[1] versus the database of redirectors? and only return true
// when it found a valid redirector?
return array('redirectid' => $matches[1]) + $this->_defaults;
} else {
return false;
}
}
public function assemble($data = array(), $reset = false, $encode = false, $partial = false)
{
if (!isset($data['redirectid'])) return '';
return $data['redirectid'];
}
}
That was air coded so it may have a bug or two to work out - it should work like this:
$route = new My_Route_Redirector(
array(
'controller' => 'redirect',
'action' => 'redirect'
)
);