Tutorials: Working with Responses

Setting View Resolvers

By default framework supports html and json view resolving on both STDERR and STDOUT flows. Json will be handled by JsonResolver/JsonRenderer, while html will generally be handled by ViewLanguageRenderer/ViewLanguageResolver.

Each view resolver on STDOUT flow corresponds to a <format> tag, child of <formats> in stdout.xml. To learn more how to configure this tag, check official documentation or its framework integration. Framework comes by default with:

<formats> <format name="html" content_type="text/html" class="ViewLanguageResolver" charset="UTF-8"/> <format name="json" content_type="application/json" class="JsonResolver" charset="UTF-8"/> </formats>

Each view resolver on STDERR flow corresponds to a <renderer> tag, child of <renderers> in stderr.xml. To learn more how to configure this tag, check official documentation or its framework integration. Framework comes by default with:

<renderers> <renderer format="html" content_type="text/html" class="ViewLanguageRenderer" charset="UTF-8"/> <renderer format="json" content_type="application/json" class="JsonRenderer" charset="UTF-8"/> </renderers>

Setting multiple resolvers for same content type

For STDOUT flow only, it is possible to have multiple resolvers for same content type:

<formats> <format name="html" content_type="text/html" class="ViewLanguageResolver" charset="UTF-8"/> <format name="custom" content_type="text/html" class="CustomResolver" charset="UTF-8"/> <format name="json" content_type="application/json" class="JsonResolver" charset="UTF-8"/> </formats>

In which case <route> that requires another resolver from default needs to set format attribute accordingly:

<routes> ... <route ... format="custom"/> </routes>

Writing View Resolvers

By default, framework supports only text/html and application/json display formats via its MVC APIs. To add support for another (eg: text/xml), developers need to setup XML tags for each MVC APIs then write referenced view resolver classes.

STDOUT View Resolvers

These view resolvers will be executed to handle responses to requests in a desired display format. To support a new display format, first you need a matching <format> in <formats> tag @ stdout.xml. Example:

<format name="xml" content_type="text/xml" class="XMLRenderer"/>

This tells that XMLRenderer class will handle text/xml responses when ever a <route> has a format attribute whose value equals xml. Example:

<route url="sitemap.xml" controller="SitemapController" view="sitemap" format="xml"/>

Now you need to write the view resolver itself. It must have a name that equals value of class attribute above, extend Lucinda\MVC\STDOUT\ViewResolver and be found in application/resolvers folder. Example:

