Remove documentation and point links in README to wiki

This commit is contained in:
PJ Dietz 2014-08-02 17:42:40 -04:00
parent 75088499b8
commit 4a6f678bd3
5 changed files with 4 additions and 542 deletions

View File

@ -10,7 +10,7 @@ Version 2
It's more RESTed than ever!
Version 2 brings a lot improvements over 1.x, but it is **not backwards compatible**. See [Changes from Version 1](documentation/changes-from-version-1.md) if you are migrating from a previous 1.x version of WellRESTed.
Version 2 brings a lot improvements over 1.x, but it is **not backwards compatible**. See [Changes from Version 1](wiki/cChanges-from-Version-1) if you are migrating from a previous 1.x version of WellRESTed.
Requirements
------------
@ -60,7 +60,7 @@ $myRouter->addRoutes(array(
$myRouter->respond();
```
See [Routes](documentation/routes.md) to learn about the various route classes.
See [Routes](wiki/Routes) to learn about the various route classes.
### Building Routes with JSON
@ -134,8 +134,8 @@ class CatsCollectionHandler extends \pjdietz\WellRESTed\Handler
}
```
See [Handlers](documentation/handlers.md) to learn about the subclassing the [`Handler`](src/pjdietz/WellRESTed/Handler.php) class.
See [HandlerInteface](documentation/handler-interface.md) to learn about more ways build completely custom classes.
See [Handlers](wiki/Handlers) to learn about the subclassing the [`Handler`](src/pjdietz/WellRESTed/Handler.php) class.
See [HandlerInteface](wiki/HandlerInterface) to learn about more ways build completely custom classes.
### Responses

View File

@ -1,103 +0,0 @@
# Changes from Version 1.x
WellRESTed 2 brings a streamlined API and extra flexibility. In order to make the kinds of improvements I wanted to make, I had to take a sledge hammer to backwards compatabiliy. If you're project is using a 1.x version, please be sure to set you Composer file to use 1.x until you are ready to migrate.
```json
{
"require": {
"pjdietz/wellrested": "1.*"
}
}
```
This is not a comprehensive list, but here I'll outline some of the major departures from version 1 to help you port to the new version.
## Routes
Routes are redesigned for version 2.
### URI Templates
In version 1, `Route` included a static method for creating a route using a URI template. Version 2 has a specific class for URI template routes.
**Version 1**
```php
$route = Route::newFromUriTemplate('/things/', 'ThingsHandler');
```
**Version 2**
```php
$route = new TemplateRoute('/things/', 'ThingsHandler');
```
### Regular Expressions
Version 1's `Route` expected you to use regular expressions. To do this in version 2, use the `RegexRoute`.
**Version 1**
```php
$route = new Route('/^\/cat\//', 'CatHandler');
```
**Version 2**
```php
$route = new RegexRoute('/^\/cat\//', 'CatHandler');
```
Version 2 also includes the `StaticRoute` class for when you want to match on an exact path.
```php
$route = new StaticRoute('/cat/', 'CatHandler');
```
See [Routes](routes.md) for more information.
## Interfaces
I whittled the number of interfaces down to three:
- [`HandlerInterface`](../src/pjdietz/WellRESTed/Interfaces/HandlerInterface.php)
- [`RequestInterface`](../src/pjdietz/WellRESTed/Interfaces/RequestInterface.php)
- [`ResponseInterface`](../src/pjdietz/WellRESTed/Interfaces/ResponseInterface.php)
(`RoutableInterface`, `RouteInterface`, `RouteTargetInterface`, `RouterInterface` are all removed.)
Version 2's design is centered around [`HandlerInterface`](../src/pjdietz/WellRESTed/Interfaces/HandlerInterface.php). This new approach both simplifies the API, but also adds a great deal of flexibility.
See [HandlerInterface](handler-interface.md) to learn more.
## No Magic Accessors
I removed the magic property methods so that I could make better use of interfaces. This means you'll need to use accessors where you previously could have used properties.
**Version 1**
```php
$request->statusCode = 200;
```
**Version 2**
```php
$request->setStatusCode(200);
```
## Making Requests
I moved the cURL functionality that allows you to make a request out of the `Request` class and into [`Client`](../src/pjdietz/WellRESTed/Client.php).
**Version 1**
```php
// Prepare a request.
$rqst = new Request('http://my.api.local/resources/');
// Make the request.
$resp = $rqst->request();
```
**Version 2**
```php
// Prepare a request.
$rqst = new Request('http://my.api.local/resources/');
// Make the request.
$client = new Client();
$resp = $client->request(rqst);
```

View File

@ -1,107 +0,0 @@
# HandlerInterface
Much of WellRESTed 2 centers around the [`HandlerInterface`](../src/pjdietz/WellRESTed/Interfaces/HandlerInterface.php) which has only one method that you need to implement:
```php
/**
* @param RequestInterface $request The request to respond to.
* @param array|null $args Optional additional arguments.
* @return ResponseInterface The handled response.
*/
public function getResponse(RequestInterface $request, array $args = null);
```
## Hello, World!
Here's a really simplistic example of a "hello world" handler.
```php
class HelloWorldHandler implements HandlerInterface
{
public function getResponse(RequestInterface $request, array $args = null)
{
$response = new Response(200, "Hello, world!");
return $response;
}
}
```
You can plug this into a [`Router`](../src/pjdietz/WellRESTed/Router.php), and the router will always respond with "Hello, world!".
```php
$router = new Router();
$router->addRoute(new HelloWorldHandler());
$router->respond();
```
### But there's no route?
Here's the cool thing about how routing works in WellRESTed 2: The route classes implement [`HandlerInterface`](../src/pjdietz/WellRESTed/Interfaces/HandlerInterface.php). When the `Router` iterates through its list of routes, it calls `getResponse()` on each one until it gets a non-`null` return value. At this point, it returns that response (or outputs it in the case of `Router::respond()`).
Each time the router calls `getResponse()` on a route that doesn't match request, the route returns `null` to indicate that something else will need to handle this.
Let's add another class to demonstrate.
```php
class DoNothingHandler implements HandlerInterface
{
public function getResponse(RequestInterface $request, array $args = null)
{
// THE GOGGLES DO NOTHING!
return null;
}
}
```
```php
$router = new Router();
$router->addRoute(new DoNothingHandler());
$router->addRoute(new HelloWorldHandler());
$router->respond();
```
This router will still always respond with "Hello, world!" even though the router will try `DoNothingHandler` first because `DoNothingHandler` returns `null`.
### 404'ed!
If none of the routes in a router return a non-`null` value, what happens?
If you're calling `Router::respond()`, you will **always** get a response. `Router::respond()` is a shorthand method that will output the response made in `Router::getNoRouteResponse()` if it gets through its entire route table and finds no matches.
If you want to customize the default 404 response, you can either subclass `Router` and redefine `Router::getNoRouteResponse()`, or you can create a [`HandlerInterface`](../src/pjdietz/WellRESTed/Interfaces/HandlerInterface.php) like our `HelloWorldHandler` that always returns a response with a `404 Not Found` status code and add it to the router **last**. (Remember: a router evaluates its routes in the order you add them.)
```php
class NotFoundHandler implements HandlerInterface
{
public function getResponse(RequestInterface $request, array $args = null)
{
$response = new Response(400);
$response->setBody("No resource at " $request->getPath());
return $response;
}
}
```
```php
$router = new Router();
$router->addRoute(/*...Real route... */);
$router->addRoute(/*...Real route... */);
$router->addRoute(/*...Real route... */);
$router->addRoute(new NotFoundHandler());
$router->respond();
```
## Nested Routers
`Router::respond()` is a shorthand method that wraps `Router::getResponse()`, which [`Router`](../src/pjdietz/WellRESTed/Router.php) must have because it too implements [`HandlerInterface`](../src/pjdietz/WellRESTed/Interfaces/HandlerInterface.php). This means that you can break your router into subrouters.
```php
$router = new Router();
$router->addRoutes(array(
new TemplateRoute("/cats/*", "CatRouter"),
new TemplateRoute("/dogs/*", "DogRouter"),
new NotFoundHandler()
));
$router->repond();
```

View File

@ -1,171 +0,0 @@
# Handlers
[`Handler`](../src/pjdietz/WellRESTed/Handler.php) is an abstract base class for you to subclass to create controllers for generating responses given requests.
## Instance Members
Your [`Handler`](../src/pjdietz/WellRESTed/Handler.php) subclass has access to three protected members:
Member | Type | Description
---------- | ---- | -----------
`args` | `array` | Map of variables to supplement the request, usually path variables.
`request` | [`RequestInterface`](../src/pjdietz/WellRESTed/Interfaces/RequestInterface.php) | The HTTP request to respond to.
`response` | [`ResponseInterface`](../src/pjdietz/WellRESTed/Interfaces/ResponseInterface.php) | The HTTP response to send based on the request.
## HTTP Verbs
Most of the action takes place inside the methods called in response to specific HTTP verbs. For example, to handle a `GET` request, implement the `get` method.
```php
class CatsCollectionHandler extends \pjdietz\WellRESTed\Handler
{
protected function get()
{
// Read some cats from the database, cache, whatever.
// ...read these an array as the variable $cats.
// Set the values for the instance's response member. This is what the
// Router will eventually output to the client.
$this->response->setStatusCode(200);
$this->response->setHeader("Content-Type", "application/json");
$this->response->setBody(json_encode($cats));
}
}
```
Implement the methods that you want to support. If you don't want to support `POST`, don't implement it. The default behavior is to respond with `405 Method Not Allowed` for most verbs.
The methods available to implement are:
HTTP Verb | Method | Default behavior
--------- | --------- | ----------------------
`GET` | `get` | 405 Method Not Allowed
`HEAD` | `head` | Call `get`, then clean the response body
`POST` | `post` | 405 Method Not Allowed
`PUT` | `put` | 405 Method Not Allowed
`DELETE` | `delete` | 405 Method Not Allowed
`PATCH` | `patch` | 405 Method Not Allowed
`OPTIONS` | `options` | Add `Allow` header, if able
### `OPTIONS` requests and `Allowed` headers
An `OPTIONS` request to your endpoint should result in the API responding with an `Allow` header listing the verbs the endpoint supports. For example:
```
HTTP/1.1 200 OK
Allow: GET, HEAD, POST, OPTIONS
Content-Length: 0
```
To support `OPTIONS` requests, implement `getAllowedMethods` and return an array of the methods you support. For a handler that supports the methods in the example response, do this:
```php
protected function getAllowedMethods()
{
return array("GET", "HEAD", "POST", "OPTIONS");
}
```
You do not need to implement `options`. `options` by default calls `getAllowedMethods`. If it gets a return value, it sets the status code to `200 OK` and adds the `Allow` header. Otherwise, it responds `405 Method Not Allowed`.
### Custom Verbs
To support custom verbs, redefine the `buildResponse`. To respond to the custom verb `SNIFF`, to this:
```php
protected function buildResponse()
{
switch ($this->request->getMethod()) {
case 'SNIFF':
// Assuming you also made a sniff() method...
$this->sniff();
break;
default:
// Let the parent's method do the rest.
self::buildResponse();
}
}
```
## HttpExceptions
Another useful feature of the [`Handler`](../src/pjdietz/WellRESTed/Handler.php) class is that it catches exceptions deriving from [`HttpException`](../src/pjdietz/WellRESTed/Exceptions/HttpExceptions.php) and turns them into responses. [`HttpException`](../src/pjdietz/WellRESTed/Exceptions/HttpExceptions.php) and its subclasses provide the status code and description for simple error responses.
For example, you can throw a `NotFountException` if the resource the request indicates does not exist.
```php
use \pjdietz\WellRESTed\Handler;
use \pjdietz\WellRESTed\Exceptions\HttpExceptions\NotFoundException;
class CatsCollectionHandler extends Handler
{
protected function get()
{
// Lookup a cat by ID.
$cat = Cat::getById($this->args["id"]);
if (!$cat) {
throw new NotFoundException();
}
$this->response->setStatusCode(200);
$this->response->setHeader("Content-Type", "application/json");
$this->response->setBody(json_encode($cat));
}
}
```
Your [`Handler`](../src/pjdietz/WellRESTed/Handler.php) will automatically turn this into a `404 Not Found` response.
Here are the available [`HttpException`](../src/pjdietz/WellRESTed/Exceptions/HttpExceptions.php) classes:
Response Code | Class
--------------------------- | -----------------------
`400 Bad Request` | `BadRequestException`
`401 Unauthorized` | `UnauthorizedException`
`403 Forbidden` | `ForbiddenException`
`404 Not Found` | `NotFoundException`
`409 Conflict` | `ConflictException`
`500 Internal Server Error` | `HttpException`
You can also create your own by subclass [`HttpException`](../src/pjdietz/WellRESTed/Exceptions/HttpExceptions.php) and setting the exception's `$code` to the status code and `$messge` to a default message.
## Custom Base Handler
When building your API, you may want to subclass [`Handler`](../src/pjdietz/WellRESTed/Handler.php) with your own abstract class that adds methods for authenticaion, supports some extra verbs, presents custom errors, adds addiitonal headers, etc. Then, you can derive all of your concrete handlers from that class.
```php
<?php
abstract class MyHandler extends \pjdietz\WellRESTed\Handler
{
protected function buildResponse()
{
try {
// Add support for a custom HTTP verb.
switch ($this->request->getMethod()) {
case 'SNIFF':
$this->sniff();
break;
default:
self::buildResponse();
}
} catch (UnauthorizedException $e) {
// Catch 401 errors and call a method to do something with them.
$this->responseToUnauthorized($e);
}
// Add a header to all responses.
$this->response->addHeader("X-Custom-Header", "Hello, world!");
}
abstract protected function sniff();
protected function responseToUnauthorized(HttpException $e)
{
$this->response->setStatusCode($e->getCode());
$this->response->setBody("Y U NO SEND CREDENTIALS?");
}
}
```

View File

@ -1,157 +0,0 @@
# Routes
WellRESTed comes with a few route classes:
- [`StaticRoute`](../src/pjdietz/WellRESTed/Routes/StaticRoute.php): Matches request paths exactly
- [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php): Matches URI templates
- [`RegexRoute`](../src/pjdietz/WellRESTed/Routes/RegexRoute.php): Matches a custom regular expression
Each works basically the same way: It first checks to see if it is a match for the request. If it's a match, it instantiates a specific class implementing the [`HandlerInterface`](../src/pjdietz/WellRESTed/Interfaces/HandlerInterface.php) (autoloading the class, if needed). Finally, it uses the handler class to provide a response.
## StaticRoute
Use a [`StaticRoute`](../src/pjdietz/WellRESTed/Routes/StaticRoute.php) when you know the exact path you want to handle. This route will match only requests to `/cats/`.
```php
$route = new StaticRoute("/cats/", "CatHandler");
```
You can also make a [`StaticRoute`](../src/pjdietz/WellRESTed/Routes/StaticRoute.php) that matches multiple exact paths. For example, suppose you have a multi-use `AnimalHandler` that you want to invoke to handle requests for `/cats/`, `/dogs/`, and `/birds/`. You can make this by passing an array instead of a string as the first parameter.
```php
$route = new StaticRoute(array("/cats/", "/dogs/", "/birds/"), "AnimalHandler");
```
## TemplateRoute
[`StaticRoute`](../src/pjdietz/WellRESTed/Routes/StaticRoute.php) is the best choice if you know the exact path up front. But, what if you want to handle a path that includes a variable? That's where the [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php) comes in.
Here's a route that will match a request to a specific cat by ID and send it to a `CatItemHandler`.
```php
$route = new TemplateRoute("/cats/{id}", "CatItemHandler");
```
This will match `/cats/1`, `/cats/99`, `/cats/molly`, etc.
A [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php) uses a URI template to match a request. To include a variable in your template, enclose it in `{}`. The variable will be extracted and made available for the handler in the handler's `args` member.
```php
class CatItemHandlder extends \pjdietz\WellRESTed\Handler
{
protected function get()
{
// Access the {id} variable from the $this->args member.
$id = $this->args["id"];
// ...Do something with the {id}.
}
}
```
For the paths `/cats/1`, `/cats/99`, `/cats/molly`, the value of `$this->args["id"]` will be `"1"`, `"99"`, or `"molly"`.
Your template may have multiple variables. Be sure to give each a unique name.
Here the handler will have access to `$this->args["catId"]` and `$this->args["dogId"]`.
```php
$route = new TemplateRoute("/cats/{catId}/{dogId}", "CatItemHandler");
```
### Default Variable Pattern
By default, the [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php) will accept for a variable any value consisting of numbers, letters, underscores, and hyphens. You can change this behavior by passing a pattern to use as the third parameter of the constructor. Here we'll restrict the template to match only numeric values.
```php
$route = new TemplateRoute("/cats/{id}", "CatItemHandler", TemplateRoute::RE_NUM);
```
This will match `/cats/1` or `/cats/99`, but NOT `/cats/molly`.
### Pattern Constants
The [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php) includes constants for some common situations. The value of each constant is a partial regular expression. You can use one of the constants, or provide your own partial regular expression.
| Constant | Pattern | Description |
| ------------ | ----------------- | ----------- |
| `RE_SLUG` | `[0-9a-zA-Z\-_]+` | **(Default)** "URL-friendly" characters such as numbers, letters, underscores, and hyphens |
| `RE_NUM` | `[0-9]+` | Digits only |
| `RE_ALPHA` | `[a-zA-Z]+` | Letters only |
| `RE_ALPHANUM` | `[0-9a-zA-Z]+` | Letters and digits |
### Variable Patterns Array
You can also set a different pattern for each variable. To do this, pass an array to the [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php) constructor as the fourth parameter. The array must have variable names as keys and patterns as values.
```php
$patterns = array(
"id" => TemplateRoute::RE_NUM,
"name" => TemplateRoute::RE_ALPHA
);
$route = new TemplateRoute(
"/cats/{id}/{name}/{more}",
"CatItemHandler",
TemplateRoute::RE_ALPHANUM,
$patterns);
```
Here, `{id}` will need to match digits and `{name}` must be all letters. Since `{more}` is not explicitly provided in the `$patterns` array, it uses the default `TemplateRoute::RE_ALPHANUM` passed as the third parameter.
### Wildcard
If you want to match all requests with paths that start with a given template, end your template with `*`. This is useful for handing groups of requests off to subrouters.
```php
$route = new TemplateRoute("/cats/*", "CatRouter");
```
This will match `/cats/`, `/cats/21`, `/cats/with/extra/path/components/`, etc.
The `*` wildcard may only appear at the **end** of your template.
## RegexRoute
If [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php) doesn't give you enough control, you can make a route that matches a regular expression using a [`RegexRoute`](../src/pjdietz/WellRESTed/Routes/RegexRoute.php).
```php
$route = new RegexRoute("~/cat/[0-9]+~", "CatHandler")
```
This will match `/cat/102`, `/cat/999`, etc. To make this more useful, we can add a capture group. The captures are made available to the handler as the `$args` member, as with the URI template variables for the [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php)
Note that the entire matched path will always be the `0` item, and captured groups will begin at `1`.
So this route...
```php
$route = new RegexRoute("~/cat/([0-9]+)~", "CatHandler")
```
...with the path `/cat/99` creates this array of matches:
```
Array
(
[0] => /cat/99
[1] => 99
)
```
You can also used named capture groups like this:
```php
$route = new RegexRoute("~/cat/(?<id>[0-9]+)~", "CatHandler")
```
The path `/cat/99` creates this array of matches:
```
Array
(
[0] => /cat/99
[1] => 99
[id] => 99
)
```