Skip to content

Commit 60d8e7b

Browse files
committed
Centralize API response parsing with ParsesApiResponse trait and fix null handling
- Introduced `ParsesApiResponse` trait to DRY up `parseResponse` logic - Refactored `QueryBuilder` and `ApiModel` to use the new trait, removing duplication - Enhanced `parseResponse` to handle: - `HttpResponse` (Laravel HTTP client) - `JsonResponse` - arrays, `Collection`, JSON strings, and generic objects with `toArray` - Added null‐response checks to prevent `method_exists` on null and fixed test failures for missing data
1 parent 555c261 commit 60d8e7b

File tree

4 files changed

+70
-45
lines changed

4 files changed

+70
-45
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
- Query builder with `where()->get()` support for remote models.
1111
- Smart error handling based on the request's expected format.
1212
- Error propagation improvements with tests for all status codes.
13+
- Introduced `ParsesApiResponse` trait to centralize API response parsing logic.
14+
15+
### Changed
16+
- Refactored `QueryBuilder` and `ApiModel` to use the new `ParsesApiResponse` trait, removing duplicate `parseResponse` implementations.
17+
1318

1419
## [0.4.1] - 2025-07-10
1520
### Added

src/Models/Model.php

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
use Kroderdev\LaravelMicroserviceCore\Contracts\ApiGatewayClientInterface;
1010
use Kroderdev\LaravelMicroserviceCore\Interfaces\ApiModelContract;
1111
use Kroderdev\LaravelMicroserviceCore\Traits\ApiModelTrait;
12+
use Kroderdev\LaravelMicroserviceCore\Traits\ParsesApiResponse;
1213

1314
abstract class Model extends BaseModel implements ApiModelContract
1415
{
1516
use ApiModelTrait;
17+
use ParsesApiResponse;
1618

1719
/**
1820
* Endpoint for this model. Defaults to plural kebab of the class name.
@@ -150,28 +152,4 @@ public function refresh(): static
150152

151153
return $this;
152154
}
153-
154-
/**
155-
* Normalize different response types to array.
156-
*/
157-
protected static function parseResponse($response): array
158-
{
159-
if ($response === null) {
160-
return [];
161-
}
162-
163-
if (is_array($response)) {
164-
return $response;
165-
}
166-
167-
if ($response instanceof Collection) {
168-
return $response->toArray();
169-
}
170-
171-
if (method_exists($response, 'json')) {
172-
return $response->json();
173-
}
174-
175-
return (array) $response;
176-
}
177155
}

src/Models/QueryBuilder.php

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
namespace Kroderdev\LaravelMicroserviceCore\Models;
44

55
use Illuminate\Support\Collection;
6+
use Kroderdev\LaravelMicroserviceCore\Traits\ParsesApiResponse;
67

78
class QueryBuilder
89
{
10+
use ParsesApiResponse;
11+
912
protected string $model;
1013

1114
protected array $filters = [];
@@ -31,25 +34,4 @@ public function get($columns = ['*']): Collection
3134

3235
return $model::fromCollection($data['data'] ?? $data);
3336
}
34-
35-
protected function parseResponse($response): array
36-
{
37-
if ($response === null) {
38-
return [];
39-
}
40-
41-
if (is_array($response)) {
42-
return $response;
43-
}
44-
45-
if ($response instanceof Collection) {
46-
return $response->toArray();
47-
}
48-
49-
if (method_exists($response, 'json')) {
50-
return $response->json();
51-
}
52-
53-
return (array) $response;
54-
}
5537
}

src/Traits/ParsesApiResponse.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace Kroderdev\LaravelMicroserviceCore\Traits;
4+
5+
use Illuminate\Http\Client\Response as HttpResponse;
6+
use Illuminate\Http\JsonResponse;
7+
use Illuminate\Support\Collection;
8+
9+
trait ParsesApiResponse
10+
{
11+
/**
12+
* Normalize different response types to array.
13+
*
14+
* @param mixed $response
15+
* @return array
16+
*/
17+
protected static function parseResponse(mixed $response): array
18+
{
19+
if (is_null($response)) {
20+
return [];
21+
}
22+
23+
if ($response instanceof HttpResponse) {
24+
$response->throw();
25+
$decoded = $response->json();
26+
return is_array($decoded) ? $decoded : [];
27+
}
28+
29+
if ($response instanceof JsonResponse) {
30+
$decoded = $response->getData(true);
31+
return is_array($decoded) ? $decoded : [];
32+
}
33+
34+
if (is_array($response)) {
35+
return $response;
36+
}
37+
38+
if ($response instanceof Collection) {
39+
return $response->toArray();
40+
}
41+
42+
if (is_string($response)) {
43+
$decoded = json_decode($response, true);
44+
return (json_last_error() === JSON_ERROR_NONE && is_array($decoded))
45+
? $decoded
46+
: [];
47+
}
48+
49+
if (method_exists($response, 'toArray')) {
50+
try {
51+
$array = $response->toArray();
52+
return is_array($array) ? $array : [];
53+
} catch (\Throwable $e) {
54+
return [];
55+
}
56+
}
57+
58+
return [];
59+
}
60+
}

0 commit comments

Comments
 (0)