Browse Source

improved fix for #744

Maurits van der Schee 3 years ago
parent
commit
fabc8e4742

+ 2
- 2
src/Tqdev/PhpCrudApi/Api.php View File

@@ -59,8 +59,8 @@ class Api implements RequestHandlerInterface
59 59
         $prefix = sprintf('phpcrudapi-%s-', substr(md5(__FILE__), 0, 8));
60 60
         $cache = CacheFactory::create($config->getCacheType(), $prefix, $config->getCachePath());
61 61
         $reflection = new ReflectionService($db, $cache, $config->getCacheTime());
62
-        $responder = new JsonResponder();
63
-        $router = new SimpleRouter($config->getBasePath(), $responder, $cache, $config->getCacheTime(), $config->getDebug());
62
+        $responder = new JsonResponder($config->getDebug());
63
+        $router = new SimpleRouter($config->getBasePath(), $responder, $cache, $config->getCacheTime());
64 64
         foreach ($config->getMiddlewares() as $middleware => $properties) {
65 65
             switch ($middleware) {
66 66
                 case 'sslRedirect':

+ 44
- 4
src/Tqdev/PhpCrudApi/Controller/JsonResponder.php View File

@@ -9,16 +9,56 @@ use Tqdev\PhpCrudApi\ResponseFactory;
9 9
 
10 10
 class JsonResponder implements Responder
11 11
 {
12
+    private $debug;
13
+
14
+    public function __construct(bool $debug)
15
+    {
16
+        $this->debug = $debug;
17
+    }
18
+
12 19
     public function error(int $error, string $argument, $details = null): ResponseInterface
13 20
     {
14
-        $errorCode = new ErrorCode($error);
15
-        $status = $errorCode->getStatus();
16
-        $document = new ErrorDocument($errorCode, $argument, $details);
17
-        return ResponseFactory::fromObject($status, $document);
21
+        $document = new ErrorDocument(new ErrorCode($error), $argument, $details);
22
+        return ResponseFactory::fromObject($document->getStatus(), $document);
18 23
     }
19 24
 
20 25
     public function success($result): ResponseInterface
21 26
     {
22 27
         return ResponseFactory::fromObject(ResponseFactory::OK, $result);
23 28
     }
29
+
30
+    public function exception($exception): ResponseInterface
31
+    {
32
+        $document = ErrorDocument::fromException($exception);
33
+        $response = ResponseFactory::fromObject($document->getStatus(), $document);
34
+        if ($this->debug) {
35
+            $response = ResponseUtils::addExceptionHeaders($response, $exception);
36
+        }
37
+        return $response;
38
+    }
39
+
40
+    public function multi($results): ResponseInterface
41
+    {
42
+        $document = array();
43
+        $success = true;
44
+        foreach ($results as $i=>$result) {
45
+            if ($result instanceof \Throwable) {
46
+                $document[$i] = ErrorDocument::fromException($result);
47
+                $success = false;
48
+            } else {
49
+                $document[$i] = $result;
50
+            }
51
+        }
52
+        $status = $success ? ResponseFactory::OK : ResponseFactory::FAILED_DEPENDENCY;
53
+        $response = ResponseFactory::fromObject($status, $document);
54
+        foreach ($results as $i=>$result) {
55
+            if ($result instanceof \Throwable) {
56
+                if ($this->debug) {
57
+                    $response = ResponseUtils::addExceptionHeaders($response, $result);
58
+                }
59
+            }
60
+        }
61
+        return $response;
62
+    }
63
+
24 64
 }

+ 3
- 3
src/Tqdev/PhpCrudApi/Controller/RecordController.php View File

@@ -70,7 +70,7 @@ class RecordController
70 70
                 $result[] = call_user_func_array($method, $arguments);
71 71
             } catch (\Throwable $e) {
72 72
                 $success = false;
73
-                $result[] = null;
73
+                $result[] = $e;
74 74
             }
75 75
         }
76 76
         if ($success) {
@@ -100,7 +100,7 @@ class RecordController
100 100
             foreach ($record as $r) {
101 101
                 $argumentLists[] = array($table, $r, $params);
102 102
             }
103
-            return $this->responder->success($this->multiCall([$this->service, 'create'], $argumentLists));
103
+            return $this->responder->multi($this->multiCall([$this->service, 'create'], $argumentLists));
104 104
         } else {
105 105
             return $this->responder->success($this->service->create($table, $record, $params));
106 106
         }
