Skip to content

Commit b989c01

Browse files
committed
feat: wip
1 parent 299ef1f commit b989c01

20 files changed

+977
-150
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"require": {
3131
"php": ">=7.4",
3232
"ext-json": "*",
33-
"illuminate/support": "^8.23 || ^9.0 || ^10.0 || ^11.0"
33+
"illuminate/support": "^8.23 || ^9.0 || ^10.0 || ^11.0",
34+
"spatie/laravel-package-tools": "^1.12"
3435
},
3536
"require-dev": {
3637
"brainmaestro/composer-git-hooks": "^2.8 || ^3.0",

config/api-response.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
/** @noinspection LaravelFunctionsInspection */
4+
35
declare(strict_types=1);
46

57
/**
@@ -10,3 +12,48 @@
1012
*
1113
* @see https://github.com/guanguans/laravel-api-response
1214
*/
15+
16+
use Symfony\Component\HttpFoundation\Response;
17+
18+
return [
19+
/**
20+
* @see \Guanguans\LaravelApiResponse\ApiResponseServiceProvider::registerRenderUsing()
21+
*/
22+
'render_using_factory' => Guanguans\LaravelApiResponse\RenderUsingFactory::class,
23+
24+
/**
25+
* @see \Guanguans\LaravelApiResponse\ApiResponse::mapException()
26+
*/
27+
'exception_map' => [
28+
Illuminate\Auth\AuthenticationException::class => [
29+
'code' => Response::HTTP_UNAUTHORIZED,
30+
],
31+
// Illuminate\Database\QueryException::class => [
32+
// 'message' => '',
33+
// 'code' => Response::HTTP_INTERNAL_SERVER_ERROR,
34+
// ],
35+
// Illuminate\Validation\ValidationException::class => [
36+
// 'code' => Response::HTTP_UNPROCESSABLE_ENTITY,
37+
// ],
38+
// Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class => [
39+
// 'message' => '',
40+
// ],
41+
// Illuminate\Database\Eloquent\ModelNotFoundException::class => [
42+
// 'message' => '',
43+
// ],
44+
],
45+
46+
'pipes' => [
47+
/*
48+
* Before...
49+
*/
50+
Guanguans\LaravelApiResponse\Pipes\DataPipe::class,
51+
Guanguans\LaravelApiResponse\Pipes\MessagePipe::with(),
52+
Guanguans\LaravelApiResponse\Pipes\ErrorPipe::with(/* ! app()->hasDebugModeEnabled() */),
53+
54+
/*
55+
* After...
56+
*/
57+
// Guanguans\LaravelApiResponse\Pipes\SetStatusCodePipe::class::with(),
58+
],
59+
];

phpstan-baseline.neon

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,34 @@
11
parameters:
22
ignoreErrors:
33
-
4-
message: "#^Anonymous function has invalid return type Guanguans\\\\LaravelApiResponse\\\\ApiResponseManager\\.$#"
4+
message: "#^Call to an undefined method Illuminate\\\\Support\\\\Collection\\:\\:unshift\\(\\)\\.$#"
55
count: 1
6-
path: src/ApiResponseServiceProvider.php
6+
path: src/ApiResponse.php
77

88
-
9-
message: "#^Anonymous function has invalid return type Guanguans\\\\LaravelApiResponse\\\\CollectorManager\\.$#"
9+
message: "#^Offset 'code' on array\\{message\\: string, code\\: int, error\\: array\\|null, headers\\: array\\} on left side of \\?\\? always exists and is not nullable\\.$#"
1010
count: 1
11-
path: src/ApiResponseServiceProvider.php
11+
path: src/ApiResponse.php
1212

1313
-
14-
message: "#^Call to static method reportIf\\(\\) on an unknown class Guanguans\\\\LaravelApiResponse\\\\Facades\\\\ApiResponse\\.$#"
14+
message: "#^Offset 'headers' on array\\{message\\: string, code\\: int, error\\: array\\|null, headers\\: array\\} on left side of \\?\\? always exists and is not nullable\\.$#"
1515
count: 1
16-
path: src/ApiResponseServiceProvider.php
16+
path: src/ApiResponse.php
1717

1818
-
19-
message: "#^Class Guanguans\\\\LaravelApiResponse\\\\ApiResponseManager not found\\.$#"
20-
count: 4
21-
path: src/ApiResponseServiceProvider.php
22-
23-
-
24-
message: "#^Class Guanguans\\\\LaravelApiResponse\\\\CollectorManager not found\\.$#"
25-
count: 4
26-
path: src/ApiResponseServiceProvider.php
27-
28-
-
29-
message: "#^Class Guanguans\\\\LaravelApiResponse\\\\Commands\\\\TestCommand not found\\.$#"
30-
count: 5
31-
path: src/ApiResponseServiceProvider.php
32-
33-
-
34-
message: "#^Class Guanguans\\\\LaravelApiResponse\\\\Facades\\\\ApiResponse not found\\.$#"
19+
message: "#^Offset 'message' on array\\{message\\: string, code\\: int, error\\: array\\|null, headers\\: array\\} on left side of \\?\\? always exists and is not nullable\\.$#"
3520
count: 1
36-
path: src/ApiResponseServiceProvider.php
21+
path: src/ApiResponse.php
3722

3823
-
39-
message: "#^Instantiated class Guanguans\\\\LaravelApiResponse\\\\ApiResponseManager not found\\.$#"
24+
message: "#^Right side of && is always true\\.$#"
4025
count: 1
41-
path: src/ApiResponseServiceProvider.php
26+
path: src/ApiResponse.php
4227

4328
-
44-
message: "#^Instantiated class Guanguans\\\\LaravelApiResponse\\\\CollectorManager not found\\.$#"
29+
message: "#^PHPDoc tag @method has invalid value \\(static \\\\Guanguans\\\\LaravelApiResponse\\\\ApiResponse putExceptionMap\\(string \\$exception, \\\\Throwable\\|array\\|\\(callable\\|array \\$mapper\\)\\)\\: Unexpected token \"\\$mapper\", expected '\\)' at offset 132$#"
4530
count: 1
46-
path: src/ApiResponseServiceProvider.php
31+
path: src/Facades/ApiResponse.php
4732

4833
-
4934
message: "#^Method Guanguans\\\\LaravelApiResponse\\\\Rectors\\\\ToInternalExceptionRector\\:\\:refactor\\(\\) should return 1\\|2\\|3\\|4\\|array\\<PhpParser\\\\Node\\>\\|PhpParser\\\\Node\\|null but empty return statement found\\.$#"

rector.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
// ->withPhpSets()
5858
// ->withPreparedSets()
5959
->withSets([
60-
// DowngradeLevelSetList::DOWN_TO_PHP_74,
60+
DowngradeLevelSetList::DOWN_TO_PHP_74,
6161
LevelSetList::UP_TO_PHP_74,
6262
])
6363
->withSets([

src/ApiResponse.php

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright (c) 2021-2024 guanguans<ityaozm@gmail.com>
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE file that was distributed with this source code.
10+
*
11+
* @see https://github.com/guanguans/laravel-api-response
12+
*/
13+
14+
namespace Guanguans\LaravelApiResponse;
15+
16+
use Guanguans\LaravelApiResponse\Concerns\ConcreteHttpStatusMethods;
17+
use Guanguans\LaravelApiResponse\Concerns\HasExceptionMap;
18+
use Guanguans\LaravelApiResponse\Concerns\HasPipes;
19+
use Illuminate\Contracts\Debug\ExceptionHandler;
20+
use Illuminate\Http\JsonResponse;
21+
use Illuminate\Pipeline\Pipeline;
22+
use Illuminate\Support\Collection;
23+
use Illuminate\Support\Traits\Conditionable;
24+
use Illuminate\Support\Traits\Dumpable;
25+
use Illuminate\Support\Traits\Macroable;
26+
use Illuminate\Support\Traits\Tappable;
27+
use Illuminate\Validation\ValidationException;
28+
use Symfony\Component\HttpFoundation\Response;
29+
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
30+
31+
/**
32+
* @see https://github.com/dingo/api
33+
* @see https://github.com/f9webltd/laravel-api-response-helpers
34+
* @see https://github.com/flugg/laravel-responder
35+
* @see https://github.com/jiannei/laravel-response
36+
* @see https://github.com/MarcinOrlowski/laravel-api-response-builder
37+
*
38+
* @method array convertExceptionToArray(\Throwable $throwable)
39+
*/
40+
class ApiResponse implements Contracts\ApiResponse
41+
{
42+
// use Dumpable;
43+
use ConcreteHttpStatusMethods;
44+
use Conditionable;
45+
use HasExceptionMap;
46+
use HasPipes;
47+
use Macroable;
48+
use Tappable;
49+
50+
public function __construct(?Collection $pipes = null, ?Collection $exceptionMap = null)
51+
{
52+
$this->pipes = collect($pipes);
53+
$this->exceptionMap = collect($exceptionMap);
54+
}
55+
56+
/**
57+
* @param mixed $data
58+
*/
59+
public function success($data = null, string $message = '', int $code = Response::HTTP_OK): JsonResponse
60+
{
61+
return $this->json(true, $code, $message, $data);
62+
}
63+
64+
public function error(string $message = '', int $code = Response::HTTP_BAD_REQUEST, ?array $error = null): JsonResponse
65+
{
66+
return $this->json(false, $code, $message, null, $error);
67+
}
68+
69+
/**
70+
* @see \Illuminate\Foundation\Exceptions\Handler::render()
71+
* @see \Illuminate\Foundation\Exceptions\Handler::prepareException()
72+
* @see \Illuminate\Foundation\Exceptions\Handler::convertExceptionToArray()
73+
* @see \Illuminate\Database\QueryException
74+
*/
75+
public function throw(\Throwable $throwable): JsonResponse
76+
{
77+
$newThrowable = $this->mapException($throwable);
78+
$newThrowable instanceof \Throwable and $throwable = $newThrowable;
79+
80+
/** @noinspection PhpCastIsUnnecessaryInspection */
81+
$code = (int) $throwable->getCode() ?: Response::HTTP_INTERNAL_SERVER_ERROR;
82+
$message = app()->hasDebugModeEnabled() ? $throwable->getMessage() : '';
83+
$error = (fn (): array => $this->convertExceptionToArray($throwable))->call(app(ExceptionHandler::class));
84+
$headers = [];
85+
86+
if ($throwable instanceof HttpExceptionInterface) {
87+
$message = $throwable->getMessage();
88+
$code = $throwable->getStatusCode();
89+
$headers = $throwable->getHeaders();
90+
}
91+
92+
if ($throwable instanceof ValidationException) {
93+
$message = $throwable->getMessage();
94+
$code = $throwable->status;
95+
$error = $throwable->errors();
96+
}
97+
98+
if (\is_array($newThrowable) && $newThrowable) {
99+
$message = $newThrowable['message'] ?? null ?: $message;
100+
$code = $newThrowable['code'] ?? null ?: $code;
101+
$error = $newThrowable['error'] ?? null ?: $error;
102+
$headers = $newThrowable['headers'] ?? null ?: $headers;
103+
}
104+
105+
return $this->error($message, $code, $error)->withHeaders($headers);
106+
}
107+
108+
/**
109+
* @param int<100, 599>|int<10000, 59999> $code
110+
* @param mixed $data
111+
* @param null|array<string, mixed> $error
112+
*/
113+
public function json(bool $status, int $code, string $message = '', $data = null, ?array $error = null): JsonResponse
114+
{
115+
return (new Pipeline(app()))
116+
->send(['status' => $status, 'code' => $code, 'message' => $message, 'data' => $data, 'error' => $error])
117+
->through($this->pipes())
118+
->then(static fn (array $data): JsonResponse => new JsonResponse(
119+
$data,
120+
200,
121+
[],
122+
\JSON_UNESCAPED_UNICODE | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_LINE_TERMINATORS
123+
| \JSON_HEX_TAG | \JSON_HEX_APOS | \JSON_HEX_AMP | \JSON_HEX_QUOT
124+
));
125+
}
126+
}

0 commit comments

Comments
 (0)