joomla/joomla-platform — pull request #1259 by LouisLandry (2012-06-07 09:56:49+00:00)
Title: New (and generic) web application routers.

I still need to write some documentation, but hopefully the code is simple and clean enough to understand clearly. There are three new classes: `JApplicationWebRouter`, `JApplicationWebRouterBase` and `JApplicationWebRouterRest`. These are not meant to be one size fits all. They are meant to provide a solid foundation for what is hopefully the majority of use cases in URL routing.

### JApplicationWebRouter
The foundational web router class is abstract. The premise of the class is that some sort of route string would be passed into the execute method, that string would be parsed within the `parseRoute()` method to determine the name of the appropriate controller to execute, then `fetchController()` would be called to actually instantiate the controller object and return it. Once that is done then the controller is simply executed.

I've purposefully not provided an implementation of `parseRoute()` in the base class because there are infinite different ways a route can be examined to determine how to proceed. Other features of the class include the ability to set a default controller name if an empty route was given, and also the ability to set a controller class prefix that would be prepended to any controller name returned from `parseRoute()`.

### JApplicationWebRouterBase
The basic concrete implementation of `JApplicationWebRouter` provides a very simple algorithm to parse routes based on a `pattern => controller` mapping. The premise is that if an incoming route matches the given pattern than the associated controller will be returned and ultimately executed. It is important to note that for this implementation the pattern is not a regular expression. The route and patterns are broken down into segments split over `/` and evaluated segment by segment. There must be an exact match for all segments to constitute the route matching the pattern.

For example if we have pattern `continents/europe`:

* The route `continents/europe` is a match. [Every segment matches]
* The route `continents/asia` is **NOT** a match. [The second segment asia does not match europe]
* The route `continents/europe/germany` is also **NOT** a match. [The final segment germany is not in the pattern]

#### Variables
Along with standard string comparison for route segments, patterns can include variables by prefixing the segment with a `:`. To continue with our previous example let's make the actual continent a variable.

The new pattern is `continents/:continent`:

* The route `continents/europe` is a match. [Every segment matches because europe is in a variable segment]
* The route `continents/asia` is **ALSO** a match. [Again, asia is in a variable segment]
* The route `continents/europe/germany` does **NOT** a match. [The final segment germany is still not in the pattern even though the europe segment works since it is in a variable segment]

A side effect of using variables is that the variable name (everything in the segment except the opening `:`) is added to the application input object with the value found in a successful route match. Using the same example as above; pattern `continents/:continent`:

* The route `continents/europe` matches and the application input object will have `europe` set for input variable `continent`.
* The route `continents/asia` matches and the application input object will have `asia` set for input variable `continent`.

### JApplicationWebRouterRest
The generic RESTful router extends `JApplicationWebRouterBase` and inherits the same pattern matching with variables as discussed above. The only addition is that `JApplicationWebRouterRest` also maintains a map of HTTP Methods [GET, POST, PUT, etc.] to controller class suffixes. This is used so that you can namespace your controllers based on the type of action being performed on the resource. As an example if we have an application that manages resources called "articles" we would only need two pattern rules: one for with an article ID and one for without.

* `articles` => `articles` - If no ID is present.
* `articles/:article_id` => `articles` - If an article ID is present.

Let's also set the controller prefix to `MyController` since that happens to be my application's prefix for finding controllers.

Now all we need are the following classes since the `JApplicationWebRouterRest` router is going to take care of the HTTP Method mapping for us.

* `MyControllerArticlesCreate` - `POST /articles` - Create a new article.
* `MyControllerArticlesDelete` - `DELETE /articles/42` - Delete article 42.
* `MyControllerArticlesGet - `GET /articles/7` - Get article 7.
* `MyControllerArticlesUpdate` - `PUT /articles/7` - Save an update to article 7.