Skip to content

Commit d4eda44

Browse files
committed
sqlsrv support
1 parent 833913e commit d4eda44

File tree

6 files changed

+238
-16
lines changed

6 files changed

+238
-16
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@ You can enable the following middleware using the "middlewares" config parameter
636636
- "pageLimits": Restricts list operations to prevent database scraping
637637
- "joinLimits": Restricts join parameters to prevent database scraping
638638
- "customization": Provides handlers for request and response customization
639+
- "json": Support read/write of JSON strings as JSON objects/arrays
639640
- "xml": Translates all input and output from JSON to XML
640641

641642
The "middlewares" config parameter is a comma separated list of enabled middlewares.
@@ -703,6 +704,9 @@ You can tune the middleware behavior using middleware specific configuration par
703704
- "joinLimits.records": The maximum number of records returned for a joined entity ("1000")
704705
- "customization.beforeHandler": Handler to implement request customization ("")
705706
- "customization.afterHandler": Handler to implement response customization ("")
707+
- "json.controllers": Controllers to process JSON strings for ("records,geojson")
708+
- "json.tables": Tables to process JSON strings for ("all")
709+
- "json.columns": Columns to process JSON strings for ("all")
706710
- "xml.types": JSON types that should be added to the XML type attribute ("null,array")
707711

708712
If you don't specify these parameters in the configuration, then the default values (between brackets) are used.
@@ -1081,6 +1085,14 @@ You may use the "customization" middleware to modify request and response and im
10811085

10821086
The above example will add a header "X-Time-Taken" with the number of seconds the API call has taken.
10831087

1088+
### JSON middleware
1089+
1090+
You may use the "json" middleware to read/write JSON strings as JSON objects and arrays.
1091+
1092+
'json.columns' => 'details',
1093+
1094+
The "pageLimits" middleware limits the page number and the number records returned from a list operation.
1095+
10841096
### XML middleware
10851097

10861098
You may use the "xml" middleware to translate input and output from JSON to XML. This request:

api.include.php

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7985,6 +7985,107 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
79857985
}
79867986
}
79877987

