[Bug]: response()->json() with dynamic status returns a string for Response
What happened?
Tried two scenarios for Response
Scenario 1
return response()->json($data, $status); returns the structure $data (expected behaviour)
Scenario 2
return response()->json($data, $status); simply returns a string
Is there a workaround to this?
How to reproduce the bug
Passing a dynamic status to response()->json() i.e. return response()->json($data, $status);
Package Version
0.12.17
PHP Version
8.4.4
Laravel Version
12.0.1
Which operating systems does with happen with?
No response
Notes
No response
@lcyoong Can you please show the pieces of your real code so I can reproduce? Anything that can be reproduced but in the real context
Hello @romalytvynenko Here you go. It's a simplified version of my code but it produces the same result
class GroupController extends BaseController
{
private $status = 200;
/**
* List of groups
*/
public function index(GetGroups $getGroups)
{
return response()->json([
'foo' => 'bar'
], $this->status);
}
}
This will produce the result
When I pass the explicit value of status
class GroupController extends BaseController
{
private $status = 200;
/**
* List of groups
*/
public function index(GetGroups $getGroups)
{
return response()->json([
'foo' => 'bar'
], 200);
}
}
It will show the response structure correctly.
@lcyoong
class GroupController extends BaseController
{
private $status = 200;
/**
* List of groups
*/
public function index(GetGroups $getGroups)
{
return response()->json([
'foo' => 'bar'
], $this->status);
}
}
This specific example is not working due to the type of status being inferred as int (and not as 200).
You see, theoretically the value of $status property of this class may be changed from the outside. And there is no way in the index method to know about this. So Scramble cannot be sure that the type of $this->status is 200.
Next, when the type of status you pass to response()->json() is not a literal integer value, the response is not getting documented and you get the observed behavior. The simplest fix for your case would be to use const:
class GroupController extends BaseController
{
const STATUS = 200;
/**
* List of groups
*/
public function index(GetGroups $getGroups)
{
return response()->json([
'foo' => 'bar'
], static::STATUS);
}
}
Sure this may not "fix" the issue at all if you rely on dynamic $this->status, but that is the entire point: properties types are tricky and cannot be relied on as we cannot be sure about its type.
@lcyoong I think I found the work-around. In my case I wanted to use single function to create a user if not exists and send OTP for login.
When new user is created I wanted to return 201 else 200 and I needed this to reflect in my doc.
this is my function
public function generate(GenerateOtpRequest $request) : JsonResponse {
$phone_number = $request->phone_number;
$user = User::wherePhoneNumber($request->phone_number)->first();
$new_user = false;
if(!$user) {
$new_user = true;
$user = $this->userService->createFromPhone($phone_number);
}
$success = $this->otpService->generateAndSend($user);
if($new_user) {
/**
* @status 201
* @body array{success: bool}
*/
return response()->json(
[
'success' => $success
],
Response::HTTP_CREATED
);
}
/**
* @status 200
* @body array{success: bool}
*/
return response()->json(
[
'success' => $success
],
Response::HTTP_OK
);
}
Output: