Skip to content

Commit 6ebd318

Browse files
authored
feat: add option to flatten response (#11)
1 parent 0d5f5c4 commit 6ebd318

File tree

4 files changed

+145
-6
lines changed

4 files changed

+145
-6
lines changed

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,37 @@ For security reasons, it only returns the array from your config and doesn't rea
4343
Route::get('/locales', ListLocalesController::class);
4444
```
4545

46+
By adding `?flatten=true` to the URL, you can get a flat array of all available locales.
47+
You can also change the default behavior in the config file.
48+
49+
#### Non-flattened response
50+
```json
51+
{
52+
"data": {
53+
"api": {
54+
"error": {
55+
"401": "Unauthenticated.",
56+
"403": "Forbidden.",
57+
"404": "Not Found.",
58+
"422": "Unprocessable Entity."
59+
}
60+
}
61+
}
62+
}
63+
```
64+
65+
#### Flattened response
66+
```json
67+
{
68+
"data": {
69+
"api.error.401": "Unauthenticated.",
70+
"api.error.403": "Forbidden.",
71+
"api.error.404": "Not Found.",
72+
"api.error.422": "Unprocessable Entity."
73+
}
74+
}
75+
```
76+
4677
### `Empuxa\LocaleViaApi\Http\Controllers\GetLocaleController`
4778
This controller returns the contents of a locale directory as JSON.
4879
If the directory does not exist, it will return an error 404.

config/locale-via-api.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@
3434
*/
3535
'load_vendor_files' => true,
3636

37+
/**
38+
* Should the output be flattened?
39+
* This will return keys as "api.error.401" instead of "api => error => 401".
40+
* DEFAULT: false
41+
*/
42+
'flatten' => false,
43+
3744
/**
3845
* Add your supported locales here.
3946
*/

src/Controllers/GetLocaleController.php

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Empuxa\LocaleViaApi\Controllers;
44

55
use Illuminate\Http\JsonResponse;
6+
use Illuminate\Http\Request;
67
use Illuminate\Routing\Controller;
78
use Illuminate\Support\Facades\Cache;
89
use Illuminate\Support\Facades\File;
@@ -12,13 +13,17 @@ class GetLocaleController extends Controller
1213
{
1314
private const LOCALE_NOT_FOUND = 'Locale not found.';
1415

16+
private bool $flatten;
17+
1518
/**
1619
* Handle the incoming request.
1720
*
1821
* @throws \Throwable
1922
*/
20-
public function __invoke(string $locale): JsonResponse
23+
public function __invoke(Request $request, string $locale): JsonResponse
2124
{
25+
$this->flatten = $request->query('flatten', config('locale-via-api.flatten', false));
26+
2227
// Ensure locale is valid and exists
2328
$this->ensureLocaleIsValid($locale);
2429
$this->ensureLocaleExists($locale);
@@ -133,12 +138,39 @@ protected function loadLocaleFiles(string $directory, string $prefix = ''): arra
133138
$key = sprintf('%s.%s', $prefix, $key);
134139
}
135140

136-
$data[$key] = File::getRequire($file);
141+
$fileData = File::getRequire($file);
142+
143+
if ($this->flatten) {
144+
$flattenedData = $this->flattenArray($fileData, $key);
145+
$data = array_merge($data, $flattenedData);
146+
} else {
147+
$data[$key] = $fileData;
148+
}
137149
}
138150

139151
return $data;
140152
}
141153

154+
/**
155+
* Flatten a multi-dimensional associative array with dot notation keys.
156+
*/
157+
protected function flattenArray(array $array, string $prefix = ''): array
158+
{
159+
$result = [];
160+
161+
foreach ($array as $key => $value) {
162+
$newKey = $prefix ? $prefix . '.' . $key : $key;
163+
164+
if (is_array($value)) {
165+
$result = array_merge($result, $this->flattenArray($value, $newKey));
166+
} else {
167+
$result[$newKey] = $value;
168+
}
169+
}
170+
171+
return $result;
172+
}
173+
142174
/**
143175
* Create a JSON response.
144176
*/

tests/Feature/GetLocaleTest.php

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22

33
use Empuxa\LocaleViaApi\Controllers\GetLocaleController;
4+
use Illuminate\Http\Request;
45
use Illuminate\Support\Facades\Cache;
56
use Illuminate\Support\Facades\File;
67

@@ -33,12 +34,14 @@
3334
->andReturn([]);
3435

3536
$controller = new GetLocaleController;
36-
$controller($locale);
37+
$request = new Request;
38+
$controller($request, $locale);
3739
});
3840

3941
it('returns a json response with correct structure', function () {
4042
$controller = new GetLocaleController;
41-
$response = $controller('en');
43+
$request = new Request;
44+
$response = $controller($request, 'en');
4245

4346
expect($response)->toBeInstanceOf(Illuminate\Http\JsonResponse::class);
4447

@@ -59,7 +62,8 @@
5962
File::put(lang_path('vendor/test-plugin/en/vendor-test.php'), "<?php return ['title' => 'Vendor Test'];");
6063

6164
$controller = new GetLocaleController;
62-
$response = $controller('en');
65+
$request = new Request;
66+
$response = $controller($request, 'en');
6367

6468
expect($response)->toBeInstanceOf(Illuminate\Http\JsonResponse::class);
6569

@@ -86,7 +90,8 @@
8690
File::put(lang_path('vendor/test-plugin/en/vendor-test.php'), "<?php return ['title' => 'Vendor Test'];");
8791

8892
$controller = new GetLocaleController;
89-
$response = $controller('en');
93+
$request = new Request;
94+
$response = $controller($request, 'en');
9095

9196
expect($response)->toBeInstanceOf(Illuminate\Http\JsonResponse::class);
9297

@@ -104,3 +109,67 @@
104109

105110
expect($responseData['meta']['hash'])->toEqual($expectedHash);
106111
});
112+
113+
it('returns a flattened json response with correct structure', function () {
114+
File::put(lang_path('en/test.php'), "<?php return ['api' => ['error' => ['401' => 'Unauthenticated.', '403' => 'Forbidden.', '404' => 'Not Found.', '422' => 'Unprocessable Entity.']]];");
115+
116+
$request = new Request(['flatten' => true]);
117+
118+
$controller = new GetLocaleController;
119+
$response = $controller($request, 'en');
120+
121+
expect($response)->toBeInstanceOf(Illuminate\Http\JsonResponse::class);
122+
123+
$responseData = $response->getData(true);
124+
125+
expect($responseData)->toHaveKeys(['data', 'meta'])
126+
->and($responseData['data'])->toBeArray()
127+
->and($responseData['data'])->toBe([
128+
'test.api.error.401' => 'Unauthenticated.',
129+
'test.api.error.403' => 'Forbidden.',
130+
'test.api.error.404' => 'Not Found.',
131+
'test.api.error.422' => 'Unprocessable Entity.',
132+
]);
133+
134+
$expectedHash = md5(json_encode([
135+
'test.api.error.401' => 'Unauthenticated.',
136+
'test.api.error.403' => 'Forbidden.',
137+
'test.api.error.404' => 'Not Found.',
138+
'test.api.error.422' => 'Unprocessable Entity.',
139+
]));
140+
141+
expect($responseData['meta']['hash'])->toEqual($expectedHash);
142+
});
143+
144+
it('returns a flattened json response with correct structure with vendor', function () {
145+
config(['locale-via-api.load_vendor_files' => true]);
146+
147+
File::put(lang_path('en/test.php'), "<?php return ['api' => ['error' => ['401' => 'Unauthenticated.', '403' => 'Forbidden.', '404' => 'Not Found.', '422' => 'Unprocessable Entity.']]];");
148+
File::put(lang_path('vendor/test-plugin/en/vendor-test.php'), "<?php return ['title' => 'Vendor Test'];");
149+
150+
$request = new Request(['flatten' => true]);
151+
152+
$controller = new GetLocaleController;
153+
$response = $controller($request, 'en');
154+
155+
expect($response)->toBeInstanceOf(Illuminate\Http\JsonResponse::class);
156+
157+
$responseData = $response->getData(true);
158+
159+
expect($responseData)->toHaveKeys(['data', 'meta'])
160+
->and($responseData['data'])->toBeArray()
161+
->and($responseData['data'])->toHaveKey('test.api.error.401')
162+
->and($responseData['data'])->toHaveKey('vendor.test-plugin.vendor-test.title')
163+
->and($responseData['data']['test.api.error.401'])->toBe('Unauthenticated.')
164+
->and($responseData['data']['vendor.test-plugin.vendor-test.title'])->toBe('Vendor Test');
165+
166+
$expectedHash = md5(json_encode([
167+
'test.api.error.401' => 'Unauthenticated.',
168+
'test.api.error.403' => 'Forbidden.',
169+
'test.api.error.404' => 'Not Found.',
170+
'test.api.error.422' => 'Unprocessable Entity.',
171+
'vendor.test-plugin.vendor-test.title' => 'Vendor Test',
172+
]));
173+
174+
expect($responseData['meta']['hash'])->toEqual($expectedHash);
175+
});

0 commit comments

Comments
 (0)