7988+
// file: src/Tqdev/PhpCrudApi/Middleware/JsonMiddleware.php
7989+
namespace Tqdev\PhpCrudApi\Middleware {
7990+
7991+
use Psr\Http\Message\ResponseInterface;
7992+
use Psr\Http\Message\ServerRequestInterface;
7993+
use Psr\Http\Server\RequestHandlerInterface;
7994+
use Tqdev\PhpCrudApi\Middleware\Base\Middleware;
7995+
use Tqdev\PhpCrudApi\RequestUtils;
7996+
use Tqdev\PhpCrudApi\ResponseFactory;
7997+
7998+
class JsonMiddleware extends Middleware
7999+
{
8000+
private function convertJsonRequestValue($value) /*: object */
8001+
{
8002+
if (is_array($value) || is_object($value)) {
8003+
$value = json_encode($value,JSON_UNESCAPED_UNICODE);
8004+
}
8005+
return $value;
8006+
}
8007+
8008+
private function convertJsonRequest($object, array $columnNames) /*: object */
8009+
{
8010+
if (is_array($object)) {
8011+
foreach ($object as $i => $obj) {
8012+
foreach ($obj as $k => $v) {
8013+
if (in_array('all', $columnNames) || in_array($k, $columnNames)) {
8014+
$object[$i]->$k = $this->convertJsonRequestValue($v);
8015+
}
8016+
}
8017+
}
8018+
} else if (is_object($object)) {
8019+
foreach ($object as $k => $v) {
8020+
if (in_array('all', $columnNames) || in_array($k, $columnNames)) {
8021+
$object->$k = $this->convertJsonRequestValue($v);
8022+
}
8023+
}
8024+
}
8025+
return $object;
8026+
}
8027+
8028+
private function convertJsonResponseValue(string $value) /*: object */
8029+
{
8030+
if (strlen($value) > 0 && in_array($value[0],['[','{'])) {
8031+
$parsed = json_decode($value);
8032+
if (json_last_error() == JSON_ERROR_NONE) {
8033+
$value = $parsed;
8034+
}
8035+
}
8036+
return $value;
8037+
}
8038+
8039+
private function convertJsonResponse($object, array $columnNames) /*: object */
8040+
{
8041+
if (is_array($object)) {
8042+
foreach ($object as $k => $v) {
8043+
$object[$k] = $this->convertJsonResponse($v, $columnNames);
8044+
}
8045+
} else if (is_object($object)) {
8046+
foreach ($object as $k => $v) {
8047+
if (in_array('all', $columnNames) || in_array($k, $columnNames)) {
8048+
$object->$k = $this->convertJsonResponse($v, $columnNames);
8049+
}
8050+
}
8051+
} else if (is_string($object)) {
8052+
$object = $this->convertJsonResponseValue($object);
8053+
}
8054+
return $object;
8055+
}
8056+
8057+
public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface
8058+
{
8059+
$operation = RequestUtils::getOperation($request);
8060+
$controllerPath = RequestUtils::getPathSegment($request, 1);
8061+
$tableName = RequestUtils::getPathSegment($request, 2);
8062+
8063+
$controllerPaths = $this->getArrayProperty('controllers', 'records,geojson');
8064+
$tableNames = $this->getArrayProperty('tables', 'all');
8065+
$columnNames = $this->getArrayProperty('columns', 'all');
8066+
if (
8067+
(in_array('all', $controllerPaths) || in_array($controllerPath, $controllerPaths)) &&
8068+
(in_array('all', $tableNames) || in_array($tableName, $tableNames))
8069+
) {
8070+
if (in_array($operation, ['create', 'update'])) {
8071+
$records = $request->getParsedBody();
8072+
$records = $this->convertJsonRequest($records,$columnNames);
8073+
$request = $request->withParsedBody($records);
8074+
}
8075+
$response = $next->handle($request);
8076+
if (in_array($operation, ['read', 'list'])) {
8077+
$records = json_decode($response->getBody()->getContents());
8078+
$records = $this->convertJsonResponse($records, $columnNames);
8079+
$response = ResponseFactory::fromObject($response->getStatusCode(), $records);
8080+
}
8081+
} else {
8082+
$response = $next->handle($request);
8083+
}
8084+
return $response;
8085+
}
8086+
}
8087+
}
8088+
79888089
// file: src/Tqdev/PhpCrudApi/Middleware/JwtAuthMiddleware.php
79898090
namespace Tqdev\PhpCrudApi\Middleware {
79908091

@@ -11008,6 +11109,7 @@ private function setHabtmValues(ReflectedTable $t1, ReflectedTable $t2, array &$
1100811109
use Tqdev\PhpCrudApi\Middleware\FirewallMiddleware;
1100911110
use Tqdev\PhpCrudApi\Middleware\IpAddressMiddleware;
1101011111
use Tqdev\PhpCrudApi\Middleware\JoinLimitsMiddleware;
11112+
use Tqdev\PhpCrudApi\Middleware\JsonMiddleware;
1101111113
use Tqdev\PhpCrudApi\Middleware\JwtAuthMiddleware;
1101211114
use Tqdev\PhpCrudApi\Middleware\MultiTenancyMiddleware;
1101311115
use Tqdev\PhpCrudApi\Middleware\PageLimitsMiddleware;
@@ -11098,7 +11200,10 @@ public function __construct(Config $config)
1109811200
case 'xml':
1109911201
new XmlMiddleware($router, $responder, $properties, $reflection);
1110011202
break;
11101-
}
11203+
case 'json':
11204+
new JsonMiddleware($router, $responder, $properties);
11205+
break;
11206+
}
1110211207
}
1110311208
foreach ($config->getControllers() as $controller) {
1110411209
switch ($controller) {

api.php

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7985,6 +7985,107 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
79857985
}
79867986
}
79877987