class XMLResolver extends \Lucinda\MVC\STDOUT\ViewResolver { public function getContent() { if($this->response->getView()=="") return ""; // locates view $view = $this->application->getViewsPath()."/".$this->response->getView().".php"; if(!file_exists($view)) { throw new Lucinda\MVC\STDOUT\ServletException("View file not found!"); } // compiles view into a string ob_start(); $_VIEW = $this->response->attributes(); require_once($view); $output = ob_get_contents(); ob_end_clean(); // sends string to output buffer return $output; } }

Then, in application/views, you need to write sitemap.php file, as referenced by view attribute above, to be rendered by resolver above. Example:

<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <?php foreach($_VIEW["items"] as $url=>$priority) { ?> <url> <loc><?php echo $url; ?></loc> <priority><?php echo $priority; ?></priority> </url> <?php ?> </urlset>

This assumes SitemapController sent an items response attribute as an array whose key is url and value is priority:

class SitemapController extends Lucinda\MVC\STDOUT\Controller { public function run() { ... $this->response->attributes("items", $model->getPages()); } }

To learn more about how STDOUT view resolvers work and what can be achieved with them, check their framework integration!

STDERR View Resolvers

These view resolvers will be executed to handle responses to exceptions in a desired display format. To support a new display format, first you need a matching <renderer> in <renderers> tag @ stderr.xml. Example:

<renderer format="html" content_type="text/xml" class="XMLRenderer"/>

This tells that XMLRenderer class will handle text/xml responses if ErrorListener is active and originally called <route> had a format attribute whose value equals xml (see above).

Now you need to write the view resolver itself. It must have a name that equals value of class attribute above, implement Lucinda\MVC\STDERR\ErrorRenderer and be found in application/renderers folder. Example:

class XMLRenderer implements \Lucinda\MVC\STDERR\ErrorRenderer { public function render(Lucinda\MVC\STDERR\Response $response) { if($response->getView()=="" || !$response->getOutputStream()->isEmpty()) return ""; // locates view $view = $response->getView().".php"; if (!file_exists($view)) { throw new Lucinda\MVC\STDERR\Exception("View file not found!"); } // compiles view into a string ob_start(); $_VIEW = $response->attributes(); require_once($view); $output = ob_get_contents(); ob_end_clean(); // sends string to output buffer $response->getOutputStream()->write($output); } }

Then, in application/views, you need same sitemap.php file as in previous section and an ErrorsController modified to send items response attribute as an array whose key is url and value is priority:

class ErrorsController extends Lucinda\MVC\STDERR\Controller { public function run() { ... $this->response->attributes("items", $model->getPages()); } }

To learn more about how STDERR view resolvers work and what can be achieved with them, check their framework integration!

Setting Response Information

Framework or developers collect response information into Response objects: a Lucinda\STDOUT\MVC\Response (encapsulating response to requests) and a Lucinda\STDERR\MVC\Response (encapsulating response to exceptions).

Both classes were done with symmetry in mind (for shared logic, methods have same signature). Most of their shared methods are useful to developers!

Setting Views

Since everything is managed in XML via view attribute of matching route, setting views is seldom needed. Following methods are dedicated at working with views:

Sending Data to Views

In order to send data to views, you must send attributes from controllers. Following Response methods are dedicated at working with attributes:

For more examples, see Views section above!

Setting Response Headers

By default, view resolvers used by framework send only Content-Type header and Etag/Last-Modified (if HTTP caching was activated via Setting Response Caching section above). To fine tune that behavior, following methods are available:

Setting Response Status

By default, HTTP response status for every request is 200 OK (containing full response body) or 304 Not Modified (if it hadn't modified from last call), if HTTP caching was activated (see Setting Response Caching section above).

When an uncaught exception is thrown and framework enters STDERR flow, HTTP response status defaults to 500 Internal Server Error, unless another one is specifically set by http_status of matching <exception>. Example:

<exception class="Lucinda\MVC\STDOUT\PathNotFoundException" http_status="404"/>

For those rare cases in which you need to programmatically control http status of response, following methods are available:

Redirecting to Another Location

To redirect caller to another location, following method is available:

Writing Directly to Output Stream

Sometimes it is necessary for controllers to write directly to output stream and thus do the task of view resolvers. To get access to output stream, use this method:

A very strong ability framework has (via STDOUT MVC API) is an ability of developers to manipulate output stream AFTER view resolvers have done their task via Lucinda\MVC\ResponseListener implementations explained below!

Setting Response Caching

Framework promotes caching of unchanged response using your browser via HTTP headers (as if it were an image). To make it possible, you need to open stdout.xml and make sure this tag exists within <listeners>:

<listener class="HttpCachingListener"/>

then create a <http_caching> tag to set up the process:

<http_caching drivers_path="application/models/cacheables" class="EtagCacheableDriver"/>

This sets up etag-based HTTP caching for your site. To learn more how to configure this tag, check official documentation!

Setting Response Internationalization

Framework supports internationalization of views based on user's preffered locale. To make it possible, you need to open stdout.xml and make sure this tag exists within <listeners>:

<listener class="LocalizationListener"/>

then create a <internationalization> tag to set up the process. Example:

<internationalization locale="en_US" method="header"/>

This sets default locale to en_US and performs internationalization by value of Accept-Language header. To learn more how to configure this tag, check official documentation!

Performing Response Internationalization

After you have completed Setting Response Internationalization section described above, you are now able to make views translate automatically by client locale (language & country). Following locale detection methods are supported:

If detection fails or locale is not supported, a default set in XML is used (eg: en-US). Once locale was detected, clients must be able to see a translated version of called page! In order for that to happen, developers need to perform following steps:

  1. create a locale folder in site root
    Name must equal value of "folder" attribute in <internationalization> tag above.
  2. create a subfolder in above for each locale your site supports
    Name must equal locale in language_COUNTRY code format (eg: en_US, fr_FR)
  3. create a messages.json file inside above subfolder, which will store your translations/locale
    Name must equal value of "domain" attribute in <internationalization> tag above.
  4. break down views into small translatable units and store each translation by name and value in file above
    Example of locale/en_US/messages.json:
    {"greeting":"Hello!", "who_are_you":"I am %0 from %1."}
    Example of locale/fr_FR/messages.json:
    {"greeting":"Bon jour!", "who_are_you":"Je suis %0 de %1."}
  5. convert views to never use hardcoded texts, but calls to translate function instead.
    So instead of:
    <p><strong>Hello!<strong><br/>I am Lucian from Bucharest.</p> You use:
    <p><strong>${translate('greeting')}<strong><br/>${translate('who_are_you', '', 'Lucian', 'Bucharest')}
  6. make sure translations for default locale are always filled (eg: locale/en_US/messages.log above)

By using a second argument for translate functions, developers can call another translation file, a very useful feature against using a single huge messages.json. Example:

${translate('who_are_you', 'presentation', 'Lucian', 'Bucharest')}

This tells to look for who_are_you translation in locale/en_US/presentation.json file (if detected locale was en_US). If second function argument is empty, value of "domain" attribute value is used instead.


Share