Response with JSON scheme

Hi. I have the following scheme of json responses:
{
"status": "success",
"data": {
},
"message": null}`
And HTML Codes.

Now I doing like this:
$response->withJson(array('status' => 'error', 'data' => null, 'message' => 'Incorrect username or password'), 404);
or
$data = array('token' => $jwtToken);
return $response->withJson(array('status' => 'success', 'data' => $data, 'message' => null));

I need to copy this every 500 responses in 40 controllers. I want something elegant:
$response->withSJson($data);
or for error:
$response->withSJson('Lecturer not exist', 404);

How to realize that?

1 Like

We use a similar sort of response structure and we’ve implemented it by subclassing the Response class and injecting it into Slim.

So, you’d need your own response class like this:

use Psr\Http\Message\ServerRequestInterface as Request;

class MyResponse extends \Slim\Http\Response {

  public function withJson($data, $status = null, $encodingOptions = 0) {
    if(!isset($data['error'])) {
      $data['error'] = false;
    }

    if(!isset($data['status'])) {
      $data['status'] = $status ?: $this->getStatusCode();
    }

    return parent::withJson($data, $status, $encodingOptions);
  }

  public function withJsonData($data) {
    return $this->withJson([
      'data' => $data
    ]);
  }
}

And then inject it like this (which is an altered copy of the response value that’s set by default):

$slimContainer = new \Slim\Container();

// Use our Response class instead of the default. This sets some extra values on JSON responses.
$slimContainer['response'] = function ($slimContainer) {
  $headers = new Headers(['Content-Type' => 'text/html; charset=UTF-8']);
  $response = new MyResponse(200, $headers);

  return $response->withProtocolVersion($slimContainer->get('settings')['httpVersion']);
};

$app = new \Slim\App($slimContainer);

Of course you’ll need to update the MyResponse class to match your particular response structure.

2 Likes

Another way to go about is to create a base controller with a protected response method which all your controllers extends that base controller.
By doing so you call $this->res($data [ , $status]);
Good luck

Works good? It is normal style?

That I don’t know. To me it feels like the cleanest way to go about this,
and just extends the precedent set by methods like withRedirect on the
existing Slim Response class. However, it’s possible that using the
dependency injection in this way makes the code a little vulnerable to
future changes in Slim.

You right. But it is better then write one code 1000 times…

You can also create a service for providing the json response and inject it into your controller, or get it from the service container (depending on how you set up your actions and controllers):

// A provider for returning the responses
class SJsonResponseProvider
{
    public function withOk($response, $data, $message = null)
    {
        return $response->withJson(['status' => 'success', 'data' => $data, 'message' => $message]);
    }
    
    public function withError($response, $message, $statusCode)
    {
        return $response->withJson(['status' => 'error', 'data' => $data, 'message' => $message], $statusCode);
    }
}

// add the service to the service container
$app = new \Slim\App([
    // define service for the provider
    'json_provider' => function() { return new SJsonResponseProvider(); }
]);

$app->get('/', function($request, $response) {
    // return OK 
    return $this->json_provider->withOk($response, ['token'=>'1234']);
    // return error
    return $this->json_provider->withError($response, 'Lecturer not exist', 404);
});

Or your can use traits:

trait SJsonResponseProvidable
{
    private function withJsonOk($response, $data, $message = null)
    {
        return $response->withJson(['status' => 'success', 'data' => $data, 'message' => $message]);
    }

    private function withJsonError($response, $message, $statusCode)
    {
        return $response->withJson(['status' => 'error', 'data' => $data, 'message' => $message], $statusCode);
    }
}


class MyAction
{
    // add the trait to this action
    use SJsonResponseProvidable;

    public function __invoke($request, $response)
    {
        // return OK
        return $this->withJsonOk($response, ['token' => '1234']);
        // return erorr
        return $this->withJsonError($response, 'Lecturer not exist', 404);
    }
}

May I get default Response? Why you modify response:

$headers = new Headers(['Content-Type' => 'text/html; charset=UTF-8']);
$response = new MyResponse(200, $headers);
return $response->withProtocolVersion($slimContainer->get('settings')['httpVersion']);

and what is in $encodingOptions?

Thanks!