I'm trying to pass an array with data to middleware and format it based on Accept
HTTP header.
The controller gets the data from db and should pass it to the response object. Response object write()
method accepts only strings:
public function getData(Request $request, Response $response): Response {
return $response->write($this->getUsers());
# This line of code should be fixed
}
The middleware should get the response and format it correctly:
public function __invoke(Request $request, Response $response, callable $next) {
$response = $next($request, $response);
$body = $response->getBody();
switch ($request->getHeader('Accept')) {
case 'application/json':
return $response->withJson($body);
break;
case 'application/xml':
# Building an XML with the data
$newResponse = new \Slim\Http\Response();
return $newResponse->write($xml)->withHeader('Content-type', 'application/xml');
break;
case 'text/html':
# Building a HTML list with the data
$newResponse = new \Slim\Http\Response();
return $newResponse->write($list)->withHeader('Content-type', 'text/html;charset=utf-8');
break;
}
}
I have a few routes behaves similarly:
$app->get('/api/users', 'UsersController:getUsers')->add($formatDataMiddleware);
$app->get('/api/products', 'UsersController:getProducts')->add($formatDataMiddleware);
By using middlewares I can add such functionality in a declarative way, keeping my controller thin.
How can I pass the original data array to response and implementing this pattern?
The Response
-Object doesn't provide this functionallity nor has some extensions to do that. So you need to adjust the Response-Class
class MyResponse extends \Slim\Http\Response {
private $data;
public function getData() {
return $this->data;
}
public function withData($data) {
$clone = clone $this;
$clone->data = $data;
return $clone;
}
}
Then you need to add the new Response to the Container
$container = $app->getContainer();
$container['response'] = function($container) { // this stuff is the default from slim
$headers = new Headers(['Content-Type' => 'text/html; charset=UTF-8']);
$response = new MyResponse(200, $headers); // <-- adjust that to the new class
return $response->withProtocolVersion($container->get('settings')['httpVersion']);
}
Now change the Response type to MyResponse
and use the withData
method
public function getData(Request $request, \MyResponse $response): Response {
return $response->withData($this->getUsers());
}
At the end you can use the getData
method and use its value and process it inside the middleware.
public function __invoke(Request $request, \MyResponse $response, callable $next) {
$response = $next($request, $response);
$data = $response->getData();
// [..]
}
That would be the answer to your question. A better solution in my opinion, would be a helper class which does what your middleware does, then you could something like this:
public function getData(Request $request, Response $response): Response {
$data = $this->getUsers();
return $this->helper->formatOutput($request, $response, $data);
}
For that there is already a lib for: rka-content-type-renderer