@@ -130,7 +130,7 @@ class RecordController
130 130
             for ($i = 0; $i < count($ids); $i++) {
131 131
                 $argumentLists[] = array($table, $ids[$i], $record[$i], $params);
132 132
             }
133
-            return $this->responder->success($this->multiCall([$this->service, 'update'], $argumentLists));
133
+            return $this->responder->multi($this->multiCall([$this->service, 'update'], $argumentLists));
134 134
         } else {
135 135
             if (count($ids) != 1) {
136 136
                 return $this->responder->error(ErrorCode::ARGUMENT_COUNT_MISMATCH, $id);

+ 3
- 20
src/Tqdev/PhpCrudApi/Middleware/Router/SimpleRouter.php View File

@@ -18,19 +18,17 @@ class SimpleRouter implements Router
18 18
     private $responder;
19 19
     private $cache;
20 20
     private $ttl;
21
-    private $debug;
22 21
     private $registration;
23 22
     private $routes;
24 23
     private $routeHandlers;
25 24
     private $middlewares;
26 25
 
27
-    public function __construct(string $basePath, Responder $responder, Cache $cache, int $ttl, bool $debug)
26
+    public function __construct(string $basePath, Responder $responder, Cache $cache, int $ttl)
28 27
     {
29 28
         $this->basePath = rtrim($this->detectBasePath($basePath), '/');
30 29
         $this->responder = $responder;
31 30
         $this->cache = $cache;
32 31
         $this->ttl = $ttl;
33
-        $this->debug = $debug;
34 32
         $this->registration = true;
35 33
         $this->routes = $this->loadPathTree();
36 34
         $this->routeHandlers = [];
@@ -141,23 +139,8 @@ class SimpleRouter implements Router
141 139
         }
142 140
         try {
143 141
             $response = call_user_func($this->routeHandlers[$routeNumbers[0]], $request);
144
-        } catch (\PDOException $e) {
145
-            if (strpos(strtolower($e->getMessage()), 'duplicate') !== false) {
146
-                $response = $this->responder->error(ErrorCode::DUPLICATE_KEY_EXCEPTION, '');
147
-            } elseif (strpos(strtolower($e->getMessage()), 'unique constraint') !== false) {
148
-                $response = $this->responder->error(ErrorCode::DUPLICATE_KEY_EXCEPTION, '');
149
-            } elseif (strpos(strtolower($e->getMessage()), 'default value') !== false) {
150
-                $response = $this->responder->error(ErrorCode::DATA_INTEGRITY_VIOLATION, '');
151
-            } elseif (strpos(strtolower($e->getMessage()), 'allow nulls') !== false) {
152
-                $response = $this->responder->error(ErrorCode::DATA_INTEGRITY_VIOLATION, '');
153
-            } elseif (strpos(strtolower($e->getMessage()), 'constraint') !== false) {
154
-                $response = $this->responder->error(ErrorCode::DATA_INTEGRITY_VIOLATION, '');
155
-            } else {
156
-                $response = $this->responder->error(ErrorCode::ERROR_NOT_FOUND, '');
157
-            }
158
-            if ($this->debug) {
159
-                $response = ResponseUtils::addExceptionHeaders($response, $e);
160
-            }
142
+        } catch (\Throwable $exception) {
143
+            $response = $this->responder->exception($exception);
161 144
         }
162 145
         return $response;
163 146
     }

+ 34
- 8
src/Tqdev/PhpCrudApi/Record/Document/ErrorDocument.php View File

@@ -6,32 +6,37 @@ use Tqdev\PhpCrudApi\Record\ErrorCode;
6 6
 
7 7
 class ErrorDocument implements \JsonSerializable
8 8
 {
9
-    public $code;
10
-    public $message;
9
+    public $errorCode;
10
+    public $argument;
11 11
     public $details;
12 12
 
13 13
     public function __construct(ErrorCode $errorCode, string $argument, $details)
14 14
     {
15
-        $this->code = $errorCode->getCode();
16
-        $this->message = $errorCode->getMessage($argument);
15
+        $this->errorCode = $errorCode;
16
+        $this->argument = $argument;
17 17
         $this->details = $details;
18 18
     }
19 19
 
20
+    public function getStatus(): int
21
+    {
22
+        return $this->errorCode->getStatus();
23
+    }
24
+
20 25
     public function getCode(): int
21 26
     {
22
-        return $this->code;
27
+        return $this->errorCode->getCode();
23 28
     }
24 29
 
25 30
     public function getMessage(): string
26 31
     {
27
-        return $this->message;
32
+        return $this->errorCode->getMessage($this->argument);
28 33
     }
29 34
 
30 35
     public function serialize()
31 36
     {
32 37
         return [
33
-            'code' => $this->code,
34
-            'message' => $this->message,
38
+            'code' => $this->getCode(),
39
+            'message' => $this->getMessage(),
35 40
             'details' => $this->details,
36 41
         ];
37 42
     }
@@ -40,4 +45,25 @@ class ErrorDocument implements \JsonSerializable
40 45
     {
41 46
         return array_filter($this->serialize());
42 47
     }
48
+
49
+    public static function fromException(\Throwable $exception)
50
+    {
51
+        $document = new ErrorDocument(new ErrorCode(ErrorCode::ERROR_NOT_FOUND), $exception->getMessage(), null);
52
+        if ($exception instanceof \PDOException) {
53
+            if (strpos(strtolower($exception->getMessage()), 'duplicate') !== false) {
54
+                $document = new ErrorDocument(new ErrorCode(ErrorCode::DUPLICATE_KEY_EXCEPTION), '', null);
55
+            } elseif (strpos(strtolower($exception->getMessage()), 'unique constraint') !== false) {
56
+                $document = new ErrorDocument(new ErrorCode(ErrorCode::DUPLICATE_KEY_EXCEPTION), '', null);
57
+            } elseif (strpos(strtolower($exception->getMessage()), 'default value') !== false) {
58
+                $document = new ErrorDocument(new ErrorCode(ErrorCode::DATA_INTEGRITY_VIOLATION), '', null);
59
+            } elseif (strpos(strtolower($exception->getMessage()), 'allow nulls') !== false) {
60
+                $document = new ErrorDocument(new ErrorCode(ErrorCode::DATA_INTEGRITY_VIOLATION), '', null);
61
+            } elseif (strpos(strtolower($exception->getMessage()), 'constraint') !== false) {
62
+                $document = new ErrorDocument(new ErrorCode(ErrorCode::DATA_INTEGRITY_VIOLATION), '', null);
63
+            } else {
64
+                $document = new ErrorDocument(new ErrorCode(ErrorCode::ERROR_NOT_FOUND), '', null);
65
+            }
66
+        }
67
+        return $document;
68
+    }
43 69
 }

+ 1
- 0
src/Tqdev/PhpCrudApi/ResponseFactory.php View File

@@ -16,6 +16,7 @@ class ResponseFactory
16 16
     const METHOD_NOT_ALLOWED = 405;
17 17
     const CONFLICT = 409;
18 18
     const UNPROCESSABLE_ENTITY = 422;
19
+    const FAILED_DEPENDENCY = 424;
19 20
     const INTERNAL_SERVER_ERROR = 500;
20 21
 
21 22
     public static function fromXml(int $status, string $xml): ResponseInterface

+ 3
- 3
tests/functional/001_records/090_add_multiple_comments.log View File

@@ -21,11 +21,11 @@ POST /records/comments
21 21
 
22 22
 [{"user_id":1,"post_id":6,"message":"multi 3","category_id":3},{"user_id":1,"post_id":0,"message":"multi 4","category_id":3}]
23 23
 ===
24
-200
24
+424
25 25
 Content-Type: application/json; charset=utf-8
26
-Content-Length: 8
26
+Content-Length: 54
27 27
 
28
-[9,null]
28
+[9,{"code":1010,"message":"Data integrity violation"}]
29 29
 ===
30 30
 GET /records/comments?include=id&filter=post_id,eq,6
31 31
 ===

+ 3
- 3
tests/functional/001_records/091_edit_multiple_comments.log View File

@@ -21,11 +21,11 @@ PUT /records/comments/7,8
21 21
 
22 22
 [{"user_id":1,"post_id":6,"message":"multi 3","category_id":3},{"user_id":1,"post_id":0,"message":"multi 4","category_id":3}]
23 23
 ===
24
-200
24
+424
25 25
 Content-Type: application/json; charset=utf-8
26
-Content-Length: 8
26
+Content-Length: 54
27 27
 
28
-[1,null]
28
+[1,{"code":1010,"message":"Data integrity violation"}]
29 29
 ===
30 30
 GET /records/comments?include=message&filter=post_id,eq,6
31 31
 ===

Loading…
Cancel
Save