7988+
// file: src/Tqdev/PhpCrudApi/Middleware/JsonMiddleware.php
7989+
namespace Tqdev\PhpCrudApi\Middleware {
7990+
7991+
use Psr\Http\Message\ResponseInterface;
7992+
use Psr\Http\Message\ServerRequestInterface;
7993+
use Psr\Http\Server\RequestHandlerInterface;
7994+
use Tqdev\PhpCrudApi\Middleware\Base\Middleware;
7995+
use Tqdev\PhpCrudApi\RequestUtils;
7996+
use Tqdev\PhpCrudApi\ResponseFactory;
7997+
7998+
class JsonMiddleware extends Middleware
7999+
{
8000+
private function convertJsonRequestValue($value) /*: object */
8001+
{
8002+
if (is_array($value) || is_object($value)) {
8003+
$value = json_encode($value,JSON_UNESCAPED_UNICODE);
8004+
}
8005+
return $value;
8006+
}
8007+
8008+
private function convertJsonRequest($object, array $columnNames) /*: object */
8009+
{
8010+
if (is_array($object)) {
8011+
foreach ($object as $i => $obj) {
8012+
foreach ($obj as $k => $v) {
8013+
if (in_array('all', $columnNames) || in_array($k, $columnNames)) {
8014+
$object[$i]->$k = $this->convertJsonRequestValue($v);
8015+
}
8016+
}
8017+
}
8018+
} else if (is_object($object)) {
8019+
foreach ($object as $k => $v) {
8020+
if (in_array('all', $columnNames) || in_array($k, $columnNames)) {
8021+
$object->$k = $this->convertJsonRequestValue($v);
8022+
}
8023+
}
8024+
}
8025+
return $object;
8026+
}
8027+
8028+
private function convertJsonResponseValue(string $value) /*: object */
8029+
{
8030+
if (strlen($value) > 0 && in_array($value[0],['[','{'])) {
8031+
$parsed = json_decode($value);
8032+
if (json_last_error() == JSON_ERROR_NONE) {
8033+
$value = $parsed;
8034+
}
8035+
}
8036+
return $value;
8037+
}
8038+
8039+
private function convertJsonResponse($object, array $columnNames) /*: object */
8040+
{
8041+
if (is_array($object)) {
8042+
foreach ($object as $k => $v) {
8043+
$object[$k] = $this->convertJsonResponse($v, $columnNames);
8044+
}
8045+
} else if (is_object($object)) {
8046+
foreach ($object as $k => $v) {
8047+
if (in_array('all', $columnNames) || in_array($k, $columnNames)) {
8048+
$object->$k = $this->convertJsonResponse($v, $columnNames);
8049+
}
8050+
}
8051+
} else if (is_string($object)) {
8052+
$object = $this->convertJsonResponseValue($object);
8053+
}
8054+
return $object;
8055+
}
8056+
8057+
public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface
8058+
{
8059+
$operation = RequestUtils::getOperation($request);
8060+
$controllerPath = RequestUtils::getPathSegment($request, 1);
8061+
$tableName = RequestUtils::getPathSegment($request, 2);
8062+
8063+
$controllerPaths = $this->getArrayProperty('controllers', 'records,geojson');
8064+
$tableNames = $this->getArrayProperty('tables', 'all');
8065+
$columnNames = $this->getArrayProperty('columns', 'all');
8066+
if (
8067+
(in_array('all', $controllerPaths) || in_array($controllerPath, $controllerPaths)) &&
8068+
(in_array('all', $tableNames) || in_array($tableName, $tableNames))
8069+
) {
8070+
if (in_array($operation, ['create', 'update'])) {
8071+
$records = $request->getParsedBody();
8072+
$records = $this->convertJsonRequest($records,$columnNames);
8073+
$request = $request->withParsedBody($records);
8074+
}
8075+
$response = $next->handle($request);
8076+
if (in_array($operation, ['read', 'list'])) {
8077+
$records = json_decode($response->getBody()->getContents());
8078+
$records = $this->convertJsonResponse($records, $columnNames);
8079+
$response = ResponseFactory::fromObject($response->getStatusCode(), $records);
8080+
}
8081+
} else {
8082+
$response = $next->handle($request);
8083+
}
8084+
return $response;
8085+
}
8086+
}
8087+
}
8088+
79888089
// file: src/Tqdev/PhpCrudApi/Middleware/JwtAuthMiddleware.php
79898090
namespace Tqdev\PhpCrudApi\Middleware {
79908091

@@ -11008,6 +11109,7 @@ private function setHabtmValues(ReflectedTable $t1, ReflectedTable $t2, array &$
1100811109
use Tqdev\PhpCrudApi\Middleware\FirewallMiddleware;
1100911110
use Tqdev\PhpCrudApi\Middleware\IpAddressMiddleware;
1101011111
use Tqdev\PhpCrudApi\Middleware\JoinLimitsMiddleware;
11112+
use Tqdev\PhpCrudApi\Middleware\JsonMiddleware;
1101111113
use Tqdev\PhpCrudApi\Middleware\JwtAuthMiddleware;
1101211114
use Tqdev\PhpCrudApi\Middleware\MultiTenancyMiddleware;
1101311115
use Tqdev\PhpCrudApi\Middleware\PageLimitsMiddleware;
@@ -11098,7 +11200,10 @@ public function __construct(Config $config)
1109811200
case 'xml':
1109911201
new XmlMiddleware($router, $responder, $properties, $reflection);
1110011202
break;
11101-
}
11203+
case 'json':
11204+
new JsonMiddleware($router, $responder, $properties);
11205+
break;
11206+
}
1110211207
}
1110311208
foreach ($config->getControllers() as $controller) {
1110411209
switch ($controller) {

src/Tqdev/PhpCrudApi/Middleware/JsonMiddleware.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,19 @@ private function convertJsonRequestValue($value) /*: object */
1919
return $value;
2020
}
2121

22-
private function convertJsonRequest($object, array $fieldNames) /*: object */
22+
private function convertJsonRequest($object, array $columnNames) /*: object */
2323
{
2424
if (is_array($object)) {
2525
foreach ($object as $i => $obj) {
2626
foreach ($obj as $k => $v) {
27-
if (in_array('all', $fieldNames) || in_array($k, $fieldNames)) {
27+
if (in_array('all', $columnNames) || in_array($k, $columnNames)) {
2828
$object[$i]->$k = $this->convertJsonRequestValue($v);
2929
}
3030
}
3131
}
3232
} else if (is_object($object)) {
3333
foreach ($object as $k => $v) {
34-
if (in_array('all', $fieldNames) || in_array($k, $fieldNames)) {
34+
if (in_array('all', $columnNames) || in_array($k, $columnNames)) {
3535
$object->$k = $this->convertJsonRequestValue($v);
3636
}
3737
}
@@ -51,16 +51,16 @@ private function convertJsonResponseValue(string $value) /*: object */
5151
}
5252

5353

54-
private function convertJsonResponse($object, array $fieldNames) /*: object */
54+
private function convertJsonResponse($object, array $columnNames) /*: object */
5555
{
5656
if (is_array($object)) {
5757
foreach ($object as $k => $v) {
58-
$object[$k] = $this->convertJsonResponse($v, $fieldNames);
58+
$object[$k] = $this->convertJsonResponse($v, $columnNames);
5959
}
6060
} else if (is_object($object)) {
6161
foreach ($object as $k => $v) {
62-
if (in_array('all', $fieldNames) || in_array($k, $fieldNames)) {
63-
$object->$k = $this->convertJsonResponse($v, $fieldNames);
62+
if (in_array('all', $columnNames) || in_array($k, $columnNames)) {
63+
$object->$k = $this->convertJsonResponse($v, $columnNames);
6464
}
6565
}
6666
} else if (is_string($object)) {
@@ -75,22 +75,22 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
7575
$controllerPath = RequestUtils::getPathSegment($request, 1);
7676
$tableName = RequestUtils::getPathSegment($request, 2);
7777

78-
$controllerPaths = $this->getArrayProperty('controllers', 'all');
78+
$controllerPaths = $this->getArrayProperty('controllers', 'records,geojson');
7979
$tableNames = $this->getArrayProperty('tables', 'all');
80-
$fieldNames = $this->getArrayProperty('fields', 'all');
80+
$columnNames = $this->getArrayProperty('columns', 'all');
8181
if (
8282
(in_array('all', $controllerPaths) || in_array($controllerPath, $controllerPaths)) &&
8383
(in_array('all', $tableNames) || in_array($tableName, $tableNames))
8484
) {
8585
if (in_array($operation, ['create', 'update'])) {
8686
$records = $request->getParsedBody();
87-
$records = $this->convertJsonRequest($records,$fieldNames);
87+
$records = $this->convertJsonRequest($records,$columnNames);
8888
$request = $request->withParsedBody($records);
8989
}
9090
$response = $next->handle($request);
9191
if (in_array($operation, ['read', 'list'])) {
9292
$records = json_decode($response->getBody()->getContents());
93-
$records = $this->convertJsonResponse($records, $fieldNames);
93+
$records = $this->convertJsonResponse($records, $columnNames);
9494
$response = ResponseFactory::fromObject($response->getStatusCode(), $records);
9595
}
9696
} else {

tests/config/base.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,6 @@
5959
},
6060
'json.controllers' => 'records',
6161
'json.tables' => 'products',
62-
'json.fields' => 'properties',
62+
'json.columns' => 'properties',
6363
'debug' => false,
6464
];

tests/fixtures/blog_sqlsrv.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ CREATE TABLE [products](
258258
[id] [int] NOT NULL CONSTRAINT [products_id_def] DEFAULT NEXT VALUE FOR [products_id_seq],
259259
[name] [nvarchar](255) NOT NULL,
260260
[price] [decimal](10,2) NOT NULL,
261-
[properties] [xml] NOT NULL,
261+
[properties] [ntext] NOT NULL,
262262
[created_at] [datetime2](0) NOT NULL,
263263
[deleted_at] [datetime2](0),
264264
CONSTRAINT [products_pkey] PRIMARY KEY CLUSTERED([id] ASC)
@@ -367,7 +367,7 @@ GO
367367
INSERT [events] ([name], [datetime], [visitors]) VALUES (N'Launch', N'2016-01-01 13:01:01', 0)
368368
GO
369369

370-
INSERT [products] ([name], [price], [properties], [created_at]) VALUES (N'Calculator', N'23.01', N'<root type="object"><depth type="boolean">false</depth><model type="string">TRX-120</model><width type="number">100</width><height type="null" /></root>', '1970-01-01 01:01:01')
370+
INSERT [products] ([name], [price], [properties], [created_at]) VALUES (N'Calculator', N'23.01', N'{"depth":false,"model":"TRX-120","width":100,"height":null}', '1970-01-01 01:01:01')
371371
GO
372372

373373
INSERT [barcodes] ([product_id], [hex], [bin], [ip_address]) VALUES (1, N'00ff01', 0x00ff01, N'127.0.0.1')

0 commit comments

Comments
 (0)