Code Bright: Responses

← Back to Index

This title was written for Laravel version 4. If you're looking for material on the latest version of Laravel, then please check out Code Smart.

When someone asks you a question, unless you are in a mood or the question doesn't make sense, you will most likely give them a response. I guess other exceptions are those question-like greetings like when someone says, 'Alright?'. Even then, you should give them at least a nod in return...some form of response.

Requests made to a web server are no different than the guy that said 'Alright?'. They will hope to get something back. In a previous chapter our responses took the shape of strings returned from routed closures. Something like this:

<?php

// app/routes.php

Route::get('/', function()
{
    return 'Yeh am alright guv.';
});

So here we have the string “Yeh am alright guv.” sent back to the browser. The string is our response and is always returned by a routed closure, or a Controller action which we will cover later.

It would be fair to assume that we would like to send some HTML as our response. This is often the case when developing web applications.

I guess we could enclose the HTML in the response string?

<?php

// app/routes.php

Route::get('/', function()
{
    return '<!doctype html>
            <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <title>Alright!</title>
                </head>
                <body>
                    <p>This is the perfect response!</p>
                </body>
            </html>';
});

Awesome! Now you see the power and grace of Laravel… just kidding. We don't want to serve HTML this way. Embedding logic would get annoying and, more importantly, it's just plain wrong!

Luckily for us, Laravel has a number of Response objects that make returning a meaningful reply a whole lot easier. Let's check out the View response since that's the most exciting one!

Views

Views are the visual part of your application. Within the Model View Controller pattern they make up the View portion. That's why they are called views. It's not rocket science. Rocket science will be covered in a later chapter.

A view is just a plain text template that can be returned to the browser, though it's likely that your views will contain HTML. Views use the .php extension and are normally located within the app/views directory. This means that PHP code can also be parsed within your views. Let's just create a very simple view for now.

<!-- app/views/simple.php -->

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Views!</title>
</head>
<body>
    <p>Oh yeah! VIEWS!</p>
</body>
</html>

Great! A tiny bit of HTML stored in app/views/simple.php. Now let's make a view.

Didn't we just do that?

Haha! Yes you did little one, but I didn't mean 'Let's make another view file.'. Instead let's make() a new view response object based upon the view file we just created. The files representing views can be called templates. This may help you distinguish between the View objects and the HTML templates.

<?php

// app/routes.php

Route::get('/', function()
{
    return View::make('simple');
});

Oh, I see! You were on about the make() method.

Indeed I was little buddy! Using the View::make() method we can create a new instance of a View response object. The first parameter we hand to the make() method is the view template that we wish to use. You will notice that we didn't use the full path app/views/simple.php. This is because Laravel is clever. It will by default assume that your views are located in app/views and will look for a file with an appropriate view extension.

If you look a little closer at the Closure you will see that the View object we have created is being returned. This is very important since Laravel will serve the result of our Closure to the web browser.

Go ahead and try hitting the / URI for your application. Great, that's the template we wrote!

Later in the book you will learn how to make different types of templates that work with the View response to make your lives easier. For now we will stick to the basics to get a good grasp on the fundamental Laravel concepts.

View Data

Being able to show templates is awesome. It really is. What if we want to use some data from our Closure though? In an earlier chapter we learned how we can use Route parameters. Maybe we want to be able to refer to these parameters in the View? Let's take a look at how this can be done.

<?php

// app/routes.php

Route::get('/{squirrel}', function($squirrel)
{
    $data['squirrel'] = $squirrel;

    return View::make('simple', $data);
});

In the above route we take the $squirrel parameter and add it to our view data array. You see, the second parameter of the make() method accepts an array that is passed to the view template. I normally call my array $data to indicate that it is my view data array, but you may use any name you like!

Before we start using this data, let's talk a little more about the view data array. When the array is passed to the view, the array keys are 'extracted' into variables that have the array key as their name and the given value. It's a little confusing to explain without an example so let me simplify it for you.

Here we have an array to be handed to View::make() as view data.

<?php
array('name' => 'Taylor Otwell', 'status' => 'Code Guru')

In the resulting view template we are able to access these values like this:

<?php echo $name;           // gives 'Taylor Otwell' ?>

<?php echo $status;         // gives 'Code Guru' ?>

So our name array key becomes the $name variable within the template. You can store multi-dimensional arrays as deep as you like in your view data array. Feel free to experiment!

Let's use the $squirrel variable in the simple template we created earlier.

<!-- app/views/simple.php -->

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Squirrels</title>
</head>
<body>
    <p>I wish I were a <?php echo $squirrel; ?> squirrel!</p>
</body>
</html>

Now if we visit the URI /gray we receive a page stating 'I wish I were a gray squirrel!'.

Well that was simple wasn't it? Using views, you will no longer have to return strings from your Closures!

