├── .gitattributes ├── .gitignore ├── application ├── config │ ├── constants.php │ └── routes.php ├── core │ ├── MY_Controller.php │ ├── MY_Router.php │ └── MY_URI.php ├── libraries │ └── Route.php └── routes │ └── .gitignore └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /application/config/constants.php: -------------------------------------------------------------------------------- 1 | my_controller/index 75 | | my-controller/my-method -> my_controller/my_method 76 | */ 77 | 78 | /** 79 | * 80 | * Route:: code here 81 | * 82 | */ 83 | 84 | 85 | $route = Route::map(); 86 | 87 | 88 | $route['default_controller'] = 'welcome'; 89 | $route['404_override'] = ''; 90 | $route['translate_uri_dashes'] = FALSE; 91 | 92 | 93 | /* End of file routes.php */ 94 | /* Location: ./application/config/routes.php */ -------------------------------------------------------------------------------- /application/core/MY_Controller.php: -------------------------------------------------------------------------------- 1 | __filter_params = array($this->uri->uri_string()); 13 | 14 | $this->call_filters('before'); 15 | } 16 | 17 | 18 | public function _remap($method, $parameters = array()) 19 | { 20 | 21 | empty($parameters) ? $this->$method() : call_user_func_array(array($this, $method), $parameters); 22 | 23 | if ($method != 'call_filters') 24 | { 25 | $this->call_filters('after'); 26 | } 27 | } 28 | 29 | 30 | /** 31 | * @param string $type 32 | */ 33 | private function call_filters($type) 34 | { 35 | 36 | $loaded_route = $this->router->get_active_route(); 37 | $filter_list = Route::get_filters($loaded_route, $type); 38 | 39 | foreach ($filter_list as $filter_data) 40 | { 41 | $param_list = $this->__filter_params; 42 | 43 | $callback = $filter_data['filter']; 44 | $params = $filter_data['parameters']; 45 | 46 | // check if callback has parameters 47 | if (!is_null($params)) 48 | { 49 | // separate the multiple parameters in case there are defined 50 | $params = explode(':', $params); 51 | 52 | // search for uris defined as parameters, they will be marked as {(.*)} 53 | foreach ($params as &$p) 54 | { 55 | if (preg_match('/\{(.*)\}/', $p, $match_p)) 56 | { 57 | $p = $this->uri->segment($match_p[1]); 58 | } 59 | } 60 | 61 | $param_list = array_merge($param_list, $params); 62 | } 63 | 64 | if (class_exists('Closure') and method_exists('Closure', 'bind')) 65 | { 66 | $callback = Closure::bind($callback, $this); 67 | } 68 | 69 | call_user_func_array($callback, $param_list); 70 | } 71 | } 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /application/core/MY_Router.php: -------------------------------------------------------------------------------- 1 | uri->segments); 54 | 55 | // Get HTTP verb 56 | $http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli'; 57 | 58 | // Is there a literal match? If so we're done 59 | if (isset($this->routes[$uri])) 60 | { 61 | // Check default routes format 62 | if (is_string($this->routes[$uri])) 63 | { 64 | $this->_load_request_uri($uri); 65 | $this->_set_request(explode('/', $this->routes[$uri])); 66 | return; 67 | } 68 | // Is there a matching http verb? 69 | elseif (is_array($this->routes[$uri]) && isset($this->routes[$uri][$http_verb])) 70 | { 71 | $this->_load_request_uri($uri); 72 | $this->_set_request(explode('/', $this->routes[$uri][$http_verb])); 73 | return; 74 | } 75 | } 76 | 77 | // Loop through the route array looking for wildcards 78 | foreach ($this->routes as $key => $val) 79 | { 80 | // Check if route format is using http verb 81 | if (is_array($val)) 82 | { 83 | if (isset($val[$http_verb])) 84 | { 85 | $val = $val[$http_verb]; 86 | } else 87 | { 88 | continue; 89 | } 90 | } 91 | 92 | //we have to keep the original key because we will have to use it 93 | //to recover the route again 94 | $original_key = $key; 95 | // Convert wildcards to RegEx 96 | $key = str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $key); 97 | 98 | // Does the RegEx match? 99 | if (preg_match('#^'.$key.'$#', $uri, $matches)) 100 | { 101 | // Are we using callbacks to process back-references? 102 | if (!is_string($val) && is_callable($val)) 103 | { 104 | // Remove the original string from the matches array. 105 | array_shift($matches); 106 | 107 | // Execute the callback using the values in matches as its parameters. 108 | $val = call_user_func_array($val, $matches); 109 | } 110 | // Are we using the default routing method for back-references? 111 | elseif (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE) 112 | { 113 | $val = preg_replace('#^'.$key.'$#', $val, $uri); 114 | } 115 | $this->_load_request_uri($original_key); 116 | 117 | $this->_set_request(explode('/', $val)); 118 | return; 119 | } 120 | } 121 | 122 | 123 | // If we got this far it means we didn't encounter a 124 | // matching route so we'll show the 404 error, because all routes 125 | // are now static 126 | //Die, you dinamic routes!!!! 127 | show_404(); 128 | } 129 | 130 | 131 | /** 132 | * Set default controller 133 | * 134 | * @return void 135 | */ 136 | protected function _set_default_controller() 137 | { 138 | parent::_set_default_controller(); 139 | 140 | $this->active_route = 'default_controller'; 141 | } 142 | 143 | private function _load_request_uri($uri) 144 | { 145 | $this->active_route = $uri; 146 | $this->uri->load_uri_parameters($uri); 147 | } 148 | 149 | public function get_active_route() 150 | { 151 | return $this->active_route; 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /application/core/MY_URI.php: -------------------------------------------------------------------------------- 1 | uri_parameters = Route::get_parameters($uri); 10 | } 11 | 12 | // Now the URI->segment Method also searchs for 13 | // named URIS in case we have declared them 14 | 15 | public function segment($n, $no_result = NULL) 16 | { 17 | if (!is_numeric($n)) 18 | { 19 | if (array_key_exists($n, $this->uri_parameters)) 20 | { 21 | $n = $this->uri_parameters[$n]; 22 | } else 23 | { 24 | return $no_result; 25 | } 26 | } 27 | 28 | return parent::segment($n, $no_result); 29 | } 30 | 31 | public function rsegment($n, $no_result = NULL) 32 | { 33 | if (!is_numeric($n)) 34 | { 35 | if (array_key_exists($n, $this->uri_parameters)) 36 | { 37 | $n = $this->uri_parameters[$n]; 38 | } else 39 | { 40 | return $no_result; 41 | } 42 | } 43 | 44 | return parent::rsegment($n, $no_result); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /application/libraries/Route.php: -------------------------------------------------------------------------------- 1 | get_from()] = &$object; 57 | } 58 | 59 | self::$pre_route_objects = array(); 60 | 61 | foreach (self::$route_objects as $key => $route_object) { 62 | $add_route = TRUE; 63 | //if there is a subdomain, we will check if it's ok with the route. 64 | //all the previously checked subdomain routes should be ignored because 65 | //we already have checked them 66 | if ($route_object->get_options('subdomain') != FALSE) { 67 | $add_route = self::_CheckSubdomain($route_object->get_options('subdomain')); 68 | } 69 | 70 | if ($add_route) { 71 | $from = $route_object->get_from(); 72 | $to = $route_object->get_to(); 73 | 74 | $routes[$from] = str_replace('{default_controller}', $controller, $to); 75 | } 76 | } 77 | 78 | return $routes; 79 | } 80 | 81 | //-------------------------------------------------------------------- 82 | 83 | /** 84 | * Returns the parameters of the selected route in case of we have defined 85 | * a parameter. This will be used in the URI library for naming uris purpouse 86 | * 87 | * Example: 88 | * Route::get_parameters('welcome/index'); 89 | * 90 | * @param array $route The string with the route to search 91 | * @return array The parameters 92 | */ 93 | public static function get_parameters($route) 94 | { 95 | if (array_key_exists($route, self::$route_objects)) { 96 | return self::$route_objects[$route]->get_parameters(); 97 | } 98 | 99 | return array(); 100 | } 101 | 102 | //-------------------------------------------------------------------- 103 | 104 | 105 | /** 106 | * A single point to the basic routing. Can be used in place of CI's $route 107 | * array if desired. Used internally by many of the methods. 108 | * 109 | * @param string $from 110 | * @param string $to 111 | * @return void 112 | */ 113 | public static function any($from, $to, $options = array(), $nested = FALSE) 114 | { 115 | return self::createRoute($from, $to, $options, $nested); 116 | } 117 | 118 | //-------------------------------------------------------------------- 119 | 120 | //-------------------------------------------------------------------- 121 | // HTTP Verb-based routing 122 | //-------------------------------------------------------------------- 123 | // Verb-based Routing works by only creating routes if the 124 | // $_SERVER['REQUEST_METHOD'] is the proper type. 125 | // 126 | 127 | /** 128 | * @param string $from 129 | * @param string $to 130 | */ 131 | public static function get($from, $to, $options = array(), $nested = FALSE) 132 | { 133 | if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'GET') { 134 | return self::createRoute($from, $to, $options, $nested); 135 | } 136 | } 137 | 138 | //-------------------------------------------------------------------- 139 | 140 | /** 141 | * @param string $from 142 | * @param string $to 143 | */ 144 | public static function post($from, $to, $options = array(), $nested = FALSE) 145 | { 146 | if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST') { 147 | return self::createRoute($from, $to, $options, $nested); 148 | } 149 | } 150 | 151 | //-------------------------------------------------------------------- 152 | 153 | /** 154 | * @param string $from 155 | * @param string $to 156 | */ 157 | public static function put($from, $to, $options = array(), $nested = FALSE) 158 | { 159 | if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'PUT') { 160 | return self::createRoute($from, $to, $options, $nested); 161 | } 162 | } 163 | 164 | //-------------------------------------------------------------------- 165 | 166 | /** 167 | * @param string $from 168 | * @param string $to 169 | */ 170 | public static function delete($from, $to, $options = array(), $nested = FALSE) 171 | { 172 | if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'DELETE') { 173 | return self::createRoute($from, $to, $options, $nested); 174 | } 175 | } 176 | 177 | //-------------------------------------------------------------------- 178 | 179 | public static function head($from, $to, $options = array(), $nested = FALSE) 180 | { 181 | if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'HEAD') { 182 | return self::createRoute($from, $to, $options, $nested); 183 | } 184 | } 185 | 186 | //-------------------------------------------------------------------- 187 | 188 | public static function patch($from, $to, $options = array(), $nested = FALSE) 189 | { 190 | if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'PATCH') { 191 | return self::createRoute($from, $to, $options, $nested); 192 | } 193 | } 194 | 195 | //-------------------------------------------------------------------- 196 | 197 | public static function options($from, $to, $options = array(), $nested = FALSE) 198 | { 199 | if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'OPTIONS') { 200 | return self::createRoute($from, $to, $options, $nested); 201 | } 202 | } 203 | 204 | //-------------------------------------------------------------------- 205 | 206 | public static function match(array $requests, $from, $to, $options = array(), $nested = FALSE) 207 | { 208 | $return = NULL; 209 | 210 | foreach ($requests as $request) { 211 | if (method_exists('Route', $request)) { 212 | $r = self::$request($from, $to, $options, $nested); 213 | 214 | if (!is_null($r)) { 215 | $return = $r; 216 | } 217 | } 218 | } 219 | return $return; 220 | } 221 | 222 | //-------------------------------------------------------------------- 223 | 224 | /** 225 | * Creates HTTP-verb based routing for a controller. 226 | * 227 | * Generates the following routes, assuming a controller named 'photos': 228 | * 229 | * Route::resources('photos'); 230 | * 231 | * Verb Path Action used for 232 | * ------------------------------------------------------------------ 233 | * GET /photos index displaying a list of photos 234 | * GET /photos/new create_new return an HTML form for creating a photo 235 | * POST /photos create create a new photo 236 | * GET /photos/{id} show display a specific photo 237 | * GET /photos/{id}/edit edit return the HTML form for editing a single photo 238 | * PUT /photos/{id} update update a specific photo 239 | * DELETE /photos/{id} delete delete a specific photo 240 | * 241 | * @param string $name The name of the controller to route to. 242 | * @param array $options An list of possible ways to customize the routing. 243 | */ 244 | public static function resources($name, $options = array(), $nested = FALSE) 245 | { 246 | if (empty($name)) { 247 | return; 248 | } 249 | 250 | $nest_offset = ''; 251 | 252 | // In order to allow customization of the route the 253 | // resources are sent to, we need to have a new name 254 | // to store the values in. 255 | $new_name = $name; 256 | 257 | // If a new controller is specified, then we replace the 258 | // $name value with the name of the new controller. 259 | if (isset($options['controller'])) { 260 | $new_name = $options['controller']; 261 | unset($options['controller']); 262 | } 263 | 264 | // If a new module was specified, simply put that path 265 | // in front of the controller. 266 | if (isset($options['module'])) { 267 | $new_name = $options['module'].'/'.$new_name; 268 | unset($options['module']); 269 | } 270 | 271 | // In order to allow customization of allowed id values 272 | // we need someplace to store them. 273 | $id = '([a-zA-Z0-9\-_]+)'; 274 | 275 | if (isset($options['constraint'])) { 276 | $id = $options['constraint']; 277 | unset($options['constraint']); 278 | } 279 | 280 | // If the 'offset' option is passed in, it means that all of our 281 | // parameter placeholders in the $to ($1, $2, etc), need to be 282 | // offset by that amount. This is useful when we're using an API 283 | // with versioning in the URL. 284 | 285 | $offset = 0; 286 | 287 | if (isset($options['offset'])) { 288 | $offset = (int) $options['offset']; 289 | unset($options['offset']); 290 | } 291 | 292 | if (is_array(self::$prefix) && !empty(self::$prefix)) { 293 | foreach (self::$prefix as $key => $p) { 294 | $nest_offset .= '/$'.($key + 1); 295 | $offset++; 296 | } 297 | } 298 | 299 | 300 | self::get($name, $new_name.'/index'.$nest_offset, $options, $nested); 301 | self::get($name.'/new', $new_name.'/create_new'.$nest_offset, $options, $nested); 302 | self::get($name.'/'.$id.'/edit', $new_name.'/edit'.$nest_offset.'/$'.(1 + $offset), $options, $nested); 303 | self::get($name.'/'.$id, $new_name.'/show'.$nest_offset.'/$'.(1 + $offset), $options, $nested); 304 | self::post($name, $new_name.'/create'.$nest_offset, $options, $nested); 305 | self::put($name.'/'.$id, $new_name.'/update'.$nest_offset.'/$'.(1 + $offset), $options, $nested); 306 | self::delete($name.'/'.$id, $new_name.'/delete'.$nest_offset.'/$'.(1 + $offset), $options, $nested); 307 | } 308 | 309 | //-------------------------------------------------------------------- 310 | /** 311 | * Adds a global pattern that will be used along all the routes 312 | * 313 | * Route::pattern('user_name', '[a-z][A-Z]'); 314 | * 315 | * @param string $pattern The pattern to search 316 | * @param string $regex The regex to substitute for 317 | */ 318 | public static function pattern($pattern, $regex) 319 | { 320 | self::$pattern_list[$pattern] = $regex; 321 | } 322 | 323 | //-------------------------------------------------------------------- 324 | /** 325 | * Return the pattern in the list if exists. If not, returns NULL 326 | * 327 | * @param string $pattern The pattern to search 328 | */ 329 | public static function get_pattern($pattern) 330 | { 331 | if (array_key_exists($pattern, self::$pattern_list)) { 332 | return self::$pattern_list[$pattern]; 333 | } else { 334 | return NULL; 335 | } 336 | } 337 | 338 | //-------------------------------------------------------------------- 339 | 340 | /** 341 | * Add a prefix to the $from portion of the route. This is handy for 342 | * grouping items under a similar URL, like: 343 | * 344 | * Route::prefix('admin', function() 345 | * { 346 | * Route::resources('users'); 347 | * }); 348 | * 349 | * @param string|array $name The prefix to add to the routes. 350 | * @param Closure $callback 351 | */ 352 | public static function prefix($name, Closure $callback) 353 | { 354 | if (is_array($name)) { 355 | if (array_key_exists('subdomain', $name)) { 356 | $subdomain = $name['subdomain']; 357 | } 358 | 359 | $name = $name['name']; 360 | } 361 | 362 | self::_add_prefix($name); 363 | 364 | if (isset($subdomain)) { 365 | self::subdomain($subdomain, $callback); 366 | } else { 367 | call_user_func($callback); 368 | } 369 | 370 | self::_delete_prefix(); 371 | } 372 | 373 | //-------------------------------------------------------------------- 374 | 375 | /** 376 | * Add a prefix to the $prefix array 377 | * 378 | * @param string $prefix The prefix to add to the routes. 379 | */ 380 | 381 | private static function _add_prefix($prefix) 382 | { 383 | 384 | self::$prefix[] = $prefix; 385 | } 386 | //-------------------------------------------------------------------- 387 | 388 | /** 389 | * Deletes a prefix from the $prefix array 390 | */ 391 | 392 | private static function _delete_prefix() 393 | { 394 | array_pop(self::$prefix); 395 | } 396 | //-------------------------------------------------------------------- 397 | 398 | /** 399 | * Return the actual prefix of the routes 400 | */ 401 | 402 | public static function get_prefix() 403 | { 404 | if (!empty(self::$prefix)) { 405 | return implode('/', self::$prefix).'/'; 406 | } else { 407 | return ''; 408 | } 409 | } 410 | 411 | //-------------------------------------------------------------------- 412 | 413 | /** 414 | * Returns the $from portion of the route if it has been saved with a name 415 | * previously. 416 | * 417 | * Example: 418 | * 419 | * Route::get('posts', 'posts/show', array('as' => 'posts')); 420 | * redirect( Route::named('posts') ); 421 | * 422 | * @param [type] $name [description] 423 | * @return [type] [description] 424 | */ 425 | public static function named($name, $parameters = array()) 426 | { 427 | if (isset(self::$named_routes[$name])) { 428 | $return_url = self::$named_routes[$name]; 429 | } else { 430 | return NULL; 431 | } 432 | 433 | if (!empty($parameters)) { 434 | foreach ($parameters as $key => $parameter) { 435 | $return_url = str_replace('$'.($key + 1), $parameter, $return_url); 436 | } 437 | } 438 | 439 | return $return_url; 440 | } 441 | 442 | //-------------------------------------------------------------------- 443 | 444 | /** 445 | * Sets a name for a route with $from portion of the route 446 | * 447 | * Example: 448 | * 449 | * Route::get('posts', 'posts/show', array('as' => 'posts')); 450 | * redirect( Route::named('posts') ); 451 | * 452 | * @param [type] $name [description] 453 | * @param string $route the route itself 454 | */ 455 | public static function set_name($name, $route) 456 | { 457 | self::$named_routes[$name] = $route; 458 | } 459 | 460 | //-------------------------------------------------------------------- 461 | 462 | //-------------------------------------------------------------------- 463 | // Contexts 464 | //-------------------------------------------------------------------- 465 | 466 | /** 467 | * Contexts provide a way for modules to assign controllers to an area of the 468 | * site based on the name of the controller. This can be used for making a 469 | * '/developer' area of the site that all modules can create functionality into. 470 | * 471 | * @param string $name The name of the URL segment 472 | * @param string $controller The name of the controller 473 | * @param array $options 474 | * 475 | * @return void 476 | */ 477 | public static function context($name, $controller = NULL, $options = array()) 478 | { 479 | // If $controller is an array, then it's actually the options array, 480 | // so we'll reorganize parameters. 481 | if (is_array($controller)) { 482 | $options = $controller; 483 | $controller = NULL; 484 | } 485 | 486 | // If $controller is empty, then we need to rename it to match 487 | // the $name value. 488 | if (empty($controller)) { 489 | $controller = $name; 490 | } 491 | 492 | $offset = isset($options['offset']) ? (int) $options['offset'] : 0; 493 | 494 | // Some helping hands 495 | $first = 1 + $offset; 496 | $second = 2 + $offset; 497 | $third = 3 + $offset; 498 | $fourth = 4 + $offset; 499 | $fifth = 5 + $offset; 500 | $sixth = 6 + $offset; 501 | 502 | self::any($name.'/(:any)/(:any)/(:any)/(:any)/(:any)/(:any)', "\${$first}/{$controller}/\${$second}/\${$third}/\${$fourth}/\${$fifth}/\${$sixth}"); 503 | self::any($name.'/(:any)/(:any)/(:any)/(:any)/(:any)', "\${$first}/{$controller}/\${$second}/\${$third}/\${$fourth}/\${$fifth}"); 504 | self::any($name.'/(:any)/(:any)/(:any)/(:any)', "\${$first}/{$controller}/\${$second}/\${$third}/\${$fourth}"); 505 | self::any($name.'/(:any)/(:any)/(:any)', "\${$first}/{$controller}/\${$second}/\${$third}"); 506 | self::any($name.'/(:any)/(:any)', "\${$first}/{$controller}/\${$second}"); 507 | self::any($name.'/(:any)', "\${$first}/{$controller}"); 508 | 509 | unset($first, $second, $third, $fourth, $fifth, $sixth); 510 | 511 | // Are we creating a home controller? 512 | if (isset($options['home']) && !empty($options['home'])) { 513 | self::any($name, "{$options['home']}"); 514 | } 515 | } 516 | 517 | //-------------------------------------------------------------------- 518 | 519 | /** 520 | * Allows you to easily block access to any number of routes by setting 521 | * that route to an empty path (''). 522 | * 523 | * Example: 524 | * Route::block('posts', 'photos/(:num)'); 525 | * 526 | * // Same as... 527 | * $route['posts'] = ''; 528 | * $route['photos/(:num)'] = ''; 529 | */ 530 | public static function block() 531 | { 532 | $paths = func_get_args(); 533 | 534 | if (!is_array($paths)) { 535 | return; 536 | } 537 | 538 | foreach ($paths as $path) { 539 | self::createRoute($path, ''); 540 | } 541 | } 542 | 543 | //-------------------------------------------------------------------- 544 | 545 | 546 | //-------------------------------------------------------------------- 547 | // Utility Methods 548 | //-------------------------------------------------------------------- 549 | 550 | /** 551 | * Resets the class to a first-load state. Mainly useful during testing. 552 | * 553 | * @return void 554 | */ 555 | public static function reset() 556 | { 557 | self::$route_objects = array(); 558 | self::$named_routes = array(); 559 | self::$routes = array(); 560 | self::$prefix = NULL; 561 | self::$pre_route_objects = array(); 562 | self::$pattern_list = array(); 563 | self::$filter_list = array(); 564 | } 565 | 566 | //-------------------------------------------------------------------- 567 | 568 | //-------------------------------------------------------------------- 569 | // Create Route Methods 570 | //-------------------------------------------------------------------- 571 | 572 | /** 573 | * Does the heavy lifting of creating an actual route. You must specify 574 | * the request method(s) that this route will work for. They can be separated 575 | * by a pipe character "|" if there is more than one. 576 | * 577 | * @param string $from 578 | * @param array $to 579 | * @param array $options 580 | * @param boolean $nested 581 | * 582 | * @return array The built route. 583 | */ 584 | static function createRoute($from, $to, $options = array(), $nested = FALSE) 585 | { 586 | if (!is_null(self::$active_subdomain)) { 587 | $options['subdomain'] = self::$active_subdomain; 588 | } 589 | 590 | if (array_key_exists('subdomain', $options) && self::_CheckSubdomain($options['subdomain']) === FALSE) { 591 | return FALSE; 592 | } 593 | 594 | $new_route = new Route_object($from, $to, $options, $nested); 595 | 596 | self::$pre_route_objects[] = $new_route; 597 | 598 | $new_route->make(); 599 | 600 | return new Route_facade($new_route); 601 | 602 | } 603 | 604 | 605 | //-------------------------------------------------------------------- 606 | 607 | //-------------------------------------------------------------------- 608 | // Subdomain Methods 609 | //-------------------------------------------------------------------- 610 | 611 | 612 | static function get_subdomain() 613 | { 614 | 615 | if (is_null(self::$subdomain)) { 616 | if (defined('ROUTE_DOMAIN_NAME') === FALSE) { 617 | define('ROUTE_DOMAIN_NAME', $_SERVER['HTTP_HOST']); 618 | } 619 | 620 | self::$subdomain = preg_replace('/^(?:([^\.]+)\.)?'.ROUTE_DOMAIN_NAME.'$/', '\1', $_SERVER['HTTP_HOST']); 621 | } 622 | 623 | return self::$subdomain; 624 | } 625 | 626 | static function subdomain($subdomain_rules, closure $callback) 627 | { 628 | if (self::_CheckSubdomain($subdomain_rules) === TRUE) { 629 | self::$active_subdomain = $subdomain_rules; 630 | call_user_func($callback); 631 | } 632 | 633 | self::$active_subdomain = NULL; 634 | } 635 | 636 | 637 | static private function _CheckSubdomain($subdomain_rules = NULL) 638 | { 639 | $subdomain = self::get_subdomain(); 640 | 641 | //if the subdomain rules are "FALSE" then if we have a subdomain we won't make the route, because 642 | //that's the indication of not allowing subdomains in that route 643 | if ($subdomain != '' and $subdomain_rules == FALSE) { 644 | return FALSE; 645 | } elseif ($subdomain == '' and $subdomain_rules == FALSE) { 646 | return TRUE; 647 | } 648 | 649 | //if subdomain it's empty, then we will return false, because there is no subdomain in the url 650 | if ($subdomain == '') { 651 | return FALSE; 652 | } 653 | 654 | $i = preg_match('/^\{(.+)\}$/', $subdomain_rules); 655 | 656 | //if the subdomain rules have a named parameter, we will wait till the end of the 657 | //route generation for it's rule 658 | if ($i > 0) { 659 | return TRUE; 660 | } 661 | 662 | $i = preg_match('/^\(\:any\)/', $subdomain_rules); 663 | 664 | if ($i > 0) { 665 | return TRUE; 666 | } 667 | 668 | $i = preg_match('/^\(\:num\)/', $subdomain_rules); 669 | 670 | if ($i > 0) { 671 | return is_numeric($subdomain); 672 | } 673 | 674 | //if we arrive here we will count the subdomain_rules as a regex, so se will check it 675 | $i = preg_match($subdomain_rules, $subdomain); 676 | 677 | if ($i > 0) { 678 | return TRUE; 679 | } 680 | 681 | return FALSE; 682 | } 683 | 684 | 685 | //-------------------------------------------------------------------- 686 | 687 | //-------------------------------------------------------------------- 688 | // Filter Methods 689 | //-------------------------------------------------------------------- 690 | 691 | /** 692 | * Adds a new filter into the list 693 | * 694 | * @param string $name 695 | * @param Closure $callback 696 | * 697 | */ 698 | static function filter($name, $callback) 699 | { 700 | self::$filter_list[$name] = $callback; 701 | } 702 | 703 | 704 | //-------------------------------------------------------------------- 705 | 706 | static function get_filters($route, $type = 'before') 707 | { 708 | if (array_key_exists($route, self::$route_objects)) { 709 | $filter_list = self::$route_objects[$route]->get_filters($type); 710 | 711 | $callback_list = array(); 712 | 713 | foreach ($filter_list as $filter) { 714 | if (is_callable($filter)) { 715 | $callback_list[] = array('filter' => $filter, 716 | 'parameters' => NULL 717 | ); 718 | } else { 719 | $param = NULL; 720 | 721 | // check if callback has parameters 722 | if (preg_match('/(.*?)\[(.*)\]/', $filter, $match)) { 723 | $filter = $match[1]; 724 | $param = $match[2]; 725 | } 726 | 727 | if (array_key_exists($filter, self::$filter_list)) { 728 | $callback_list[] = array('filter' => self::$filter_list[$filter], 729 | 'parameters' => $param 730 | ); 731 | } 732 | } 733 | } 734 | 735 | return $callback_list; 736 | } 737 | 738 | return array(); 739 | } 740 | 741 | } 742 | 743 | 744 | class Route_facade 745 | { 746 | 747 | private $loaded_object; 748 | 749 | function __construct(Route_object & $object) 750 | { 751 | $this->loaded_object = &$object; 752 | } 753 | 754 | public function where($parameter, $pattern = NULL) 755 | { 756 | $this->loaded_object->where($parameter, $pattern); 757 | 758 | return $this; 759 | } 760 | } 761 | 762 | class Route_object 763 | { 764 | 765 | private $pre_from; 766 | private $from; 767 | private $to; 768 | private $options; 769 | private $nested; 770 | private $prefix; 771 | 772 | private $optional_parameters = array(); 773 | private $parameters = array(); 774 | private $optional_objects = array(); 775 | 776 | /** 777 | * @param string $from 778 | * @param boolean $nested 779 | */ 780 | function __construct($from, $to, $options, $nested) 781 | { 782 | $this->pre_from = $from; 783 | $this->to = $to; 784 | $this->options = $options; 785 | $this->nested = $nested; 786 | 787 | $this->prefix = Route::get_prefix(); 788 | 789 | $this->pre_from = $this->prefix.$this->pre_from; 790 | 791 | //check for route parameters 792 | $this->_check_parameters(); 793 | 794 | } 795 | 796 | public function make() 797 | { 798 | // Due to bug stated in https://github.com/Patroklo/codeigniter-static-laravel-routes/issues/11 799 | // we will make a cleanup of the parameters not used in the optional cases 800 | $parameter_positions = array_flip(array_keys($this->parameters)); 801 | 802 | //first of all, we check for optional parameters. If they exist, 803 | //we will make another route without the optional parameter 804 | foreach ($this->optional_parameters as $parameter) { 805 | $from = $this->pre_from; 806 | $to = $this->to; 807 | 808 | //we get rid of prefix in case it exists 809 | if (!empty($this->prefix) && strpos($from, $this->prefix) === 0) { 810 | $from = substr($from, strlen($this->prefix)); 811 | }; 812 | 813 | 814 | foreach ($parameter as $p) { 815 | 816 | // Create the new $from without some of the optional routes 817 | $from = str_replace('/{'.$p.'}', '', $from); 818 | 819 | // Create the new $to without some of the optional destiny routes 820 | if (array_key_exists($p, $parameter_positions)) { 821 | $to = str_replace('/$'.($parameter_positions[$p] + 1), '', $to); 822 | } 823 | } 824 | 825 | // Save the optional routes in case we will need them for where callings 826 | $this->optional_objects[] = Route::createRoute($from, $to, $this->options, $this->nested); 827 | } 828 | 829 | // Do we have a nested function? 830 | if ($this->nested && is_callable($this->nested)) { 831 | $name = rtrim($this->pre_from, '/'); 832 | if (array_key_exists('subdomain', $this->options)) { 833 | Route::prefix(array('name' => $name, 'subdomain' => $this->options['subdomain']), $this->nested); 834 | } else { 835 | Route::prefix($name, $this->nested); 836 | } 837 | } 838 | 839 | } 840 | 841 | private function _check_parameters() 842 | { 843 | preg_match_all('/\{(.+?)\}/', $this->pre_from, $matches); 844 | 845 | if (array_key_exists(1, $matches) && !empty($matches[1])) { 846 | //we make the parameters that the route could have and, if 847 | //it's an optional parameter, we add it into the optional parameters array 848 | //to make later the new route without it 849 | 850 | $uris = array(); 851 | foreach ($matches[1] as $parameter) { 852 | if (substr($parameter, -1) == '?') { 853 | $new_key = str_replace('?', '', $parameter); 854 | 855 | //$this->optional_parameters[$parameter] = $new_key; 856 | $uris[] = $new_key; 857 | 858 | $this->pre_from = str_replace('{'.$parameter.'}', '{'.$new_key.'}', $this->pre_from); 859 | 860 | $parameter = $new_key; 861 | } 862 | 863 | $this->parameters[$parameter] = array('value' => NULL); 864 | } 865 | 866 | if (!empty($uris)) { 867 | $num = count($uris); 868 | 869 | //The total number of possible combinations 870 | $total = pow(2, $num); 871 | 872 | //Loop through each possible combination 873 | for ($i = 0; $i < $total; $i++) { 874 | 875 | $sub_list = array(); 876 | 877 | for ($j = 0; $j < $num; $j++) { 878 | //Is bit $j set in $i? 879 | if (pow(2, $j) & $i) { 880 | $sub_list[] = $uris[$j]; 881 | } 882 | } 883 | 884 | $this->optional_parameters[] = $sub_list; 885 | } 886 | 887 | if (!empty($this->optional_parameters)) { 888 | array_shift($this->optional_parameters); 889 | } 890 | } 891 | 892 | 893 | $uri_list = explode('/', $this->pre_from); 894 | 895 | foreach ($uri_list as $key => $uri) { 896 | $new_uri = str_replace(array('{', '}'), '', $uri); 897 | 898 | if (array_key_exists($new_uri, $this->parameters)) { 899 | $this->parameters[$new_uri]['uri'] = ($key + 1); 900 | } 901 | 902 | } 903 | 904 | } 905 | } 906 | 907 | public function get_from() 908 | { 909 | //check if parameters of the from have a regex pattern to put in their place 910 | //if not, they will be a (:any) 911 | 912 | if (is_null($this->from)) { 913 | $pattern_list = array(); 914 | $substitution_list = array(); 915 | $named_route_substitution_list = array(); 916 | 917 | $pattern_num = 1; 918 | 919 | foreach ($this->parameters as $parameter => $data) { 920 | $value = $data['value']; 921 | 922 | //if there is a question mark in the parameter 923 | //we will add a scape \ for the regex 924 | $pattern_list[] = '/\{'.$parameter.'\}/'; 925 | 926 | //if parameter is null will check if there is a global parameter, if not, 927 | //we will put an (:any) 928 | if (is_null($value)) { 929 | $pattern_value = Route::get_pattern($parameter); 930 | 931 | if (!is_null($pattern_value)) { 932 | if ($pattern_value[0] != '(' && $pattern_value[strlen($pattern_value) - 1] != ')') { 933 | $pattern_value = '('.$pattern_value.')'; 934 | } 935 | 936 | $substitution_list[] = $pattern_value; 937 | } else { 938 | $substitution_list[] = '(:any)'; 939 | } 940 | } else { 941 | if ($value[0] != '(' && $value[strlen($value) - 1] != ')') { 942 | $value = '('.$value.')'; 943 | } 944 | 945 | $substitution_list[] = $value; 946 | } 947 | 948 | $named_route_substitution_list[] = '\$'.$pattern_num; 949 | $pattern_num += 1; 950 | } 951 | 952 | // check for named subdomains 953 | if (array_key_exists('subdomain', $this->options)) { 954 | $i = preg_match('/^\{(.+)\}$/', $this->options['subdomain']); 955 | 956 | if ($i > 0) { 957 | preg_match('/^\{(.+)\}$/', $this->options['subdomain'], $check); 958 | 959 | $subdomain = $check[1]; 960 | 961 | if (!array_key_exists($subdomain, $this->parameters)) { 962 | $pattern_value = Route::get_pattern($subdomain); 963 | 964 | if (!is_null($pattern_value)) { 965 | $this->options['subdomain'] = $pattern_value; 966 | } else { 967 | $this->options['subdomain'] = '(:any)'; 968 | } 969 | } else { 970 | $value = $this->parameters[$subdomain]['value']; 971 | $this->options['subdomain'] = $value; 972 | } 973 | } else { 974 | $this->options['checked_subdomain'] = $this->options['subdomain']; 975 | unset($this->options['subdomain']); 976 | } 977 | } 978 | 979 | // make substitutions to make codeigniter comprensible routes 980 | $this->from = preg_replace($pattern_list, $substitution_list, $this->pre_from); 981 | 982 | // make substitutions in case there is a named route 983 | // Are we saving the name for this one? 984 | if (isset($this->options['as']) && !empty($this->options['as'])) { 985 | $named_route = preg_replace($pattern_list, $named_route_substitution_list, $this->pre_from); 986 | 987 | Route::set_name($this->options['as'], $named_route); 988 | } 989 | 990 | } 991 | return $this->from; 992 | } 993 | 994 | public function get_to() 995 | { 996 | return $this->to; 997 | } 998 | 999 | public function where($parameter, $pattern = NULL) 1000 | { 1001 | if (is_array($parameter)) { 1002 | foreach ($parameter as $key => $value) 1003 | { 1004 | $this->where($key, $value); 1005 | } 1006 | } 1007 | else { 1008 | //calling all the optional routes to send them the where 1009 | foreach ($this->optional_objects as $ob) { 1010 | $ob->where($parameter, $pattern); 1011 | } 1012 | 1013 | $this->parameters[$parameter]['value'] = $pattern; 1014 | } 1015 | 1016 | return $this; 1017 | } 1018 | 1019 | public function get_parameters() 1020 | { 1021 | $return_parameters = array(); 1022 | 1023 | foreach ($this->parameters as $key => $parameter) { 1024 | if (array_key_exists('uri', $parameter)) { 1025 | $return_parameters[$key] = $parameter['uri']; 1026 | } 1027 | } 1028 | 1029 | return $return_parameters; 1030 | } 1031 | 1032 | public function get_filters($type = 'before') 1033 | { 1034 | if (isset($this->options[$type]) && !empty($this->options[$type])) { 1035 | $filters = $this->options[$type]; 1036 | 1037 | if (is_string($filters)) { 1038 | $filters = explode('|', $filters); 1039 | } 1040 | 1041 | if (!is_array($filters)) { 1042 | $filters = array($filters); 1043 | } 1044 | 1045 | return $filters; 1046 | } 1047 | 1048 | return array(); 1049 | } 1050 | 1051 | public function get_options($option = NULL) 1052 | { 1053 | if ($option == NULL) { 1054 | return $this->options; 1055 | } else { 1056 | if (array_key_exists($option, $this->options)) { 1057 | return $this->options[$option]; 1058 | } 1059 | } 1060 | 1061 | return FALSE; 1062 | 1063 | } 1064 | 1065 | } 1066 | -------------------------------------------------------------------------------- /application/routes/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # CodeIgniter Laravel-like static routes and filters 2 | 3 | A little group or libraries that let Codeigniter work as a static routing system with filters as simmilar to the Laravel routing system as I could have done. 4 | 5 | 6 | Written by Joseba Juániz ([@Patroklo](http://twitter.com/Patroklo)). 7 | Based on the routing work of ([Bonfire team](http://cibonfire.com/)). 8 | 9 | 10 | ## Requirements 11 | 12 | Codeigniter 3.x (For the 2.x branch look at: [LINK](https://github.com/Patroklo/codeigniter-static-laravel-routes/tree/for-codeigniter-2.X) 13 | 14 | Php 5.3 or above 15 | 16 | ## Licensing 17 | 18 | For now I'm not licensing this work. It has some code from the Bonfire Project, wich keeps their license rights. 19 | 20 | 21 | ## Future plans 22 | 23 | - Caching routes. 24 | 25 | ## Installation 26 | 27 | Just put copy the files into your server using the same folder structure. If the developer have a previously installed module system as HMVC the developer could have to overwrite the MY_Route files. If that's the case and the developer have any trouble making it, just ask for directions about that. 28 | 29 | ## Installation alongside Hierarchical model-view-controller (HMVC) modules 30 | 31 | I have tested 2 libraries right now, I don't give direct support to any of those and all I won't be responsible of the changes you make in any file. 32 | 33 | ### wiredesignz Modular extensions - HMVC 34 | 35 | The installation it's pretty simple with this library. 36 | 37 | 1 - Add the HMVC library on your Codeigniter installation. ([LINK](https://bitbucket.org/wiredesignz/codeigniter-modular-extensions-hmvc)). 38 | 39 | 2 - Change the APPPATH/core/MY_Router.php file to another name, I'll use "Base_Router.php" in this tutorial. You'll also have to change the class name of the file to match the filename. 40 | 41 | 3 - Then install this library. 42 | 43 | 4 - Change the APPPATH/core/MY_Controller.php extended file from "CI_Controller" to "MX_Controller". 44 | 45 | 5 - Change the APPPATH/core/MY_Router.php extended file from "CI_Router" to the new name you have given to the router file of the modular extensions library (in this example "Base_router") remember that you probably will have to add a include_once clause to import that file before the class declaration. 46 | 47 | tl;dr install HMVC library, install the router library and make MY_Controller extend MX_Controller and MY_Router extend the HMVC Router library. 48 | 49 | ### jenssegers codeigniter-hmvc-modules 50 | 51 | 1 - Add the HMVC library on your Codeigniter installation. ([LINK](https://github.com/jenssegers/codeigniter-hmvc-modules)). 52 | 53 | 2 - Delete the APPPATH/core/MY_Router.php file and put the APPPATH/third_party/HMVC/Router.php in it's place changing its name to MY_Router (and also the class inside the file). 54 | 55 | 3 - Then install this library without MY_Router.php to prevent file collisions. 56 | 57 | 4 - Move the Router.php file to APPPATH/core but changing its name to another one (in this example we will use "Base_Router.php"). 58 | 59 | 5 - Change the APPPATH/core/MY_Router.php extended file from "CI_Router" to the new name you have given to the router file of this library (in this example "Base_router") remember that you probably will have to add a include_once clause to import that file before the class declaration. 60 | 61 | tl;dr install HMVC library, install the router library and make the HMVC router library extend this router library. 62 | 63 | ## Routing tutorial 64 | 65 | The developer can use all the HTML methods to define a Route. This route will only be generated if the query of the page coincides with it's method. Meaning that if the developer make a GET call to the server, the library will only make the GET routes, leaving the rest as not existant. This also can be used by the developer to make a RESTFUL server. 66 | 67 | ### Basic routing 68 | 69 | The basic methods are: 70 | 71 | 72 | Route::get('user', 'user/index'); 73 | Route::post('user/(:any)', 'user/load/$1'); 74 | Route::put('user/(:any), 'user/update/$1'); 75 | Route::delete('user/(:any)','user/delete/$1'); 76 | Route::head('user', 'user/index'); 77 | Route::patch('user/(:any), 'user/update/$1'); 78 | Route::options('user/(:any),'user/load/$1'); 79 | 80 | The developer can also use two additional functions that let the route to be generated in more than one method: 81 | 82 | `any` will work with any HTTP method (GET, POST, PUT, DELETE...). 83 | 84 | 85 | Route::any('user', 'user/index'); 86 | 87 | `match` lets the developer to define manually wich methods will be accepted by this route. 88 | 89 | 90 | Route::match(array('GET', 'POST'), 'user', 'user/index'); 91 | 92 | 93 | ### Subdomain routing 94 | 95 | It uses the same routing method as the Basic routing but adding an extra wildcard for the subdomain. 96 | Adding this extra option means that the routes with them won't show on urls without subdomain. 97 | 98 | Route::any('user', 'user/index', array('subdomain' => '(:any)')); 99 | 100 | 101 | ### Named routes 102 | 103 | The developer can name a route. That will let him to call this name instead of using all the route in the future. 104 | 105 | 106 | 107 | Route::set_name('user_update', 'admin/user/load/'); 108 | 109 | Route::get('user', 'user/index', array('as' => 'user')); 110 | 111 | 112 | Calling a named route: 113 | 114 | 115 | echo Route::named('user'); 116 | 117 | redirect(Route::named('user')); 118 | 119 | Optionally, if that route has uri parameters, you can set them via array at the second parameter of the method: 120 | 121 | 122 | redirect(Route::named('user', array('12'))); 123 | 124 | ### Named parameters 125 | 126 | The developer can also define named parameters in each route instead of using wildcards or regular expressions. This will let the developer use them also in the URI with their defined names. 127 | If the developer don't define a named parameter, it will be used a an `(:any)` in the route. 128 | 129 | 130 | Route::any('user/{id}', 'user/load/$1'); 131 | 132 | 133 | There are two kinds of parameter definitions. The global definition, that will set a wildcard or regular expression to every named parameter with that name in the routes file and the local definition, that will only affect to the Route in wich it's defined. 134 | 135 | Global parameter definition: 136 | 137 | 138 | Route::pattern('id', '[0-9]+'); 139 | Route::pattern('name', '(:any)'); 140 | 141 | 142 | Local parameter definition: 143 | 144 | 145 | Route::post('user/{id}', 'user/load/$1')->where('id', '[0-9]+'); 146 | 147 | 148 | Multiple local parameter definition; 149 | 150 | 151 | Route::post('user/{id}/{name}', 'user/load/$1/$2')->where(array('id' => '[0-9]+', 'name' => '(:any)')); 152 | 153 | 154 | ### Optional parameters 155 | 156 | There can be defined optional parameters. That will let Codeigniter use that route as much as there is or isn't a URI defined in that position. 157 | The parameter definition is the same as the normal Named Parameters (the parameter name without the question mark "?"). 158 | 159 | 160 | Route::any('user/{id?}', 'user/load/$1')->where('id', '[0-9]+'); 161 | 162 | 163 | This will let the developer use "user" and "user/12" as routes using Codeigniter the method load in the controller user in both calls. 164 | 165 | The developer can also stack multiple optional parameters like this and all the possible permutations of the optional uris will be defined automatically: 166 | 167 | 168 | Route::any('user/{id?}/{name?}/{telephone?}', 'user/load/$1/$2/$3')->where('id', '[0-9]+'); 169 | 170 | 171 | ### Accessing named and optional parameters 172 | 173 | Codeigniter will store a list of this parameters in the URI library, so the developer can access them using their name instead of the position in the uri segment. 174 | 175 | 176 | $user_id = $this->uri->segment('id'); 177 | 178 | $user_name = $this->uri->rsegment('name'); 179 | 180 | 181 | ### Route filters 182 | 183 | The developer can also define route filters. That will execute the defined methods before or after the controller execution. 184 | This is useful for adding additional info or making previous checks, like if the user is logged in. 185 | 186 | Filter definition: 187 | 188 | 189 | Route::filter('logged_in', function(){ 190 | 191 | if($this->auth->logged_in() == FALSE) 192 | { 193 | show_404(); 194 | } 195 | 196 | }); 197 | 198 | This code will be executed immediately after the CI_Controller construction or after the method calling, so the developer can use all the auto loaded files and also load new ones with `$this->load` if it's neccesary. 199 | 200 | Adding a filter to a route: 201 | 202 | The developer can define two filter callings, `before` a route calling and `after` the route execution. 203 | 204 | 205 | Route::any('user/{id}', 'user/load/$1', array('before' => 'logged_in')); 206 | 207 | 208 | This will launch the logged_in check before Codeigniter calls the `load` method, so if the user it's not logged in, instead will receive a 404 error display. 209 | 210 | Passing multiple filters 211 | 212 | 213 | Route::any('user/{id}', 'user/load/$1', array('before' => array('logged_in', 'check_params'))); 214 | Route::any('user/{id}', 'user/load/$1', array('after' => 'logged_in|check_params')); 215 | 216 | Anonymous function filters 217 | 218 | Route::any('user/{id}', 'user/load/$1', array('before' => function(){ 219 | // your code here 220 | })); 221 | 222 | 223 | Specifying filter parameters 224 | 225 | All filters always receive as first parameter the uri string of the route. But it's possible to set manually more parameters to send to the filter method. 226 | 227 | Sometimes it's useful to send parameters to the filter anonymous functions. This can be easily made as: 228 | 229 | Route::any('user/{id}', 'user/load/$1', array('before' => 'logged_in[user_name]')); 230 | 231 | The developer can also add uri segments as parameters putting the number or name of the segment between {}. The filter parameters will be separated with the ":" character 232 | 233 | Route::any('user/{id}', 'user/load/$1', array('before' => 'logged_in[user_name:{id}]')); 234 | 235 | Now the parameter will look like: 236 | 237 | Route::filter('logged_in', function($uri, $user_name, $id){ 238 | 239 | var_dump($uri, $user_name, $id); 240 | 241 | if($this->auth->logged_in() == FALSE) 242 | { 243 | show_404(); 244 | } 245 | 246 | }); 247 | 248 | 249 | ### Route groups 250 | 251 | This lets the developer add a prefix to every route inside it. This it's useful for defining admin routes, for example. 252 | 253 | 254 | Route::prefix('admin', function(){ 255 | 256 | Route::prefix('user', function(){ 257 | 258 | Route::post('update/(:any)', 'user/update/$1'); 259 | 260 | }); 261 | 262 | }); 263 | 264 | 265 | Another way of define route groups 266 | 267 | 268 | Route::any('admin', 'admin/index', array(), function(){ 269 | 270 | Route::any('user', 'user/index'); 271 | 272 | }); 273 | 274 | 275 | ### RESTFUL like routes 276 | 277 | 278 | Route::resources('photos', $options); 279 | 280 | 281 | This will autocreate all this routes: 282 | 283 | ``` 284 | GET /photos index displaying a list of photos 285 | GET /photos/new create_new return an HTML form for creating a photo 286 | POST /photos create create a new photo 287 | GET /photos/{id} show display a specific photo 288 | GET /photos/{id}/edit edit return the HTML form for editing a single photo 289 | PUT /photos/{id} update update a specific photo 290 | DELETE /photos/{id} delete delete a specific photo 291 | ``` 292 | 293 | The $options parameter it's optional and can hold the same values as any other $options parameter 294 | of the rest of route methods (like filters, etc...). 295 | 296 | 297 | ### Auto routes 298 | 299 | The developer can make with one call a 6 tier route definition 300 | 301 | 302 | Route::context('photos', 'photo'); 303 | 304 | ``` 305 | photo/(:any)/(:any)/(:any)/(:any)/(:any)/(:any) $1/photo/$2/$3/$4/$5/$6 306 | photo/(:any)/(:any)/(:any)/(:any)/(:any) $1/photo/$2/$3/$4/$5 307 | photo/(:any)/(:any)/(:any)/(:any) $1/photo/$2/$3/$4 308 | photo/(:any)/(:any)/(:any) $1/photo/$2/$3 309 | photo/(:any)/(:any) $1/photo/$2 310 | photo/(:any) $1/photo 311 | ``` 312 | 313 | ### Block routes 314 | 315 | Not very useful now that Codeigniter only has static routes with this library, but the developer can also block manually routes. 316 | 317 | 318 | Route::block('user/(:any)'); 319 | 320 | Will generate a blank route to this uris that, eventually, will led to a 404 display error. 321 | 322 | 323 | ### Additional route files 324 | 325 | The library will search for a folder named "routes" at "application/". Also will open all files listed inside it, so if the developer want's to have more than one file to keep an organized route file structure he can deploy the files there and all will be processed by the Routes library. 326 | 327 | Important note: the `Route::map()` call method must be always placed only at the "/application/config/routes.php" file. 328 | 329 | ## Change Log 330 | 331 | ### 1.5.2 332 | * Added support for $options parameter to "resources" method. 333 | 334 | ### 1.5.1 335 | * Added support for anonymous functions directly into route filter definition. 336 | 337 | ### 1.5: 338 | * Added support for subdomains 339 | * Added "routes" folder that lets developer use more than one file to storage routes. 340 | * Improved route naming functionalities. 341 | * Added support to send parameters to filters. 342 | 343 | ### 1.0: 344 | * First release of the system. Added static routes, filter hook system and naming routes system. 345 | --------------------------------------------------------------------------------