On Friday, 3 August 2012 10:13:04 UTC-4, ADmad wrote:
> Mark, will Router::url() will now be able to generate full urls without
> protocol like "//somedomain.com/foo/bar" ?
> On Monday, July 23, 2012 7:34:37 AM UTC+5:30, mark_story wrote:
>> The Router is an important, powerful and frequently used piece of
>> CakePHP. It is not without its problems though. For 3.0, I've spent
>> sometime fixing many of the problems in router and adding missing
>> functionality.
>> ### Problems
>> * Reverse routing is slow. With many connected routes, reverse routing
>> can be slow, as its a linear search across all connected routes.
>> * Named parameters are a mess. They are an unconventional way to build
>> URL's that aren't implemented or supported by any other tools. They break
>> the ideas of 'convention over configuration', as they are not a convention
>> shared anywhere else on the internet. This makes it difficult for other
>> tools to parse and generate. In addition to this, named parameters incur
>> some overhead when routes are matched, as various filtering steps are
>> required.
>> * Prefixes are poorly implemented. I feel that prefixes could be better
>> implemented as sub-namespaces. This would result in smaller, easier to
>> test code as admin/other methods are separate and contained.
>> #### Named routes
>> Named routes, even if the names are not entirely unique, can speed up
>> reverse routing. By reducing the set of routes that needs to be traversed.
>> Using a bucketed array, routes with the same name could be put into sets
>> that could be traversed. If no names are provided, a linear search would
>> be done.
>> Names could be explicit, or generated based on the route template.
>> <?php
>> // Explicit name.
>> Router::connect('/:controller/:action/*', [], ['_name' =>
>> '_controller::_action']);
>> Router::connect('/login', ['controller' => 'users', 'action' => 'login'],
>> ['_name' => 'login']);
>> // Generated name. Results in 'posts::_action'
>> Router::connect('/posts/:id/:action', ['controller' => 'posts']);
>> Generated names use the controller + action to create a name. If a
>> controller action has multiple routes that point to it, those routes are
>> added to a single collection. When reverse routing only the routes for a
>> given plugin.controller::action are considered. In many cases this will
>> only match against one route. Routes also fallback in a reliable order.
>> If `['controller' => 'posts', 'action' => 'index']` was matched the
>> following keys would be attempted:
>> * posts:index
>> * posts:_action
>> * _controller:_action
>> Plugin routes fallback in a similar fashion but with the Plugin prefix
>> always present. For example:
>> * assetcompress.asset:get
>> * assetcompress.asset:_action
>> * assetcompress._controller:_action
>> * _controller:_action
>> ### Special route keys
>> Currently url arrays support a few special keys. The special keys should
>> be expanded to contain the following:
>> * `ssl` - Set to true. Basically a shortcut for `_scheme => https`. Can
>> be set to false to convert the scheme to http.
>> * `_scheme` - Defaults to the current request's scheme. Would be used
>> for creating https, or webcal links for example.
>> * `_host` - The host to use, defaults to the current host.
>> * `_port` - Defaults to the current requests' port. Would be used for
>> running SSL on un-conventional ports.
>> * `_full` - Set to true to include the full protocol, port and domain.
>> (Defaults to false)
>> * `_base` - Set to false to return an application relative path.
>> Application relative paths are missing the subdirectory the application is
>> running in. This is useful for making sub requests.
>> * `_ext` - The extension used for the url ie. `json` for `.json` urls.
>> * `#` - Create fragment identifiers.
>> All special keys except `#` are prefixed with a `_`. This is to prevent
>> overlap with userland route elements. As long as none of the following
>> keys are used generated urls will be domain relative (ie.
>> `/dir/controller/action`) `_scheme`, `_port`, `_host`, `_full`.
>> ### Reverse Routing examples
>> <?php
>> // foo is unknown, and not a route element.
>> // This url uses the _controller:_action default route.
>> // so the extra params become a query string parameter.
>> Router::url(['controller' => 'posts', 'action' => 'index', 'foo' =>
>> 'bar']);
>> // Creates /posts/index?foo=bar
>> // Create a url for App\Controller\Admin\PostsController.
>> Router::url([
>> 'prefix' => 'admin',
>> 'controller' => 'posts',
>> 'action' => 'index',
>> 'lang' => 'en'
>> ]);
>> // Creates /admin/posts/en/index
>> // Use a name for the route.
>> Router::url('login');
>> Router::url() has two valid signatures:
>> <?php
>> Router::url($name, $params);
>> Router::url($params);
>> Using named routes lets you more tersely express routes that would
>> require long sets of parameters in normal routing arrays. Additionally
>> using unique names for all routes reduces the time to lookup routes to
>> almost nothing.
>> If no explicit name is used, a name will be generated based on the
>> routing parameters and used to look at a smaller route set. This will
>> speed up route lookups, but has the potential to incorrectly choose routes.
>> In these situations its best to use explicit names for routes.
>> ### Prefixed actions
>> Prefixed actions have historically been implemented as prefixes to method
>> names. I feel this has a few drawbacks:
>> * Action names must be munged and combined with prefixes to create
>> callable methods names.
>> * Prefixed actions have special code to protect them from direct URL
>> access.
>> * Prefixes have special code to handle them when generating urls.
>> * Prefixes require 2 parameters in calls to Router::connect().
>> * Prefixed actions are in the same class as the non-prefixed actions.
>> This creates enormous classes that are generally handling multiple aspects
>> of an application. Having both the admin and non-admin actions in the same
>> class increases the risk of accidentally making mistakes related to
>> privilege escalation. By using components, traits, and inheritance code
>> reuse (the main benefit of having prefixes in the same class) could be
>> achieved.
>> #### New implementation
>> Prefixes directly map to separate namespaces inside a plugin/app. A
>> routing key would be used to indicate the prefix. Possible options are
>> `_prefix`, `_ns`, `_namespace`, `prefix`, `namespace`.
>> <?php
>> // Route connection (using `prefix`)
>> Router::connect('/admin/:controller/:action/*', array('prefix' =>
>> 'admin'));
>> // generate a url
>> Router::url(['controller' => 'posts', 'action' => 'index', 'prefix' =>
>> 'admin']);
>> // The above resolves to.
>> App\Controller\Admin\PostsController::index()
>> Prefixes are `Inflector::camelize()` d before being combined into a
>> namespace. This allows for multi-word prefixes. Prefixes in plugins would
>> work in a similar way:
>> <?php
>> // Route connection (using `prefix`)
>> Router::connect(
>> '/admin/contacts/:controller/:action/*',
>> ['prefix' => 'admin', 'plugin' => 'contacts']
>> );
>> // generate a url
>> Router::url([
>> 'plugin' => 'contacts',
>> 'controller' => 'users',
>> 'action' => 'index',
>> 'prefix' => 'admin'
>> ]);
>> // The above resolves to.
>> Contacts\Controller\Admin\UsersController::index()
>> The `prefix` key could be set to `false` to force a route to generate as
>> a non-prefixed method. The `Routing.prefixes` Configure var would still be
>> available to declaratively configure which prefixes are going to be
>> connected by the default routes.
>> #### View files
>> All view files would be located in paths similar to the controllers. So
>> if your controller was `App\Controller\Admin\ContactsController` the view
>> files would be in `App/View/Admin/Contacts/` by convention. Of course you
>> can change this using `Controller::$viewPath`. Prefix controllers with
>> alternate formats would follow the convention of
>> `App/View/Admin/Contacts/{xml,json}/`.
>> I apologize for the long winded post, if anyone is interested in the
>> changes you can see the branch diff at:
>> https://github.com/cakephp/cakephp/compare/3.0...3.0-router
>> If anyone has any feedback, I'd love to hear it so I can integrate the
>> feedback and merge this into 3.0.
>> -Mark