Earlier I mentioned that there are different types of response objects. In some circumstances you may wish to redirect the flow of your application to another route or logic portion. In such a circumstance the Redirect reponse object will be useful. See, Laravel’s got your back!

Redirects

A Redirect is a special type of response object which redirects the flow of the application to another route. Let’s create a couple of sample routes so that I can explain in more detail.

<?php

// app/routes.php

Route::get('first', function()
{
    return 'First route.';
});

Route::get('second', function()
{
    return 'Second route.';
});

Having added the above routes, you will receive ‘First route.’ upon visiting the /first URI and ‘Second route.’ upon visiting the /second URI.

Excellent, that’s exactly what we expected to happen. Now let’s completely shift the flow of the application by adding a redirect to the first routed closure.

<?php

// app/routes.php

Route::get('first', function()
{
    return Redirect::to('second');
});

Route::get('second', function()
{
    return 'Second route.';
});

In the first route we are now returning the result of the Redirect::to() method and passing the URI of the target location. In this case we are passing the URI for the second route second as the location.

If you now visit the /first URI you will be greeted with the text ‘Second route.’. This is because upon receiving the returned Redirect object, Laravel has shifted the flow of our application to the target destination. In this case the flow has been shifted to the closure of the second route.

This can be really useful when a condition of some kind has failed and you need to redirect the user to a more useful location. Here’s an example using the authentication system (which we will cover later) that will provide another use case.

<?php

// app/routes.php

Route::get('books', function()
{
    if (Auth::guest()) return Redirect::to('login');

    // Show books to only logged in users.
});

In this example, if a user who has not yet logged into the system visits the /books URI then they are considered a guest and would be redirected to the login page.

Later you will find a better way to limit access when we discuss route filters, so don’t read too much into the above example. Instead, just consider that we could redirect the user to a more sensible destination if our conditions are not met.

Custom Responses

Both View and Redirect inherit from the Laravel Response object. The response object is an instance of a class that can be handed back to Laravel as the result of a routed closure or a controller action to enable the framework to serve the right kind of response to the browser.

Response objects generally contain a body, a status code, HTTP headers, and other useful information. For example, the body segment of the View would be its HTML content. The status code for a Redirect would be a 301. Laravel uses this information to construct a sensible result that can be returned to the browser.

We aren’t merely limited to using View and Redirect. We could also create our own response object to suit our needs. Let’s have a look at how this can be done.

<?php

// app/routes.php

Route::get('custom/response', function()
{
    return Response::make('Hello world!', 200);
});

In the above example we use the Response::make() method to create a new response object. The first parameter is the content or body of the response and the second parameter is the HTTP status code that will be served with the response.

If you haven’t seen HTTP status codes before, then think of them as status notifications for the web browser receiving your page. For example, if all goes well, a standard response may contain a 200 status code, which is web-server speak for ‘A-OK’. A 302 status code indicates that a redirect has been performed.

In fact, I bet you have already come across the now infamous 404 not found page. The 404 part is the status code received by the browser when a requested resource could not be found.

Simply put, the above response will serve the content ‘Hello world!’ with a HTTP status code of 200 to let the browser know that its request was a success.

HTTP headers are a collection of key-value pairs of data which represent useful information to the web browser that is receiving the response. Normally they are used to indicate the format of the result or how long a piece of content should be cached for. However, we are able to define custom headers as we please. Let’s have a look at how we can set some header values.

<?php

// app/routes.php

Route::get('custom/response', function()
{
    $response = Response::make('Hello world!', 200);
    $response->headers->set('our key', 'our value');
    return $response;
});

We have created a sample response object just as we did in the previous example. However this time we have also set a custom header.

The collection of HTTP headers can be accessed as the headers property of the response object.

<?php

var_dump($response->headers);

Using the set() method on this collection we can add our own header to the collection by providing a key as a first parameter and the associated value as the second.

Once our header has been added we simply return the response object as we have done previously. The browser will receive the headers along with the response and can use this information however it wishes.

Let’s think of a more useful example. Hrrm... let’s instead imagine that we want our application to serve markdown responses instead of HTML. We know that a web browser would not be able to parse a markdown response, but we might have another desktop application that can.

To indicate that the content is markdown and not HTML we will modify the Content-Type header. The Content-Type is a common header key used by browsers to distinguish between the various formats of content that are sent to them. Don’t be confused! Let’s have an example.

<?php

// app/routes.php

Route::get('markdown/response', function()
{
    $response = Response::make('***some bold text***', 200);
    $response->headers->set('Content-Type', 'text/x-markdown');
    return $response;
});

Having set the body of the response object to some sample markdown, in this case some bold text, and the Content-Type header to the mime type for the Markdown plain text format, we have served a response that can be identified as Markdown.

