I'm looking for a one line route to route dashed controller and method names to the actual underscored controller and method names.
For example the URL
/controller-name/method-name-which-is-long/
would route to
/controller_name/method_name_which_is_long/
see: http://codeigniter.com/forums/viewreply/696690/ which gave me the idea to ask :)
That is exactly my requirement too and I was using routes like
$route['logued/presse-access'] = "logued/presse_access";
In my previous project I needed to create 300-400 routing rules, most of them are due to dash to underscore conversion.
For my next project I eagerly want to avoid it. I have done some quick hack and tested it, though have not used in any live server, its working for me. Do the following..
Make sure the subclass_prefix is as follows in your system/application/config/config.php
$config['subclass_prefix'] = 'MY_';
Then upload a file named MY_Router.php in system/application/libraries directory.
<?php
class MY_Router extends CI_Router {
function set_class($class)
{
//$this->class = $class;
$this->class = str_replace('-', '_', $class);
//echo 'class:'.$this->class;
}
function set_method($method)
{
// $this->method = $method;
$this->method = str_replace('-', '_', $method);
}
function _validate_request($segments)
{
// Does the requested controller exist in the root folder?
if (file_exists(APPPATH.'controllers/'.str_replace('-', '_', $segments[0]).EXT))
{
return $segments;
}
// Is the controller in a sub-folder?
if (is_dir(APPPATH.'controllers/'.$segments[0]))
{
// Set the directory and remove it from the segment array
$this->set_directory($segments[0]);
$segments = array_slice($segments, 1);
if (count($segments) > 0)
{
// Does the requested controller exist in the sub-folder?
if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().str_replace('-', '_', $segments[0]).EXT))
{
show_404($this->fetch_directory().$segments[0]);
}
}
else
{
$this->set_class($this->default_controller);
$this->set_method('index');
// Does the default controller exist in the sub-folder?
if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.EXT))
{
$this->directory = '';
return array();
}
}
return $segments;
}
// Can't find the requested controller...
show_404($segments[0]);
}
}
Now you can freely use url like http://example.com/logued/presse-access and it will call the proper controller and function by automatically converting dash to underscore.
Edit:
Here is our Codeigniter 2 solution which overrides the new CI_Router functions:
<?php
class MY_Router extends CI_Router {
function set_class($class)
{
$this->class = str_replace('-', '_', $class);
}
function set_method($method)
{
$this->method = str_replace('-', '_', $method);
}
function set_directory($dir) {
$this->directory = $dir.'/';
}
function _validate_request($segments)
{
if (count($segments) == 0)
{
return $segments;
}
// Does the requested controller exist in the root folder?
if (file_exists(APPPATH.'controllers/'.str_replace('-', '_', $segments[0]).'.php'))
{
return $segments;
}
// Is the controller in a sub-folder?
if (is_dir(APPPATH.'controllers/'.$segments[0]))
{
// Set the directory and remove it from the segment array
$this->set_directory($segments[0]);
$segments = array_slice($segments, 1);
while(count($segments) > 0 && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]))
{
// Set the directory and remove it from the segment array
$this->set_directory($this->directory . $segments[0]);
$segments = array_slice($segments, 1);
}
if (count($segments) > 0)
{
// Does the requested controller exist in the sub-folder?
if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().str_replace('-', '_', $segments[0]).'.php'))
{
if ( ! empty($this->routes['404_override']))
{
$x = explode('/', $this->routes['404_override']);
$this->set_directory('');
$this->set_class($x[0]);
$this->set_method(isset($x[1]) ? $x[1] : 'index');
return $x;
}
else
{
show_404($this->fetch_directory().$segments[0]);
}
}
}
else
{
// Is the method being specified in the route?
if (strpos($this->default_controller, '/') !== FALSE)
{
$x = explode('/', $this->default_controller);
$this->set_class($x[0]);
$this->set_method($x[1]);
}
else
{
$this->set_class($this->default_controller);
$this->set_method('index');
}
// Does the default controller exist in the sub-folder?
if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.'.php'))
{
$this->directory = '';
return array();
}
}
return $segments;
}
// If we've gotten this far it means that the URI does not correlate to a valid
// controller class. We will now see if there is an override
if ( ! empty($this->routes['404_override']))
{
$x = explode('/', $this->routes['404_override']);
$this->set_class($x[0]);
$this->set_method(isset($x[1]) ? $x[1] : 'index');
return $x;
}
// Nothing else to do at this point but show a 404
show_404($segments[0]);
}
}
Now one has to place this file like application/core/MY_Router.php and make sure he has subclass_prefix is defined as $config['subclass_prefix'] = 'MY_'; in application/config/config.php
Few extra lines of code has been added in method _validate_request():
while(count($segments) > 0 && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]))
{
// Set the directory and remove it from the segment array
$this->set_directory($this->directory . $segments[0]);
$segments = array_slice($segments, 1);
}
It is used so that one can make use of multi-level subdirectory inside controllers directory, whereas normally we can use single level subdirectory inside controllers folder and can call it in url. One can remove this code if it not necessary but it has no harm on the normal flow.
Just coming back to this question after upgrading to CodeIgniter 2. Here is a solution which is more robust than the accepted answer because it will survive CodeIgniter core updates.
<?php
class MY_Router extends CI_Router
{
public function set_class($class)
{
parent::set_class($this->_repl($class));
}
public function set_method($method)
{
parent::set_method($this->_repl($method));
}
public function _validate_request($segments)
{
if (isset($segments[0]))
$segments[0] = $this->_repl($segments[0]);
if (isset($segments[1]))
$segments[1] = $this->_repl($segments[1]);
return parent::_validate_request($segments);
}
private function _repl($s)
{
return str_replace('-', '_', $s);
}
}
It should be saved as application/core/MY_Router.php. Now, you can have Controller and Method names with underscores in them such as Abc_Def (in file abc_def.php) and refer to them with the URL /abc-def.
Actually this is native now in Codeigniter 3
Just do this in routes file
$route['translate_uri_dashes'] = TRUE;
And it will do it for controllers and methods automatically .
Please vote this answer up , as it's most recent
<?php
class MY_Router extends CI_Router
{
function _set_request($segments = array()) {
parent::_set_request(str_replace('-', '_', $segments));
}
}
?>
Put this file MY_Router.php inside /application/libraries (CI1) or /application/core (CI2)
Remember that this will effect all segments, not only module, controller and method.
Alternative to this extend is to add each segment to router.php
$route['this-is-a-module-or-controler'] = 'this_is_a_module_or_controller';
As you can see the extend method would be easier to use. You can choose to make the function also to handle only the first two or three segments so that the other segments are not affected with the _ replacement.
This is an old question, but I'd like to post that e-mike had a great solution to this problem, and a lot simpler.
<?php
public function _set_request($segments){
// Fix only the first 2 segments
for($i = 0; $i < 2; ++$i){
if(isset($segments[$i])){
$segments[$i] = str_replace('-', '_', $segments[$i]);
}
}
// Run the original _set_request method now, giving it our newly replaced segments
parent::_set_request($segments);
}
?>
Hope this helps anyone else with this problem.
I believe what you are looking for is a either a pre-system or else a pre-controller hook that will take the requested URI and update it.
Overriding the Router class is a nice approach, there is also a way of replacing - with _ by registering a "pre-system" hook.
First create the hook by adding these lines to your ‘config/hooks.php’ file:
$hook['pre_system'] = array(
'class' => '',
'function' => 'prettyurls',
'filename' => 'myhooks.php',
'filepath' => 'hooks',
'params' => array()
);
Now create a ‘myhooks.php’ file within the ‘application/hooks’ folder and add this function (don’t forget to open a PHP tag if this is the first hook):
<?php
function prettyurls() {
if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '') {
$newkey = str_replace('-','_',key($_GET));
$_GET[$newkey] = $_GET[key($_GET)];
unset($_GET[key($_GET)]);
}
if (isset($_SERVER['PATH_INFO'])) $_SERVER['PATH_INFO'] = str_replace('-','_',$_SERVER['PATH_INFO']);
if (isset($_SERVER['QUERY_STRING'])) $_SERVER['QUERY_STRING'] = str_replace('-','_',$_SERVER['QUERY_STRING']);
if (isset($_SERVER['ORIG_PATH_INFO'])) $_SERVER['ORIG_PATH_INFO'] = str_replace('-','_',$_SERVER['ORIG_PATH_INFO']);
if (isset($_SERVER['REQUEST_URI'])) $_SERVER['REQUEST_URI'] = str_replace('-','_',$_SERVER['REQUEST_URI']);
}
You will probably need to edit your ‘config/config.php’ file to enable hooks (around line 91 for me):
$config['enable_hooks'] = TRUE;
This answer is ripped from http://codeigniter.com/forums/viewthread/124396/#644012
I'm not sure if you could do that with a route...
However, somewhere in the Codeigniter core libraries (possibly Router or URI) will be something that converts the underscored uris into a camelcase class name.
I had a quick look and couldn't find it, but if you do, just copy that library to your application/libraries folder, and modify it there.
You can use this _remap() method to handle such behavior. Place this method in your controllers or create a core controller and place it in.
public function _remap($method, $params = array()){
if(method_exists($this, $method)){
return call_user_func_array(array($this, $method), $params);
}else{
$method = str_replace("-", "_", $method);
if(method_exists($this, $method)){
return call_user_func_array(array($this, $method), $params);
}
}
show_404();
}
Related
How to run controller from the sub folder in codeigniter.
I have tried but i get the following error bellow.
You can do this by following below steps:
Stpep 1) create sub-folder "superadmin" in controllers folder like below:
controllers/superadmin
Stpep 2) create controller controllers/superadmin/loginvalueget.php in sub-folder "superadmin" like below:
<?php
class Loginvalueget extends CI_Controller {
function index()
{
die('hello world');
}
}
?>
Step 3) change default controller route in application/config/routes.php like below:
$route['default_controller'] = 'superadmin/loginvalueget';
Step 4) replace _set_default_controller() with below method in system/core/Router.php:
protected function _set_default_controller()
{
if (empty($this->default_controller))
{
show_error('Unable to determine what should be displayed. A default route has not been specified in the routing file.');
}
// Is the method being specified?
$x = explode('/', $this->default_controller);
$dir = APPPATH.'controllers';
$dir_arr = array();
foreach($x as $key => $val){
if(!is_dir($dir.'/'.$val)){
if(file_exists($dir.'/'.ucfirst($val).'.php')){
$class = $val;
if(array_key_exists(($key+1), $x)){
$method = $x[$key+1];
}else{
$method = 'index';
}
}else{
show_error('Not found specified default controller : '. $this->default_controller);
}
break;
}
$dir_arr[] = $val;
$dir = $dir.'/'.$val;
}
//set directory
$this->set_directory(implode('/', $dir_arr));
$this->set_class($class);
$this->set_method($method);
// Assign routed segments, index starting from 1
$this->uri->rsegments = array(
1 => $class,
2 => $method
);
log_message('debug', 'No URI present. Default controller set.');
}
As you can see yourself in the attachment, you haven't set the route. You can do this by setting route path in "application>config>routes.php".
I am using CodeIgniter2. I am using routing to route url segments to a controller and method.
This seems to work. My pages load as expected i.e. the url goes to the correct method to get the page info from the database, come back and display the correct page. My routes.php relevant code is:
$route['default_controller'] = "content";
$route['en/(:num)/(:any)'] = "content/en/$1";
$route['de/(:num)/(:any)'] = "content/de/$1";
$route['es/(:num)/(:any)'] = "content/es/$1";
$route['it/(:num)/(:any)'] = "content/it/$1";
$route['ar/(:num)/(:any)'] = "content/ar/$1";
$route['404_override'] = '';
HOWEVER instead of that displayed page showing a http status of 200, it shows a http status of 404 ... I have no idea why.
I suspect it is to do with a MY_Router.php file i have to give a custom error page but i can't work out what's going on.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Router extends CI_Router {
var $error_controller = 'error';
var $error_method_404 = 'error_404';
function My_Router()
{
parent::CI_Router();
}
// this is just the same method as in Router.php, with show_404() replaced by $this->error_404();
function _validate_request($segments)
{
// Does the requested controller exist in the root folder?
if (file_exists(APPPATH.'controllers/'.$segments[0].EXT))
{
return $segments;
}
// Is the controller in a sub-folder?
if (is_dir(APPPATH.'controllers/'.$segments[0]))
{
// Set the directory and remove it from the segment array
$this->set_directory($segments[0]);
$segments = array_slice($segments, 1);
if (count($segments) > 0)
{
// Does the requested controller exist in the sub-folder?
if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].EXT))
{
return $this->error_404();
}
}
else
{
$this->set_class($this->default_controller);
$this->set_method('index');
// Does the default controller exist in the sub-folder?
if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.EXT))
{
$this->directory = '';
return array();
}
}
return $segments;
}
// Can't find the requested controller...
return $this->error_404();
}
function error_404()
{
$this->directory = "";
$segments = array();
$segments[] = $this->error_controller;
$segments[] = $this->error_method_404;
return $segments;
}
function fetch_class()
{
// if method doesn't exist in class, change
// class to error and method to error_404
$this->check_method();
return $this->class;
}
function check_method()
{
$ignore_remap = true;
$class = $this->class;
if (class_exists($class))
{
// methods for this class
$class_methods = array_map('strtolower', get_class_methods($class));
// ignore controllers using _remap()
if($ignore_remap && in_array('_remap', $class_methods))
{
return;
}
if (! in_array(strtolower($this->method), $class_methods))
{
$this->directory = "";
$this->class = $this->error_controller;
$this->method = $this->error_method_404;
include(APPPATH.'controllers/'.$this->fetch_directory().$this->error_controller.EXT);
}
}
}
function show_404()
{
include(APPPATH.'controllers/'.$this->fetch_directory().$this->error_controller.EXT);
call_user_func(array($this->error_controller, $this->error_method_404));
}
}
/* End of file MY_Router.php */
/* Location: ./system/application/libraries/MY_Router.php */
Solved - the wordpress blog integrated in the site was setting the 404 status for all non wordpress pages i.e. codeigniter pages
index.php of CI had the following code which needed to be commented out
/*
*---------------------------------------------------------------
* WORDPRESS INTEGRATION
*---------------------------------------------------------------
* The ci_site_url function helps to avoid collision between WP & CI.
*/
//header("HTTP/1.0 200 OK");
define('WP_USE_THEMES', false);
require_once './blog/wp-blog-header.php';
add_filter('site_url', 'ci_site_url', 1);
function ci_site_url()
{
include(APPPATH.'/config/config.php');
return $config['base_url'];
}
In .NET using AttributeRouting we can add the Route for each Action Method. Something like below
[HttpGet, Route("Create-Project")]
public ActionResult CreateProject()
{
return View();
}
So, in the above code...line 1 indicates that we can mention the route for each Action Method. So url will become something like below..
http://domainname/Create-Project
Question
Is it feasible in PHP MVC CI ? and right now I will have to write the code in route.php in config folder.
In routes.php you can define it
Syntax
$route['short_url_name'] = 'complete/path/for/url';
Example
$route['Contact-Us'] = 'home/contact';
$route['Blog'] = 'home/blog';
$route['Our-Work'] = 'home/work';
so URL look like
http://domain.com/Contact-Us
http://domain.com/Blog
http://domain.com/Our-Work
Codeigniter.com examples
In Application/config/router.php you can add some config like that:
$route['Create-Project'] = 'home/welcome';
So, each time you visit http://domain.com/Create-Project, it will redirect you to http://domain.com/home/welcome
If you are looking for database driven dynamic routes you can rty this, else you can follow what #abdulla has suggested.
Table structure for Dynamic routes(Table name example: routes).
id | slug |route
---------------------------------------------
1 |Pankaj |controller/method/id of user
2 |Abdulla |controller/method/id of user
3 |Niranjan |controller/method/id of user
In controller
$this->load->helper('text');
$slug = $this->input->post("username");// example if you want the username in url as route.
$slug = url_title(convert_accented_characters($slug), 'dash', TRUE);
$slug = $this->Routes_model->validate_slug($slug);
$route['slug'] = $slug;
$route_id = $this->Routes_model->save($route);
$route_id will have the id of route in routes table, insert the route id and slug in users table(For which table you want dynamic routes)
This is the code for route model
<?php
class Routes_Model extends CI_Model {
var $file_name;
function __construct()
{
parent::__construct();
$this->file_name = APPPATH.'config/routes'.EXT;
}
function check_slug($slug, $id=false)
{
if($id)
{
$this->db->where('id !=', $id);
}
$this->db->where('slug', $slug);
return (bool) $this->db->count_all_results('routes');
}
function validate_slug($slug, $id=false, $count=false)
{
if(is_numeric($slug))
{
$slug = '';
$characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
for ($i = 0; $i < 3; $i++)
{
$slug .= $characters[rand(0, strlen($characters) - 1)];
}
}
if($this->check_slug($slug.$count, $id))
{
if(!$count)
{
$count = 1;
}
else
{
$count++;
}
return $this->validate_slug($slug, $id, $count);
}
else
{
return $slug.$count;
}
}
function save($route)
{
// print_r($route);exit;
if(!empty($route['id']))
{
$this->db->where('id', $route['id']);
$this->db->update('routes', $route);
return $route['id'];
}
else
{
$this->db->insert('routes', $route);
return $this->db->insert_id();
}
}
}
?>
One condition i have checked is if the username is a number, i ll generate a length of 3 characters in validate_slug().
Now add the following code in core folder by file name MY_Router.php
<?php
class My_Router extends CI_Router
{
function __construct()
{
parent::__construct();
}
// this is here to add an additional layer to the routing system.
//If a route isn't found in the routes config file. then it will scan the database for a matching route.
function _parse_routes()
{
$segments = $this->uri->segments;
$segments = array_splice($segments, -2, 2);
// Turn the segment array into a URI string
$uri = implode('/', $segments);
// Is there a literal match? If so we're done
if (isset($this->routes[$uri]))
{
return $this->_set_request(explode('/', $this->routes[$uri]));
}
// Loop through the route array looking for wild-cards
foreach ($this->routes as $key => $val)
{
// Convert wild-cards to RegEx
$key = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $key));
// Does the RegEx match?
if (preg_match('#^'.$key.'$#', $uri))
{
// Do we have a back-reference?
if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE)
{
$val = preg_replace('#^'.$key.'$#', $val, $uri);
}
return $this->_set_request(explode('/', $val));
}
}
//look through the database for a route that matches and apply the same logic as above :-)
//load the database connection information
require_once BASEPATH.'database/DB'.EXT;
if(count($segments) == 1)
{
$row = $this->_get_db_route($segments[0]);
if(!empty($row))
{
return $this->_set_request(explode('/', $row['route']));
}
}
else
{
$segments = array_reverse($segments);
//start with the end just to make sure we're not a multi-tiered category or category/product combo before moving to the second segment
//we could stop people from naming products or categories after numbers, but that would be limiting their use.
$row = $this->_get_db_route($segments[0]);
//set a pagination flag. If this is set true in the next if statement we'll know that the first row is segment is possibly a page number
$page_flag = false;
if($row)
{
return $this->_set_request(explode('/', $row['route']));
}
else
{
//this is the second go
$row = $this->_get_db_route($segments[1]);
$page_flag = true;
}
//we have a hit, continue down the path!
if($row)
{
if(!$page_flag)
{
return $this->_set_request(explode('/', $row['route']));
}
else
{
$key = $row['slug'].'/([0-9]+)';
//pages can only be numerical. This could end in a mighty big error!!!!
if (preg_match('#^'.$key.'$#', $uri))
{
$row['route'] = preg_replace('#^'.$key.'$#', $row['route'],$uri);
return $this->_set_request(explode('/', $row['route']));
}
}
}
}
// If we got this far it means we didn't encounter a
// matching route so we'll set the site default route
$this->_set_request($this->uri->segments);
}
function _get_db_route($slug)
{
return DB()->where('slug',$slug)->get('routes')->row_array();
}
}
The above file is very important. As codeigniter first runs files inside core folder, this will create the slug you want. No need to add any routes in routes.php file.
Now while displaying users, you shud echo the slug in your users table.
Lets say controller name is users, method name is edit_user.
your controller code will look like this,
function edit_user($id){
// fetch the data related to $id, and load the view here
}
In url if it sees Niranjan, it will automatically take the route from routes table and will enter your function, edit_user().
Yes Method Looks Lengthy, but works perfect, i use this in all my ecommerce sites.
I'm making a toolkit for php applications. I've a made a routing system based on some conventions, it works well but i would like to learn how to make mod_rewrite rules or any other stuff to finally make the url good to see and good for seo.
The route system starts from a config file that set the app and url roots.
$app_root = $_SERVER["DOCUMENT_ROOT"].dirname($_SERVER["PHP_SELF"])."/";
$app_url = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']).'/';
define("APP_URL",$app_url);
define("APP_ROOT",$app_root);
The route always get start from index.php, wich makes instances of controllers#actions from GET parameters controllers=?&action=?
This is the index.php
<?php
include_once 'controller/Frontend.php';
require 'libraries/Router.php';
$params=array();
if(isset($_GET['controller'])&&isset($_GET['action'])){
$c = $_GET['controller'];
$a = $_GET['action'];
// add all query string additional params to method signature i.e. &id=x&category=y
$queryParams = array_keys($_GET);
$queryValues = array_values($_GET);
for ($i=2;$i<count($queryParams);$i++) {
$params[$queryParams[$i]] = $queryValues[$i];
}
if ($_POST) {
// add all query string additional params to method signature i.e. &id=x&category=y
$queryParams = array_keys($_POST);
$queryValues = array_values($_POST);
for ($i=0;$i<count($_POST);$i++) {
$params[$queryParams[$i]] = $queryValues[$i];
}
}
include_once APP_ROOT."/controller/$c.php";
$controller = new $c();
$controller->$a($params);
} else {
//attiva controller predefinito
$controller = new Frontend();
$controller->index();
}
This allow to select what controller and what action the router must call.
The router function here get the APP URL from settings.php in the root. You give im the two controllers#action params as string and it make the URL like so:
index.php?controller=X&action=Y&[params...]
<?php
require './settings.php';
function router($controller,$action,$query_data="") {
$param = is_array($query_data) ? http_build_query($query_data) : "$query_data";
$url = APP_URL."index.php?controller=$controller&action=$action&$param";
return $url;
}
function relativeRouter ($controller,$action,$query_data=""){
$param = is_array($query_data) ? http_build_query($query_data) : "$query_data";
$url = "index.php?controller=$controller&action=$action&$param";
return $url;
}
function redirectToOriginalUrl() {
$url = $_SERVER['HTTP_REQUEST_URI'];
header("location: $url");
}
function switchAction ($controller, $action) {
$r = router($controller, $action);
header("location:$r", true, 302);
}
In templates file i call router('controller,'action') to retrive url's to actions and also pass GET/POST data (collected from index.php that put's them into the method signature as array).
Don't blame me for using global POST/GET without filtering i'm still developing the thing, security things will be made after ;)
What i would like to ask if someone could share some thoughts on how to make pretty urls like site/page/action....
For example www.site.com/blog/post?id=1
(Actually the N params in the router function ($query_data) works this way, you pass array['id' => '1'] and you get ?id=1)
What are best strategies to make good urls?
Thank you so much, still learning PHP.
If there are best way to do such things just give your feedback.
I found myself an answer to the question, i post here maybe it's useful.
I've added a .htaccess file in the root:
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
This will return each request to the root/index.php file.
Index file collect routes from the HTTP request, check if the route exist in the "routes.json" file.
URL are written in this way:
site.com/controller/action. GET params are written as follows
site.com/controller/action/[params]/[value]...... This output for example site.com/blog/post/id/1
That should be also fine for REST.
Here the index.php
<?php
require 'controller/Frontend.php';
require 'Class/Router.php';
//require 'libraries/Router.php';
/*
* ** ROUTING SETTINGS **
*/
$app_root = $_SERVER["DOCUMENT_ROOT"].dirname($_SERVER["PHP_SELF"])."/";
$app_url = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']).'/';
define("APP_URL",$app_url);
define("APP_ROOT",$app_root);
$basepath = implode('/', array_slice(explode('/', $_SERVER['SCRIPT_NAME']), 0, -1));
$uri = substr($_SERVER['REQUEST_URI'], strlen($basepath));
//echo $uri;
if ($uri == "/") {
$frontend = new Frontend();
$frontend->index();
} else {
$root = ltrim ($uri, '/');
//$paths = explode("/", $uri);
$paths = parse_url($root, PHP_URL_PATH);
$route = explode("/",$paths);
$request = new \PlayPhp\Classes\Request();
// controller
$c = $route[0];
// action
$a = $route[1];
$reverse = Router::inverseRoute($c,$a);
$rest = $_SERVER['REQUEST_METHOD'];
switch ($rest) {
case 'PUT':
//rest_put($request);
break;
case 'POST':
if (Router::checkRoutes($reverse, "POST")) {
foreach ($_POST as $name => $value) {
$request->setPost($name,$value);
}
break;
} else {
Router::notFound($reverse,"POST");
}
case 'GET':
if (Router::checkRoutes($reverse, "GET")) {
for ($i = 2; $i < count($route); $i++) {
$request->setGet($route[$i], $route[++$i]);
}
break;
} else {
Router::notFound($reverse,"GET");
}
break;
case 'HEAD':
//rest_head($request);
break;
case 'DELETE':
//rest_delete($request);
break;
case 'OPTIONS':
//rest_options($request);
break;
default:
//rest_error($request);
break;
}
include_once APP_ROOT.'controller/'.$c.'.php';
$controller = new $c();
$controller->$a($request);
}
The Router class:
<?php
include 'config/app.php';
/*
* Copyright (C) 2015 yuri.blanc
*/
require 'Class/http/Request.php';
class Router {
protected static $routes;
private function __construct() {
Router::$routes = json_decode(file_get_contents(APP_ROOT.'config/routes.json'));
}
public static function getInstance(){
if (Router::$routes==null) {
new Router();
}
return Router::$routes;
}
public static function go($action,$params=null) {
$actions = explode("#", $action);
$c = strtolower($actions[0]);
$a = strtolower($actions[1]);
// set query sting to null
$queryString = null;
if(isset($params)) {
foreach ($params as $name => $value) {
$queryString .= '/'.$name.'//'.$value;
}
return APP_URL."$c/$a$queryString";
}
return APP_URL."$c/$a";
}
public static function checkRoutes($action,$method){
foreach (Router::getInstance()->routes as $valid) {
/* echo $valid->action . ' == ' . $action . '|||';
echo $valid->method . ' == ' . $method . '|||';*/
if ($valid->method == $method && $valid->action == $action) {
return true;
}
}
}
public static function inverseRoute($controller,$action) {
return ucfirst($controller)."#".$action;
}
public static function notFound($action,$method) {
die("Route not found:: $action with method $method");
}
}
I use the json_decode function to parse the json object in stdClass().
The json file looks like this:
{"routes":[
{"action":"Frontend#index", "method":"GET"},
{"action":"Frontend#register", "method":"GET"},
{"action":"Frontend#blog", "method":"GET"}
]}
This way i can whitelist routes with their methods and return 404 errors while not found.
System is still quite basic but gives and idea and works, hope someone will find useful.
I'm trying to create multilingual application. I've implemented ability of translationable content and next step should be showing it to user. I want to have ability of changing language depending on URL. I've found a couple of components for those purposes but they all create urls which I don't like. For example, my application's default language is English and I have content which is translated into French. I have page "contacts", for instance. And URLs which will be generated by application will be: mysite.com/en/contacts, mysite.com/fr/contacts, but I want to have mysite.com/contacts for default language and mysite.com/fr/contacts for French language. It's simillar for site's root too. mysite.com/ - for default language and mysite.com/fr for French.
Is there any methods for implementing these functionality?
I'm using XUrlManager extension XUrlManager on GitHub
Yii generates URL's based on UrlManager rules. If you want URL's without /lang/ code - you need just create correct rules. For example, if you dublicate records in rules array:
'rules'=>array(
'<_c:\w+>/<_a:\w+>'=>'<_c>/<_a>',
'<language:\w{2}>/<_c:\w+>/<_a:\w+>'=>'<_c>/<_a>',
);
your URL's will be generated withou /en/ and /fr/, but URL's with code works too. By default, XUrlManager use previously selected language and store this in session or cookie.
If you want only hide /en/ and use /fr/ and others always, you can change your XUrlManager extension with:
public function createUrl($route,$params=array(),$ampersand='&')
{
if(!isset($params['language']) && Yii::app()->language!=='en')
$params['language']=Yii::app()->language;
return parent::createUrl($route,$params,$ampersand);
}
I've found very elegant method for solving my problem on http://www.elisdn.ru
Reimplement CHttpRequest
class DLanguageHttpRequest extends CHttpRequest
{
private $_requestUri;
public function getRequestUri()
{
if ($this->_requestUri === null)
$this->_requestUri = DMultilangHelper::processLangInUrl(parent::getRequestUri());
return $this->_requestUri;
}
public function getOriginalUrl()
{
return $this->getOriginalRequestUri();
}
public function getOriginalRequestUri()
{
return DMultilangHelper::addLangToUrl($this->getRequestUri());
}
}
Reimplement CUrlManager
class DLanguageUrlManager extends CUrlManager
{
public function createUrl($route, $params=array(), $ampersand='&')
{
$url = parent::createUrl($route, $params, $ampersand);
return DMultilangHelper::addLangToUrl($url);
}
}
Change config
return array(
'sourceLanguage'=>'en',
'language'=>'ru',
'components'=>array(
'request'=>array(
'class'=>'DLanguageHttpRequest',
...
),
'urlManager'=>array(
'class'=>'DLanguageUrlManager',
...
),
),
...
'params'=>array(
'translatedLanguages'=>array(
'ru'=>'Russian',
'en'=>'English',
'de'=>'Deutsch',
),
'defaultLanguage'=>'ru',
),
);
Create DMultilangHelper
class DMultilangHelper
{
public static function enabled()
{
return count(Yii::app()->params['translatedLanguages']) > 1;
}
public static function suffixList()
{
$list = array();
$enabled = self::enabled();
foreach (Yii::app()->params['translatedLanguages'] as $lang => $name)
{
if ($lang === Yii::app()->params['defaultLanguage']) {
$suffix = '';
$list[$suffix] = $enabled ? $name : '';
} else {
$suffix = '_' . $lang;
$list[$suffix] = $name;
}
}
return $list;
}
public static function processLangInUrl($url)
{
if (self::enabled())
{
$domains = explode('/', ltrim($url, '/'));
$isLangExists = in_array($domains[0], array_keys(Yii::app()->params['translatedLanguages']));
$isDefaultLang = $domains[0] == Yii::app()->params['defaultLanguage'];
if ($isLangExists && !$isDefaultLang)
{
$lang = array_shift($domains);
Yii::app()->setLanguage($lang);
}
$url = '/' . implode('/', $domains);
}
return $url;
}
public static function addLangToUrl($url)
if (self::enabled())
{
$domains = explode('/', ltrim($url, '/'));
$isHasLang = in_array($domains[0], array_keys(Yii::app()->params['translatedLanguages']));
$isDefaultLang = Yii::app()->getLanguage() == Yii::app()->params['defaultLanguage'];
if ($isHasLang && $isDefaultLang)
array_shift($domains);
if (!$isHasLang && !$isDefaultLang)
array_unshift($domains, Yii::app()->getLanguage());
$url = '/' . implode('/', $domains);
}
return $url;
}
}
After all of these steps your application will have URLs which you want
More information here