logo

Tutorials: Working with Responses

Setting View Resolvers

By default framework supports html, json, console view resolving on both STDERR and STDOUT flows. Each view resolver on STDOUT/STDERR flow corresponds to a tag in stdout.xml/stderr.xml. Framework comes by default with:

<resolvers> <resolver format="html" content_type="text/html" class="Lucinda\Project\ViewResolvers\Html" charset="UTF-8"/> <resolver format="json" content_type="application/json" class="Lucinda\Project\ViewResolvers\Json" charset="UTF-8"/> <resolver format="console" content_type="text/plain" class="Lucinda\Project\ViewResolvers\Console" charset="UTF-8"/> </formats>

Setting multiple resolvers for same content type

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

<resolvers> <resolver format="html" content_type="text/html" class="Lucinda\Project\ViewResolvers\Html" charset="UTF-8"/> <resolver format="json" content_type="application/json" class="Lucinda\Project\ViewResolvers\Json" charset="UTF-8"/> <resolver format="custom" content_type="text/html" class="Lucinda\Project\ViewResolvers\CustomResolver" charset="UTF-8"/> </formats>

In which case 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.

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 tag @ stdout.xml or stderr.xml. Example:

<resolver format="xml" content_type="text/xml" class="Lucinda\Project\ViewResolvers\Xml"/>

This tells that Lucinda\Project\ViewResolvers\Xml class will handle text/xml responses when ever a has a format attribute whose value equals xml. Example:

<route id="sitemap.xml" controller="Lucinda\Project\Controllers\Sitemap" 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 and be found in src/ViewResolvers folder. Example:

namespace Lucinda\Project\ViewResolvers; class Xml extends \Lucinda\MVC\ViewResolver { public function run(): void { if (!$this->response->view()->getFile()) { return; } // locates view $view = $this->application->getViewsPath()."/".$this->response->view()->getFile().".php"; if (!file_exists($view)) { throw new Lucinda\MVC\ConfigurationException("View file not found!"); } // compiles view into a string ob_start(); $_VIEW = $this->response->view()->getData(); require_once($view); $output = ob_get_contents(); ob_end_clean(); // sends string to output buffer $this->response->setBody($output); } }

Then, in templates/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 Lucinda\Project\Controllers\Sitemap sent an items response attribute as an array whose key is url and value is priority (for STDERR flow extend instead):

namespace Lucinda\Project\Controllers; use Lucinda\Project\DAO\Pages; class Sitemap extends Lucinda\STDOUT\Controller { public function run(): void { $pages = new Pages(); $this->response->view()["items"] = $pages->getAll(); } }

Setting Response Information

MVC APIs underneath collect response to be rendered into object. Most of its methods are useful to developers!

Setting View Files

Since everything is managed in XML via view attribute of matching , setting views is seldom needed. Following method is dedicated at setting view file:

Sending Data to Views

In order to send data to views from controllers, following method is relevant:

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 method is relevant:

If you desire to set response headers by their ISO name instead (so you won't have to keep on remembering their names), you need to open bootstrap index.php file and add this event listener:

$object->addEventListener(Lucinda\STDOUT\EventType::REQUEST, Lucinda\Project\EventListeners\HttpHeaders::class);

then use in controllers or subsequent event listeners to easily set value of response headers. Example:

$this->attributes->getHeaders()->getResponse()->addTransferEncoding("gzip")

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 . Example:

<route id="Lucinda\STDOUT\PathNotFoundException" http_status="404"/>

For those rare cases in which you need to programmatically control http status of response, following method is relevant:

Redirecting to Another Location

To redirect caller to another location, use class. Example:

$redirect = new Lucinda\MVC\Response\Redirect(URI); $redirect->setPermanent(PERMANENT); # optional $redirect->setPreventCaching(PREVENT_CACHING); # optional $redirect->run();

Where:

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:

If this is used, MVC APIs will not perform view resolution (since output stream was already filled) and output exactly what you filled there!

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 bootstrap index.php file and add this event listener:

$object->addEventListener(Lucinda\STDOUT\EventType::RESPONSE, Lucinda\Project\EventListeners\HttpCaching::class);

which requires another event listener before:

$object->addEventListener(Lucinda\STDOUT\EventType::REQUEST, Lucinda\Project\EventListeners\HttpHeaders::class);

which requires a tag @ stdout.xml:

<headers cacheable="src/Cacheables/[DRIVER]"/>

Where [DRIVER] is a implementation, able to get a string / date representation of response about to be sent. Framework comes by default with:

Performing Response Caching

After you have completed Setting Response Caching section described above, you are now able to automatically communicate with client browser cache depending on [DRIVER] solution used and deliver an efficient response according to following rules:

Request Header
Received
If Cacheable
Used Is:
Is Response
Matching?
Then Response
Header Sent
With Response
HTTP Status
Response Body
Included?
- no ETag 200 yes
If-None-Match yes ETag 304 no
If-None-Match no ETag 200 yes
- no Last-Modified 200 yes
If-Modified-Since yes Last-Modified 304 no
If-Modified-Since no Last-Modified 200 yes

Setting Response Internationalization

Framework supports internationalization of views based on user's preffered locale. To make it possible, you need to open bootstrap index.php file and add this event listener:

$object->addEventListener(Lucinda\STDOUT\EventType::REQUEST, Lucinda\Project\EventListeners\Localization::class);

then create a 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.

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 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 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 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 function, 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.

×