In Recess a is an object responsible for taking a web request, invoking the intended logic in a Model, and then selecting a View which will generate the response, likely in HTML, to be sent back to the client.
Having kicked-off a HelloWorldApplication in Recess Tools, let's take a quick look at some basic concepts.
In an apps/helloWorld directory there is a subdirectory controllers where Controllers are placed. Creating a new controller involves creating a new PHP file containing a class which extends Controller. Here is an example:
The file will be named HelloWorldController.class.php. The .class.php extension is important in Recess because it implies to the Recess Library that the file has only classes defined in it.
Let's add some functionality to the controller. We will begin by adding a simple method to print 'Hello World' and exit.
Example 4.3. Hello World! in Recess
<?php class HelloWorldController extends Controller { function printIt() { print 'Hello World!'; exit; } } ?>
If we now navigate to helloWorld/printIt we will see 'Hello World!' printed. How did the request for helloWorld/printIt wind up getting mapped to the printIt method? This process is called Routing.
Routing is the subsystem in Recess that maps a URL to a method in your controller. For more information on routing details refer to this article on Routing.
An HTTP request contains a variety of information: variables, headers, cookies, a URL, etc. Routing takes care of mapping an HTTP method (GET/POST/PUT/DELETE) to a controller method. Within your controller method you'll likely need to perform logic based on information in the request. Controllers have a Request object which can be referenced using $this->request. Let's take a look:
Example 4.4. Printing the Request object from a simple Controller
<?php class HelloWorldController extends Controller { function printIt() { print $this->request->resource . '<br />'; print_r($this->request); exit; } } ?>
Now refresh helloWorld/printIt in your browser. If the output runs together in one line view the source. These are the variables available on the Request object. For example, $this->request->resource is '/helloWorld/printIt'. Try navigating to 'helloWorld/printIt?foo=bar' and notice how $this->request->get['foo'] is set to 'bar'. In a Request object member variables get, post, and put hold the variables passed into the request when using the GET, POST, or PUT method like PHP's $_GET and $_POST.
A Controller is responsible for indicating which view template to use. If no response is returned from a controller method the default view template will be nameOfTheControllerMethod.php in the views/ directory. Try removing the body of the printIt method and refresh to get an error indicating no view template at helloWorld/views/printIt.php can be found. To change the view template file we can use the ok helper method.
Example 4.5. Returning an HTTP OK response and hinting the view template.
<?php class HelloWorldController extends Controller { function printIt() { return $this->ok('the-view'); } } ?>
After refreshing the error message will change to indicate the new location helloWorld/views/the-view.php. The ok helper method will be discussed soon. Let's create a new file named the-view.php with the content below and save it into the views folder:
Example 4.6. A simple view template.
<html>
<head><title>Hello World View</title></head>
<body>
<?php echo 'Hello World!'; ?>
</body>
</html>Try refreshing, you should see 'Hello World!'. Lets pass some variables from the Controller to the View Template.
Example 4.7. Passing data from a controller to a view template.
<?php class HelloWorldController extends Controller { function printIt() { $this->message = 'Hello World'; $this->repeat = 10; return $this->ok('the-view'); } } ?>
Example 4.8. A view template taking input from a controller.
<html>
<head><title>Hello World View</title></head>
<body>
<?php for($i = 0; $i < $repeat; $i++): ?>
<?php echo $message, '<br />'; ?>
<?php endfor; ?>
</body>
</html>Refresh to see 'Hello World!' printed 10 times. How do those variables propagate to the view template? The public instance variables in a controller are copied into the Response object which gets passed to a View, the View then sets those variables in the context of the view template. This process is vaguely similar to the Memento design pattern.
When writing view templates a great trick to quickly see what variables are available is to force an error in the view and look at the Recess Diagnostics screen. Try replacing the for loop in your view template with this code:
Example 4.9. An error in a view template invokes Recess Diagnostics
<html>
<head><title>Hello World View</title></head>
<body>
<?php echo $fail; ?>
</body>
</html>This will trigger an undefined variable error in PHP that will bring up Recess Diagnostics. On the Diagnostics screen there is a 'Context' table that shows all of the variables available in the local context. Here we can see $message and $repeat set as well as some other variables, one of which is the Response object.
Controller methods are allowed to return either nothing at all or a Response object. When a controller method does not return anything Recess assumes an OkResponse is intended with a view template that has the same name as the controller method. The 'Ok' prefix of OkResponse corresponds to the HTTP 200 OK response code. A Recess Response object contains the information Recess needs to respond to a request including: the response code, data to be passed to the view, headers to be sent back, cookies, a reference to the request, and some additional meta data used by Recess.
In the base AbstractController class there are a number of helper methods which will import and instantiate a response for you. The ok method is an example of a helper method which returns an OkResponse. Other helpers include: conflict, redirect, forwardOk, forwardNotFound, created, and unauthorized. The forwarding responses are a special kind of Response.
A ForwardingResponse causes Recess to handle another request and sends the body of that response to the client. For example, imagine you would like to build a PHP REST interface for creating Posts. After creating a Post you would like to send a 201 CREATED response that contains a Location header informing the client where to find that resource. For web browsers you likely want to send back meaningful content, perhaps the new list of Posts. In Recess this would look like:
Example 4.10. Responding with a ForwardingResponse
<?php class PostsController extends Controller { /** !Route POST, /posts */ function insertPost() { $post = Make::a('Post')->copy($this->request->data('Post'))->insert(); return $this->created('/post/' . $post->id, '/posts'); } /** !Route GET, /posts */ function listPosts() { $this->posts = Make::a('Post')->all(); } /** !Route GET, /posts/$id */ function showPost($id) { $this->post = Make::a('Post')->equal('id', $id)->first(); } } ?>
The important line is $this->created('/post/' . $post->id, '/posts'); The created helper method takes two arguments, the first is the URL to the created resource that will be sent in the Location header, the second is the URL to the 'content' to respond with. In this case the REST resource created is at /posts/$id but the response will render the HTML for the list of all posts at /posts.
Introducing dependencies on specific URLs in your controllers (and views!) is a bad practice because these URLs may change due to refactoring. Recess decouples this knowledge by providing a helper method that returns the URL to a controller method. Lets take another stab at the PostsController using urlTo.
Example 4.11. Using the urlTo method in PostsController
<?php class PostsController extends Controller { /** !Route POST, /posts */ function insertPost() { $post = Make::a('Post')->copy($this->request->data('Post'))->insert(); return $this->created( $this->urlTo('showPost', $post->id), $this->urlTo('listPosts')); } /** !Route GET, /posts */ function listPosts() { $this->posts = Make::a('Post')->all(); } /** !Route GET, /posts/$id */ function showPost($id) { $this->post = Make::a('Post')->equal('id', $id)->first(); } } ?>
The urlTo helper method will return the URL which maps to the controller method passed as an argument. Notice that methods which take parameters must be passed the parameters as subsequent arguments as shown in urlTo('showPosts', $post->id). Now if we change URLs using the relative routing techniques shown in the Routing Screencast we do not have to find all of the points where that URL was referenced. Also, if the name of a method changes and urlTo('thatMethod') is called Recess will throw an error which simplifies debugging.
Controllers in Recess simplify the process of accepting a request, delegating to application logic in Models, and passing off responsibility for responding to a view all in a RESTful manner. The conventions of selecting a view name based on the controller method name and returning a 200 OK response by default can be overriden with ease using helper methods. Finally, the urlTo method helps keep controllers and views DRY.