A custom response formatter

As was mentioned at the beginning of this chapter, the last stage of processing the data before sending it to the client is passing it through the response formatter. Anything that the controller action returned is wrapped into the Response object, which decides how to ultimately send the data down the pipe. Let's see how we can utilize a custom response formatter for our purposes.

Probably the most obvious use of the custom response formatter is returning the JSON data for a given route. The following is a throwaway snippet of the exploratory code, which returns the list of attributes of the registered services from the database:

    public function actionJson()
    {
        $models = ServiceRecord::find()->all();
        $data = array_map(function ($model) {return $model->attributes;}, $models);

        $response = Yii::$app->response;
        $response->format = Response::FORMAT_JSON;
        $response->data = $data;

        return $response;
    }

In the following screenshot, the result of talking to the /services/json route from the command line is presented. Note that the correct content-type HTTP header was set by the server.

A custom response formatter

First, we have to note that we do not render anything here. The data we get, an associative array, is wrapped into a manually crafted Response instance and returned from the controller action and added to the Response.data field. When we return the string generated by the Controller.render() method, Yii wraps it in the Response instance behind the curtains for us. In fact, everything that is not Response descendant and is being returned from the controller action will be added to the Response.data field automatically.

Secondly, we do not create the Response object ourselves, but we get the reference to it as a Yii component instead. This way, we get the default values of its properties already set by Yii at the application initialization step. The Response object is used exactly once in an application's lifetime, so there's really no difference whether we'll get it as a Yii component or by calling __construct().

The format field tells the Response object how to format the outgoing data. At the time of writing, Yii developers have the following types built-in, and hence don't need to re-implement them:

Literal

Effect

Response::FORMAT_HTML

This is the default literal. Content-Type will be set to text/html. No processing will be done on the data, except that objects will be serialized using the __toString() call.

Response::FORMAT_RAW

Data will be returned without any processing, except that objects will be serialized using the __toString() call. Content-Type will not be set by Yii.

Response::FORMAT_JSON

Data will be processed by the Json::encode() method shipped with the Yii framework. Content-Type will be set to application/json.

Response::FORMAT_JSONP

Data must be an array with the callback string element and the data element. The response is the string callback(data), where data is processed by Json::encode(). This literal basically implements a strict JSONP recommendation (see http://json-p.org/). Content-Type will be set to text/javascript.

Response::FORMAT_XML

Data will be processed by the XmlResponseFormatter class. In short, this means that the well-formed XML string has a response, and the Content-Type header is set to application/xml. Data is expected to be either an associative array or an object with public fields.

In each case when the Content-Type header is set, charset will be set to the value of the charset property of the Response instance else, to the value of the charset property of the application. The only exception here is JSON, where charset will always be UTF-8 by specification.

Let's do something crazy and serialize the data about services not to JSON but to YAML (see http://www.yaml.org/spec/1.2/spec.html). One of Codeception's dependencies is the YAML library from the Symfony2 project, so we'll just utilize it instead of writing our own serializer.

Create the YamlResponseFormatter class inside the utilities directory with the following content:

<?php
namespace apputilities;

use SymfonyComponentYamlYaml;
use yiiwebResponseFormatterInterface;

class YamlResponseFormatter implements ResponseFormatterInterface
{
    const FORMAT = 'yaml';

    public function format($response)
    {
        $response->headers->set('Content-Type: application/yaml'),
        $response->headers->set('Content-Disposition: inline'),
        $response->content = Yaml::dump($response->data);
    }
}

Note the highlighted parts. We are using the Yaml class from the Symfony library, and we have to implement the ResponseFormatterInterface.

A class constant named FORMAT is just convenient so when we set the format of the Response instance, we'll be more descriptive.

The implementation of the format() method is pretty straightforward, thanks to the intuitive Yaml::dump() method. The idea is that we need to set the headers and content fields of the Response instance and not return anything from this method.

The YAML format does not have a Multipurpose Internet Mail Extensions (MIME) type registered (check here: http://www.iana.org/assignments/media-types/media-types.xhtml), so we have arbitrarily decided to use application/yaml to stress the fact that YAML format a serialization format intended to be read by some program.

To wire this formatter to the Response component, we need to add the formatter's declaration to the components.response.formatters item during the application configuration, as follows:

    'components' => [
        'response' => [
            'formatters' => [
                'yaml' => [
                    'class' => 'apputilitiesYamlResponseFormatter'
                ]
            ]
        ]
    ]

The highlighted part is the declaration of YamlResponseFormatter to handle the yaml format.

Finally, add the action routine to ServiceController:

    public function actionYaml()
    {
        $models = ServiceRecord::find()->all();
        $data = array_map(function ($model) {return $model->attributes;}, $models);

        $response = Yii::$app->response;
        $response->format = YamlResponseFormatter::FORMAT;
        $response->data = $data;

        return $response;
    }

Note that the code is almost identical to the code that formatted data as JSON. This is the whole point of the custom response formatters. An example result of reaching the /services/yaml route using CURL is shown in the following screenshot:

A custom response formatter

This YAML data can be read back to the PHP data structures using the Yaml::parse() method, which mirrors the Yaml::dump() method. If you open this route in the browser, it'll open the Save as... dialog prompting you to save the file, since the application/yaml MIME type will not be opened as plain text in the browser. We included the Content-Disposition: inline header to force the browser to display the data if it's capable of doing so.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.218.221