Our desktop application can now make a request to the /markdown/response URI, examine the Content-Type header, and by receiving the text/x-markdown value it will know to use a markdown transformer to handle the body.

Now because we are all friends here I’m going to share a secret with you. Come closer. Get in here. Let’s have a huddle. The response object doesn’t actually belong to Laravel.

TREACHERY? WHAT MADNESS IS THIS?

Now don’t get too worked up. You see, to avoid a lot of ‘re-inventing the wheel’, Laravel has used some of the more robust components that belong to the Symfony 2 project. The Laravel response object actually inherits most of its content from the Response object belonging to the Symfony HTTPFoundation component.

What this means is that if we take a look at the API for the Symfony response object, suddenly we have access to a whole heap of extra methods that aren’t covered in the Laravel docs! Holy smokes! Now that I have given away this secret, there’s nothing to stop you from becoming a Laravel master!

The API documentation for the Symfony Response object can be found here. If you look at the page you will notice that the class has an attribute called $headers. That’s right! That’s the collection of headers we were using only a minute ago.

Since the Laravel response object inherits from this one, feel free to use any of the methods you see in the Symfony API documentation. For example, let’s have a look at the setTtl() method. What does the API say?


public Response setTtl(integer $seconds)

Sets the response's time-to-live for shared caches.
This method adjusts the Cache-Control/s-maxage directive.

Parameters:

integer $seconds Number of seconds

Return Value:

Response


Right, so this method sets the time-to-live value for shared caches. I’m no expert on this kind of thing, but a time to live suggests how long a piece of information is considered useful before it is discarded. In this instance the TTL relates to the content caching.

Let’s give it a value for funsies. Having looked at the method signature, we see that it accepts an integer representing the time-to-live value in seconds. Let’s give this response 60 seconds to live. Like some kind of cruel James Bond villain.

<?php

// app/routes.php

Route::get('our/response', function()
{
    $response = Response::make('Bond, James Bond.', 200);
    $response->setTtl(60);
    return $response;
});

Now when our response is served, it will have a time to live value of 60 seconds. You see, by interrogating the underlying Symfony component, we have a wealth of advanced options that we can use to modify our application responses to suit our needs.

Don’t feel overwhelmed by the amount of complexity contained within the base Response object. For the most part, you will be happy using Laravel’s View and Response classes to serve simple HTTP responses. The above example simply serves as a good starting point for advanced users looking to tweak their applications for specific scenarios.

Response Shortcuts

Laravel is your friend. It’s true... no actually it’s not true. Laravel is more than a friend. It loves you. It really does. Because of this, it has provided a number of response shortcuts to make your life easier. Let’s have a look at what’s available to us.

JSON Responses

Often within our application we will have some data that we wish to serve as JSON. It could be a simple object or an array of values.

Laravel provides a Response::json() method that will configure the response object with a number of details that are specific to JSON results. For example an appropriate HTTP Content-Type header.

We could set these up manually, but why bother when Laravel will do it for us? Let’s have a look at this method in action.

<?php

// app/routes.php

Route::get('markdown/response', function()
{
    $data = array('iron', 'man', 'rocks');
    return Response::json($data);
});

By handing an array to the Response::json() method it has been converted to a JSON string and set as the body of our new Response object. Appropriate headers have been set to explain that the provided data is infact a JSON string. The web browser will receive the following body:

["iron","man","rocks"]

Which is both compact and true. Enjoy using this shortcut to build clean yet functional JSON APIs!

Download Responses

Serving files directly requires certain headers to be set. Fortunately, Laravel takes care of this for you using the Response::download() shortcut. Let’s see this in action.

<?php

// app/routes.php

Route::get('file/download', function()
{
    $file = 'path_to_my_file.pdf';
    return Response::download($file);
});

Now if we navigate to the /file/download URI the browser will intiate a download instead of displaying a response. The Response::download() method received a path to a file which will be served when the response is returned.

You can also provide optional second and third parameters to configure a custom HTTP status code and an array of headers. For example:

<?php

// app/routes.php

Route::get('file/download', function()
{
    $file = 'path_to_my_file.pdf';
    return Response::download($file, 418, array('iron', 'man'));
});

Here we will serve our file with the HTTP status code of 418 (I’m a Teapot) and a header value of iron=man.

Well this chapter was a lot longer than I originally anticipated, but I’m sure you will see that returning appropriate response objects can be a lot more valuable than returning simple strings.

In the next chapter we will take a look at route filters, which will allow us to protect our routes or perform actions before/after they are executed.

My books are available online for free to encourage learning. However, if you'd like for me to keep writing, then please consider buying a digital copy over at Leanpub.com.

It's available in PDF, ePub, and Kindle format, and contains a bunch of extras that you won't find on the site. I have a full-time job, and I write my books in my spare time. Please consider buying a copy so that I can continue to write new books from the comfort of my sofa!