|
@@ -4525,18 +4525,58 @@ namespace Tqdev\PhpCrudApi\Controller {
|
4525
|
4525
|
|
4526
|
4526
|
class JsonResponder implements Responder
|
4527
|
4527
|
{
|
|
4528
|
+ private $debug;
|
|
4529
|
+
|
|
4530
|
+ public function __construct(bool $debug)
|
|
4531
|
+ {
|
|
4532
|
+ $this->debug = $debug;
|
|
4533
|
+ }
|
|
4534
|
+
|
4528
|
4535
|
public function error(int $error, string $argument, $details = null): ResponseInterface
|
4529
|
4536
|
{
|
4530
|
|
- $errorCode = new ErrorCode($error);
|
4531
|
|
- $status = $errorCode->getStatus();
|
4532
|
|
- $document = new ErrorDocument($errorCode, $argument, $details);
|
4533
|
|
- return ResponseFactory::fromObject($status, $document);
|
|
4537
|
+ $document = new ErrorDocument(new ErrorCode($error), $argument, $details);
|
|
4538
|
+ return ResponseFactory::fromObject($document->getStatus(), $document);
|
4534
|
4539
|
}
|
4535
|
4540
|
|
4536
|
4541
|
public function success($result): ResponseInterface
|
4537
|
4542
|
{
|
4538
|
4543
|
return ResponseFactory::fromObject(ResponseFactory::OK, $result);
|
4539
|
4544
|
}
|
|
4545
|
+
|
|
4546
|
+ public function exception($exception): ResponseInterface
|
|
4547
|
+ {
|
|
4548
|
+ $document = ErrorDocument::fromException($exception);
|
|
4549
|
+ $response = ResponseFactory::fromObject($document->getStatus(), $document);
|
|
4550
|
+ if ($this->debug) {
|
|
4551
|
+ $response = ResponseUtils::addExceptionHeaders($response, $exception);
|
|
4552
|
+ }
|
|
4553
|
+ return $response;
|
|
4554
|
+ }
|
|
4555
|
+
|
|
4556
|
+ public function multi($results): ResponseInterface
|
|
4557
|
+ {
|
|
4558
|
+ $document = array();
|
|
4559
|
+ $success = true;
|
|
4560
|
+ foreach ($results as $i=>$result) {
|
|
4561
|
+ if ($result instanceof \Throwable) {
|
|
4562
|
+ $document[$i] = ErrorDocument::fromException($result);
|
|
4563
|
+ $success = false;
|
|
4564
|
+ } else {
|
|
4565
|
+ $document[$i] = $result;
|
|
4566
|
+ }
|
|
4567
|
+ }
|
|
4568
|
+ $status = $success ? ResponseFactory::OK : ResponseFactory::FAILED_DEPENDENCY;
|
|
4569
|
+ $response = ResponseFactory::fromObject($status, $document);
|
|
4570
|
+ foreach ($results as $i=>$result) {
|
|
4571
|
+ if ($result instanceof \Throwable) {
|
|
4572
|
+ if ($this->debug) {
|
|
4573
|
+ $response = ResponseUtils::addExceptionHeaders($response, $result);
|
|
4574
|
+ }
|
|
4575
|
+ }
|
|
4576
|
+ }
|
|
4577
|
+ return $response;
|
|
4578
|
+ }
|
|
4579
|
+
|
4540
|
4580
|
}
|
4541
|
4581
|
}
|
4542
|
4582
|
|
|
@@ -4614,11 +4654,11 @@ namespace Tqdev\PhpCrudApi\Controller {
|
4614
|
4654
|
$params = RequestUtils::getParams($request);
|
4615
|
4655
|
if (strpos($id, ',') !== false) {
|
4616
|
4656
|
$ids = explode(',', $id);
|
4617
|
|
- $result = [];
|
|
4657
|
+ $argumentLists = array();
|
4618
|
4658
|
for ($i = 0; $i < count($ids); $i++) {
|
4619
|
|
- array_push($result, $this->service->read($table, $ids[$i], $params));
|
|
4659
|
+ $argumentLists[] = array($table, $ids[$i], $params);
|
4620
|
4660
|
}
|
4621
|
|
- return $this->responder->success($result);
|
|
4661
|
+ return $this->responder->multi($this->multiCall([$this->service, 'read'], $argumentLists));
|
4622
|
4662
|
} else {
|
4623
|
4663
|
$response = $this->service->read($table, $id, $params);
|
4624
|
4664
|
if ($response === null) {
|
|
@@ -4628,6 +4668,27 @@ namespace Tqdev\PhpCrudApi\Controller {
|
4628
|
4668
|
}
|
4629
|
4669
|
}
|
4630
|
4670
|
|
|
4671
|
+ private function multiCall(callable $method, array $argumentLists): array
|
|
4672
|
+ {
|
|
4673
|
+ $result = array();
|
|
4674
|
+ $success = true;
|
|
4675
|
+ $this->service->beginTransaction();
|
|
4676
|
+ foreach ($argumentLists as $arguments) {
|
|
4677
|
+ try {
|
|
4678
|
+ $result[] = call_user_func_array($method, $arguments);
|
|
4679
|
+ } catch (\Throwable $e) {
|
|
4680
|
+ $success = false;
|
|
4681
|
+ $result[] = $e;
|
|
4682
|
+ }
|
|
4683
|
+ }
|
|
4684
|
+ if ($success) {
|
|
4685
|
+ $this->service->commitTransaction();
|
|
4686
|
+ } else {
|
|
4687
|
+ $this->service->rollBackTransaction();
|
|
4688
|
+ }
|
|
4689
|
+ return $result;
|
|
4690
|
+ }
|
|
4691
|
+
|
4631
|
4692
|
public function create(ServerRequestInterface $request): ResponseInterface
|
4632
|
4693
|
{
|
4633
|
4694
|
$table = RequestUtils::getPathSegment($request, 2);
|
|
@@ -4643,11 +4704,11 @@ namespace Tqdev\PhpCrudApi\Controller {
|
4643
|
4704
|
}
|
4644
|
4705
|
$params = RequestUtils::getParams($request);
|
4645
|
4706
|
if (is_array($record)) {
|
4646
|
|
- $result = array();
|
|
4707
|
+ $argumentLists = array();
|
4647
|
4708
|
foreach ($record as $r) {
|
4648
|
|
- $result[] = $this->service->create($table, $r, $params);
|
|
4709
|
+ $argumentLists[] = array($table, $r, $params);
|
4649
|
4710
|
}
|
4650
|
|
- return $this->responder->success($result);
|
|
4711
|
+ return $this->responder->multi($this->multiCall([$this->service, 'create'], $argumentLists));
|
4651
|
4712
|
} else {
|
4652
|
4713
|
return $this->responder->success($this->service->create($table, $record, $params));
|
4653
|
4714
|
}
|
|
@@ -4673,11 +4734,11 @@ namespace Tqdev\PhpCrudApi\Controller {
|
4673
|
4734
|
if (count($ids) != count($record)) {
|
4674
|
4735
|
return $this->responder->error(ErrorCode::ARGUMENT_COUNT_MISMATCH, $id);
|
4675
|
4736
|
}
|
4676
|
|
- $result = array();
|
|
4737
|
+ $argumentLists = array();
|
4677
|
4738
|
for ($i = 0; $i < count($ids); $i++) {
|
4678
|
|
- $result[] = $this->service->update($table, $ids[$i], $record[$i], $params);
|
|
4739
|
+ $argumentLists[] = array($table, $ids[$i], $record[$i], $params);
|
4679
|
4740
|
}
|
4680
|
|
- return $this->responder->success($result);
|
|
4741
|
+ return $this->responder->multi($this->multiCall([$this->service, 'update'], $argumentLists));
|
4681
|
4742
|
} else {
|
4682
|
4743
|
if (count($ids) != 1) {
|
4683
|
4744
|
return $this->responder->error(ErrorCode::ARGUMENT_COUNT_MISMATCH, $id);
|
|
@@ -4699,11 +4760,11 @@ namespace Tqdev\PhpCrudApi\Controller {
|
4699
|
4760
|
$params = RequestUtils::getParams($request);
|
4700
|
4761
|
$ids = explode(',', $id);
|
4701
|
4762
|
if (count($ids) > 1) {
|
4702
|
|
- $result = array();
|
|
4763
|
+ $argumentLists = array();
|
4703
|
4764
|
for ($i = 0; $i < count($ids); $i++) {
|
4704
|
|
- $result[] = $this->service->delete($table, $ids[$i], $params);
|
|
4765
|
+ $argumentLists[] = array($table, $ids[$i], $params);
|
4705
|
4766
|
}
|
4706
|
|
- return $this->responder->success($result);
|
|
4767
|
+ return $this->responder->multi($this->multiCall([$this->service, 'delete'], $argumentLists));
|
4707
|
4768
|
} else {
|
4708
|
4769
|
return $this->responder->success($this->service->delete($table, $id, $params));
|
4709
|
4770
|
}
|
|
@@ -4729,11 +4790,11 @@ namespace Tqdev\PhpCrudApi\Controller {
|
4729
|
4790
|
if (count($ids) != count($record)) {
|
4730
|
4791
|
return $this->responder->error(ErrorCode::ARGUMENT_COUNT_MISMATCH, $id);
|
4731
|
4792
|
}
|
4732
|
|
- $result = array();
|
|
4793
|
+ $argumentLists = array();
|
4733
|
4794
|
for ($i = 0; $i < count($ids); $i++) {
|
4734
|
|
- $result[] = $this->service->increment($table, $ids[$i], $record[$i], $params);
|
|
4795
|
+ $argumentLists[] = array($table, $ids[$i], $record[$i], $params);
|
4735
|
4796
|
}
|
4736
|
|
- return $this->responder->success($result);
|
|
4797
|
+ return $this->responder->multi($this->multiCall([$this->service, 'increment'], $argumentLists));
|
4737
|
4798
|
} else {
|
4738
|
4799
|
if (count($ids) != 1) {
|
4739
|
4800
|
return $this->responder->error(ErrorCode::ARGUMENT_COUNT_MISMATCH, $id);
|
|
@@ -5435,6 +5496,21 @@ namespace Tqdev\PhpCrudApi\Database {
|
5435
|
5496
|
return $this->definition;
|
5436
|
5497
|
}
|
5437
|
5498
|
|
|
5499
|
+ public function beginTransaction() /*: void*/
|
|
5500
|
+ {
|
|
5501
|
+ $this->pdo->beginTransaction();
|
|
5502
|
+ }
|
|
5503
|
+
|
|
5504
|
+ public function commitTransaction() /*: void*/
|
|
5505
|
+ {
|
|
5506
|
+ $this->pdo->commit();
|
|
5507
|
+ }
|
|
5508
|
+
|
|
5509
|
+ public function rollBackTransaction() /*: void*/
|
|
5510
|
+ {
|
|
5511
|
+ $this->pdo->rollBack();
|
|
5512
|
+ }
|
|
5513
|
+
|
5438
|
5514
|
private function addMiddlewareConditions(string $tableName, Condition $condition): Condition
|
5439
|
5515
|
{
|
5440
|
5516
|
$condition1 = VariableStore::get("authorization.conditions.$tableName");
|
|
@@ -5609,7 +5685,7 @@ namespace Tqdev\PhpCrudApi\Database {
|
5609
|
5685
|
$this->port,
|
5610
|
5686
|
$this->database,
|
5611
|
5687
|
$this->tables,
|
5612
|
|
- $this->username
|
|
5688
|
+ $this->username,
|
5613
|
5689
|
]));
|
5614
|
5690
|
}
|
5615
|
5691
|
}
|
|
@@ -6979,19 +7055,17 @@ namespace Tqdev\PhpCrudApi\Middleware\Router {
|
6979
|
7055
|
private $responder;
|
6980
|
7056
|
private $cache;
|
6981
|
7057
|
private $ttl;
|
6982
|
|
- private $debug;
|
6983
|
7058
|
private $registration;
|
6984
|
7059
|
private $routes;
|
6985
|
7060
|
private $routeHandlers;
|
6986
|
7061
|
private $middlewares;
|
6987
|
7062
|
|
6988
|
|
- public function __construct(string $basePath, Responder $responder, Cache $cache, int $ttl, bool $debug)
|
|
7063
|
+ public function __construct(string $basePath, Responder $responder, Cache $cache, int $ttl)
|
6989
|
7064
|
{
|
6990
|
7065
|
$this->basePath = rtrim($this->detectBasePath($basePath), '/');
|
6991
|
7066
|
$this->responder = $responder;
|
6992
|
7067
|
$this->cache = $cache;
|
6993
|
7068
|
$this->ttl = $ttl;
|
6994
|
|
- $this->debug = $debug;
|
6995
|
7069
|
$this->registration = true;
|
6996
|
7070
|
$this->routes = $this->loadPathTree();
|
6997
|
7071
|
$this->routeHandlers = [];
|
|
@@ -7102,23 +7176,8 @@ namespace Tqdev\PhpCrudApi\Middleware\Router {
|
7102
|
7176
|
}
|
7103
|
7177
|
try {
|
7104
|
7178
|
$response = call_user_func($this->routeHandlers[$routeNumbers[0]], $request);
|
7105
|
|
- } catch (\PDOException $e) {
|
7106
|
|
- if (strpos(strtolower($e->getMessage()), 'duplicate') !== false) {
|
7107
|
|
- $response = $this->responder->error(ErrorCode::DUPLICATE_KEY_EXCEPTION, '');
|
7108
|
|
- } elseif (strpos(strtolower($e->getMessage()), 'unique constraint') !== false) {
|
7109
|
|
- $response = $this->responder->error(ErrorCode::DUPLICATE_KEY_EXCEPTION, '');
|
7110
|
|
- } elseif (strpos(strtolower($e->getMessage()), 'default value') !== false) {
|
7111
|
|
- $response = $this->responder->error(ErrorCode::DATA_INTEGRITY_VIOLATION, '');
|
7112
|
|
- } elseif (strpos(strtolower($e->getMessage()), 'allow nulls') !== false) {
|
7113
|
|
- $response = $this->responder->error(ErrorCode::DATA_INTEGRITY_VIOLATION, '');
|
7114
|
|
- } elseif (strpos(strtolower($e->getMessage()), 'constraint') !== false) {
|
7115
|
|
- $response = $this->responder->error(ErrorCode::DATA_INTEGRITY_VIOLATION, '');
|
7116
|
|
- } else {
|
7117
|
|
- $response = $this->responder->error(ErrorCode::ERROR_NOT_FOUND, '');
|
7118
|
|
- }
|
7119
|
|
- if ($this->debug) {
|
7120
|
|
- $response = ResponseUtils::addExceptionHeaders($response, $e);
|
7121
|
|
- }
|
|
7179
|
+ } catch (\Throwable $exception) {
|
|
7180
|
+ $response = $this->responder->exception($exception);
|
7122
|
7181
|
}
|
7123
|
7182
|
return $response;
|
7124
|
7183
|
}
|
|
@@ -9790,32 +9849,37 @@ namespace Tqdev\PhpCrudApi\Record\Document {
|
9790
|
9849
|
|
9791
|
9850
|
class ErrorDocument implements \JsonSerializable
|
9792
|
9851
|
{
|
9793
|
|
- public $code;
|
9794
|
|
- public $message;
|
|
9852
|
+ public $errorCode;
|
|
9853
|
+ public $argument;
|
9795
|
9854
|
public $details;
|
9796
|
9855
|
|
9797
|
9856
|
public function __construct(ErrorCode $errorCode, string $argument, $details)
|
9798
|
9857
|
{
|
9799
|
|
- $this->code = $errorCode->getCode();
|
9800
|
|
- $this->message = $errorCode->getMessage($argument);
|
|
9858
|
+ $this->errorCode = $errorCode;
|
|
9859
|
+ $this->argument = $argument;
|
9801
|
9860
|
$this->details = $details;
|
9802
|
9861
|
}
|
9803
|
9862
|
|
|
9863
|
+ public function getStatus(): int
|
|
9864
|
+ {
|
|
9865
|
+ return $this->errorCode->getStatus();
|
|
9866
|
+ }
|
|
9867
|
+
|
9804
|
9868
|
public function getCode(): int
|
9805
|
9869
|
{
|
9806
|
|
- return $this->code;
|
|
9870
|
+ return $this->errorCode->getCode();
|
9807
|
9871
|
}
|
9808
|
9872
|
|
9809
|
9873
|
public function getMessage(): string
|
9810
|
9874
|
{
|
9811
|
|
- return $this->message;
|
|
9875
|
+ return $this->errorCode->getMessage($this->argument);
|
9812
|
9876
|
}
|
9813
|
9877
|
|
9814
|
9878
|
public function serialize()
|
9815
|
9879
|
{
|
9816
|
9880
|
return [
|
9817
|
|
- 'code' => $this->code,
|
9818
|
|
- 'message' => $this->message,
|
|
9881
|
+ 'code' => $this->getCode(),
|
|
9882
|
+ 'message' => $this->getMessage(),
|
9819
|
9883
|
'details' => $this->details,
|
9820
|
9884
|
];
|
9821
|
9885
|
}
|
|
@@ -9824,6 +9888,27 @@ namespace Tqdev\PhpCrudApi\Record\Document {
|
9824
|
9888
|
{
|
9825
|
9889
|
return array_filter($this->serialize());
|
9826
|
9890
|
}
|
|
9891
|
+
|
|
9892
|
+ public static function fromException(\Throwable $exception)
|
|
9893
|
+ {
|
|
9894
|
+ $document = new ErrorDocument(new ErrorCode(ErrorCode::ERROR_NOT_FOUND), $exception->getMessage(), null);
|
|
9895
|
+ if ($exception instanceof \PDOException) {
|
|
9896
|
+ if (strpos(strtolower($exception->getMessage()), 'duplicate') !== false) {
|
|
9897
|
+ $document = new ErrorDocument(new ErrorCode(ErrorCode::DUPLICATE_KEY_EXCEPTION), '', null);
|
|
9898
|
+ } elseif (strpos(strtolower($exception->getMessage()), 'unique constraint') !== false) {
|
|
9899
|
+ $document = new ErrorDocument(new ErrorCode(ErrorCode::DUPLICATE_KEY_EXCEPTION), '', null);
|
|
9900
|
+ } elseif (strpos(strtolower($exception->getMessage()), 'default value') !== false) {
|
|
9901
|
+ $document = new ErrorDocument(new ErrorCode(ErrorCode::DATA_INTEGRITY_VIOLATION), '', null);
|
|
9902
|
+ } elseif (strpos(strtolower($exception->getMessage()), 'allow nulls') !== false) {
|
|
9903
|
+ $document = new ErrorDocument(new ErrorCode(ErrorCode::DATA_INTEGRITY_VIOLATION), '', null);
|
|
9904
|
+ } elseif (strpos(strtolower($exception->getMessage()), 'constraint') !== false) {
|
|
9905
|
+ $document = new ErrorDocument(new ErrorCode(ErrorCode::DATA_INTEGRITY_VIOLATION), '', null);
|
|
9906
|
+ } else {
|
|
9907
|
+ $document = new ErrorDocument(new ErrorCode(ErrorCode::ERROR_NOT_FOUND), '', null);
|
|
9908
|
+ }
|
|
9909
|
+ }
|
|
9910
|
+ return $document;
|
|
9911
|
+ }
|
9827
|
9912
|
}
|
9828
|
9913
|
}
|
9829
|
9914
|
|
|
@@ -10349,6 +10434,21 @@ namespace Tqdev\PhpCrudApi\Record {
|
10349
|
10434
|
return $this->reflection->getType($table);
|
10350
|
10435
|
}
|
10351
|
10436
|
|
|
10437
|
+ public function beginTransaction() /*: void*/
|
|
10438
|
+ {
|
|
10439
|
+ $this->db->beginTransaction();
|
|
10440
|
+ }
|
|
10441
|
+
|
|
10442
|
+ public function commitTransaction() /*: void*/
|
|
10443
|
+ {
|
|
10444
|
+ $this->db->commitTransaction();
|
|
10445
|
+ }
|
|
10446
|
+
|
|
10447
|
+ public function rollBackTransaction() /*: void*/
|
|
10448
|
+ {
|
|
10449
|
+ $this->db->rollBackTransaction();
|
|
10450
|
+ }
|
|
10451
|
+
|
10352
|
10452
|
public function create(string $tableName, /* object */ $record, array $params) /*: ?int*/
|
10353
|
10453
|
{
|
10354
|
10454
|
$this->sanitizeRecord($tableName, $record, '');
|
|
@@ -10773,8 +10873,8 @@ namespace Tqdev\PhpCrudApi {
|
10773
|
10873
|
$prefix = sprintf('phpcrudapi-%s-', substr(md5(__FILE__), 0, 8));
|
10774
|
10874
|
$cache = CacheFactory::create($config->getCacheType(), $prefix, $config->getCachePath());
|
10775
|
10875
|
$reflection = new ReflectionService($db, $cache, $config->getCacheTime());
|
10776
|
|
- $responder = new JsonResponder();
|
10777
|
|
- $router = new SimpleRouter($config->getBasePath(), $responder, $cache, $config->getCacheTime(), $config->getDebug());
|
|
10876
|
+ $responder = new JsonResponder($config->getDebug());
|
|
10877
|
+ $router = new SimpleRouter($config->getBasePath(), $responder, $cache, $config->getCacheTime());
|
10778
|
10878
|
foreach ($config->getMiddlewares() as $middleware => $properties) {
|
10779
|
10879
|
switch ($middleware) {
|
10780
|
10880
|
case 'sslRedirect':
|
|
@@ -11297,6 +11397,7 @@ namespace Tqdev\PhpCrudApi {
|
11297
|
11397
|
const METHOD_NOT_ALLOWED = 405;
|
11298
|
11398
|
const CONFLICT = 409;
|
11299
|
11399
|
const UNPROCESSABLE_ENTITY = 422;
|
|
11400
|
+ const FAILED_DEPENDENCY = 424;
|
11300
|
11401
|
const INTERNAL_SERVER_ERROR = 500;
|
11301
|
11402
|
|
11302
|
11403
|
public static function fromXml(int $status, string $xml): ResponseInterface
|