It is fairly common to add json handler to a CakePHP route and have it automatically respond with formatted JSON. Consider this example.
-
In your route:
$routes->prefix('Api', function (RouteBuilder $routes) { $routes->setExtensions(['json']); $routes->fallbacks(DashedRoute::class); }); -
In your controller:
public function initialize(): void { parent::initialize(); $this->addViewClasses([\Cake\View\JsonView::class]); $this->viewBuilder()->setOption('serialize', true); }
So now when you request actions in that controller as /controller/action.json, they will respond with their view variables serialized into JSON.
But what if you want to have formatted responses to a custom extension?
For example, /controller/action.env responding with KEY=VALUE?
1. Tell CakePHP router about your new extension
In your config/routes.php:
$routes->prefix('Api', function (RouteBuilder $routes) {
$routes->setExtensions(['json', 'env']);
$routes->fallbacks(DashedRoute::class);
});
2. Tell CakePHP controller about view class for the extension
In your controller:
public function initialize(): void
{
parent::initialize();
$this->addViewClasses([\Cake\View\JsonView::class, \App\View\EnvView::class]);
$this->viewBuilder()->setOption('serialize', true);
}
Important:
- The
EnvViewdoes not exist yet - we will create it in the next step. - The above only makes CakePHP aware of your custom view class, but you may also have to explicitly link it to the file extention you want it to handle. See #4 below.
3. Create the actual view class
In src/View/EnvView.php, define class EnvView extending \Cake\View\SerializedView. The class must have these two methods:
contentType()_serialize()
<?php
namespace App\View;
class EnvView extends \Cake\View\SerializedView
{
/**
* Mime-type this view class renders as.
*
* @return string The plain text content type.
*/
public static function contentType(): string
{
return 'text/plain';
}
/**
* @inheritDoc
*/
protected function _serialize(array|string $serialize): string
{
if (is_string($serialize)) {
$serialize = [$serialize];
}
// Serialize only the keys requested explicitly
$data = array_intersect_key($this->viewVars, array_flip($serialize));
$lines = [];
foreach ($data as $key => $value) {
$lines[] = sprintf('%s=%s', $key, \escapeshellarg($value));
}
return (string)implode(PHP_EOL, $lines);
}
}
4. (Optional) Link your extension to a mime type
CakePHP controller matches requested file extension to a view class based on $mimeTypes array in src/Http/MimeType.php. The built-in list is pretty exhaustive, but it does not contain env from our example. So a necessary step is to explicitly map env to the mime type we return in EnvView::contentType(), which happens to be text/plain.
Update the controller initialize() to:
public function initialize(): void
{
\Cake\Http\MimeType::addMimeTypes('env', \App\View\EnvView::contentType()); // ADD THIS
parent::initialize();
$this->addViewClasses([\Cake\View\JsonView::class, \App\View\EnvView::class]);
$this->viewBuilder()->setOption('serialize', true);
}
Further reading: