@@ -482,36 +482,34 @@ public function jsonSerialize()
482
482
class ReflectedDatabase implements \JsonSerializable
483
483
{
484
484
private $ name ;
485
- private $ tableNames ;
485
+ private $ tableTypes ;
486
486
487
- public function __construct (String $ name , array $ tableNames )
487
+ public function __construct (String $ name , array $ tableTypes )
488
488
{
489
489
$ this ->name = $ name ;
490
- $ this ->tableNames = [];
491
- foreach ($ tableNames as $ tableName ) {
492
- $ this ->tableNames [$ tableName ] = true ;
493
- }
490
+ $ this ->tableTypes = $ tableTypes ;
494
491
}
495
492
496
493
public static function fromReflection (GenericReflection $ reflection ): ReflectedDatabase
497
494
{
498
495
$ name = $ reflection ->getDatabaseName ();
499
- $ tableNames = [];
496
+ $ tableTypes = [];
500
497
foreach ($ reflection ->getTables () as $ table ) {
501
498
$ tableName = $ table ['TABLE_NAME ' ];
499
+ $ tableType = $ table ['TABLE_TYPE ' ];
502
500
if (in_array ($ tableName , $ reflection ->getIgnoredTables ())) {
503
501
continue ;
504
502
}
505
- $ tableNames [$ tableName ] = true ;
503
+ $ tableTypes [$ tableName ] = $ tableType ;
506
504
}
507
- return new ReflectedDatabase ($ name , array_keys ( $ tableNames ) );
505
+ return new ReflectedDatabase ($ name , $ tableTypes );
508
506
}
509
507
510
508
public static function fromJson ( /* object */ $ json ): ReflectedDatabase
511
509
{
512
510
$ name = $ json ->name ;
513
- $ tableNames = $ json ->tables ;
514
- return new ReflectedDatabase ($ name , $ tableNames );
511
+ $ tableTypes = ( array ) $ json ->tables ;
512
+ return new ReflectedDatabase ($ name , $ tableTypes );
515
513
}
516
514
517
515
public function getName (): String
@@ -521,28 +519,33 @@ public function getName(): String
521
519
522
520
public function hasTable (String $ tableName ): bool
523
521
{
524
- return isset ($ this ->tableNames [$ tableName ]);
522
+ return isset ($ this ->tableTypes [$ tableName ]);
523
+ }
524
+
525
+ public function getType (String $ tableName ): String
526
+ {
527
+ return isset ($ this ->tableTypes [$ tableName ]) ? $ this ->tableTypes [$ tableName ] : '' ;
525
528
}
526
529
527
530
public function getTableNames (): array
528
531
{
529
- return array_keys ($ this ->tableNames );
532
+ return array_keys ($ this ->tableTypes );
530
533
}
531
534
532
535
public function removeTable (String $ tableName ): bool
533
536
{
534
- if (!isset ($ this ->tableNames [$ tableName ])) {
537
+ if (!isset ($ this ->tableTypes [$ tableName ])) {
535
538
return false ;
536
539
}
537
- unset($ this ->tableNames [$ tableName ]);
540
+ unset($ this ->tableTypes [$ tableName ]);
538
541
return true ;
539
542
}
540
543
541
544
public function serialize ()
542
545
{
543
546
return [
544
547
'name ' => $ this ->name ,
545
- 'tables ' => array_keys ( $ this ->tableNames ) ,
548
+ 'tables ' => $ this ->tableTypes ,
546
549
];
547
550
}
548
551
@@ -557,13 +560,15 @@ public function jsonSerialize()
557
560
class ReflectedTable implements \JsonSerializable
558
561
{
559
562
private $ name ;
563
+ private $ type ;
560
564
private $ columns ;
561
565
private $ pk ;
562
566
private $ fks ;
563
567
564
- public function __construct (String $ name , array $ columns )
568
+ public function __construct (String $ name , String $ type , array $ columns )
565
569
{
566
570
$ this ->name = $ name ;
571
+ $ this ->type = $ type ;
567
572
$ this ->columns = [];
568
573
foreach ($ columns as $ column ) {
569
574
$ columnName = $ column ->getName ();
@@ -585,10 +590,10 @@ public function __construct(String $name, array $columns)
585
590
}
586
591
}
587
592
588
- public static function fromReflection (GenericReflection $ reflection , String $ name ): ReflectedTable
593
+ public static function fromReflection (GenericReflection $ reflection , String $ name, String $ type ): ReflectedTable
589
594
{
590
595
$ columns = [];
591
- foreach ($ reflection ->getTableColumns ($ name ) as $ tableColumn ) {
596
+ foreach ($ reflection ->getTableColumns ($ name, $ type ) as $ tableColumn ) {
592
597
$ column = ReflectedColumn::fromReflection ($ reflection , $ tableColumn );
593
598
$ columns [$ column ->getName ()] = $ column ;
594
599
}
@@ -604,19 +609,20 @@ public static function fromReflection(GenericReflection $reflection, String $nam
604
609
foreach ($ fks as $ columnName => $ table ) {
605
610
$ columns [$ columnName ]->setFk ($ table );
606
611
}
607
- return new ReflectedTable ($ name , array_values ($ columns ));
612
+ return new ReflectedTable ($ name , $ type , array_values ($ columns ));
608
613
}
609
614
610
615
public static function fromJson ( /* object */ $ json ): ReflectedTable
611
616
{
612
617
$ name = $ json ->name ;
618
+ $ type = $ json ->type ;
613
619
$ columns = [];
614
620
if (isset ($ json ->columns ) && is_array ($ json ->columns )) {
615
621
foreach ($ json ->columns as $ column ) {
616
622
$ columns [] = ReflectedColumn::fromJson ($ column );
617
623
}
618
624
}
619
- return new ReflectedTable ($ name , $ columns );
625
+ return new ReflectedTable ($ name , $ type , $ columns );
620
626
}
621
627
622
628
public function hasColumn (String $ columnName ): bool
@@ -639,6 +645,11 @@ public function getName(): String
639
645
return $ this ->name ;
640
646
}
641
647
648
+ public function getType (): String
649
+ {
650
+ return $ this ->type ;
651
+ }
652
+
642
653
public function columnNames (): array
643
654
{
644
655
return array_keys ($ this ->columns );
@@ -673,6 +684,7 @@ public function serialize()
673
684
{
674
685
return [
675
686
'name ' => $ this ->name ,
687
+ 'type ' => $ this ->type ,
676
688
'columns ' => array_values ($ this ->columns ),
677
689
];
678
690
}
@@ -871,7 +883,8 @@ private function loadTable(String $tableName, bool $useCache): ReflectedTable
871
883
if ($ data != '' ) {
872
884
$ table = ReflectedTable::fromJson (json_decode (gzuncompress ($ data )));
873
885
} else {
874
- $ table = ReflectedTable::fromReflection ($ this ->db ->reflection (), $ tableName );
886
+ $ tableType = $ this ->database ->getType ($ tableName );
887
+ $ table = ReflectedTable::fromReflection ($ this ->db ->reflection (), $ tableName , $ tableType );
875
888
$ data = gzcompress (json_encode ($ table , JSON_UNESCAPED_UNICODE ));
876
889
$ this ->cache ->set ("ReflectedTable( $ tableName) " , $ data , $ this ->ttl );
877
890
}
@@ -893,6 +906,11 @@ public function hasTable(String $tableName): bool
893
906
return $ this ->database ->hasTable ($ tableName );
894
907
}
895
908
909
+ public function getType (String $ tableName ): String
910
+ {
911
+ return $ this ->database ->getType ($ tableName );
912
+ }
913
+
896
914
public function getTable (String $ tableName ): ReflectedTable
897
915
{
898
916
if (!isset ($ this ->tables [$ tableName ])) {
@@ -1147,11 +1165,14 @@ public function _list(Request $request): Response
1147
1165
public function read (Request $ request ): Response
1148
1166
{
1149
1167
$ table = $ request ->getPathSegment (2 );
1150
- $ id = $ request ->getPathSegment (3 );
1151
- $ params = $ request ->getParams ();
1152
1168
if (!$ this ->service ->hasTable ($ table )) {
1153
1169
return $ this ->responder ->error (ErrorCode::TABLE_NOT_FOUND , $ table );
1154
1170
}
1171
+ if ($ this ->service ->getType ($ table ) != 'table ' ) {
1172
+ return $ this ->responder ->error (ErrorCode::OPERATION_NOT_SUPPORTED , __FUNCTION__ );
1173
+ }
1174
+ $ id = $ request ->getPathSegment (3 );
1175
+ $ params = $ request ->getParams ();
1155
1176
if (strpos ($ id , ', ' ) !== false ) {
1156
1177
$ ids = explode (', ' , $ id );
1157
1178
$ result = [];
@@ -1171,14 +1192,17 @@ public function read(Request $request): Response
1171
1192
public function create (Request $ request ): Response
1172
1193
{
1173
1194
$ table = $ request ->getPathSegment (2 );
1195
+ if (!$ this ->service ->hasTable ($ table )) {
1196
+ return $ this ->responder ->error (ErrorCode::TABLE_NOT_FOUND , $ table );
1197
+ }
1198
+ if ($ this ->service ->getType ($ table ) != 'table ' ) {
1199
+ return $ this ->responder ->error (ErrorCode::OPERATION_NOT_SUPPORTED , __FUNCTION__ );
1200
+ }
1174
1201
$ record = $ request ->getBody ();
1175
1202
if ($ record === null ) {
1176
1203
return $ this ->responder ->error (ErrorCode::HTTP_MESSAGE_NOT_READABLE , '' );
1177
1204
}
1178
1205
$ params = $ request ->getParams ();
1179
- if (!$ this ->service ->hasTable ($ table )) {
1180
- return $ this ->responder ->error (ErrorCode::TABLE_NOT_FOUND , $ table );
1181
- }
1182
1206
if (is_array ($ record )) {
1183
1207
$ result = array ();
1184
1208
foreach ($ record as $ r ) {
@@ -1193,15 +1217,18 @@ public function create(Request $request): Response
1193
1217
public function update (Request $ request ): Response
1194
1218
{
1195
1219
$ table = $ request ->getPathSegment (2 );
1220
+ if (!$ this ->service ->hasTable ($ table )) {
1221
+ return $ this ->responder ->error (ErrorCode::TABLE_NOT_FOUND , $ table );
1222
+ }
1223
+ if ($ this ->service ->getType ($ table ) != 'table ' ) {
1224
+ return $ this ->responder ->error (ErrorCode::OPERATION_NOT_SUPPORTED , __FUNCTION__ );
1225
+ }
1196
1226
$ id = $ request ->getPathSegment (3 );
1227
+ $ params = $ request ->getParams ();
1197
1228
$ record = $ request ->getBody ();
1198
1229
if ($ record === null ) {
1199
1230
return $ this ->responder ->error (ErrorCode::HTTP_MESSAGE_NOT_READABLE , '' );
1200
1231
}
1201
- $ params = $ request ->getParams ();
1202
- if (!$ this ->service ->hasTable ($ table )) {
1203
- return $ this ->responder ->error (ErrorCode::TABLE_NOT_FOUND , $ table );
1204
- }
1205
1232
$ ids = explode (', ' , $ id );
1206
1233
if (is_array ($ record )) {
1207
1234
if (count ($ ids ) != count ($ record )) {
@@ -1223,11 +1250,14 @@ public function update(Request $request): Response
1223
1250
public function delete (Request $ request ): Response
1224
1251
{
1225
1252
$ table = $ request ->getPathSegment (2 );
1226
- $ id = $ request ->getPathSegment (3 );
1227
- $ params = $ request ->getParams ();
1228
1253
if (!$ this ->service ->hasTable ($ table )) {
1229
1254
return $ this ->responder ->error (ErrorCode::TABLE_NOT_FOUND , $ table );
1230
1255
}
1256
+ if ($ this ->service ->getType ($ table ) != 'table ' ) {
1257
+ return $ this ->responder ->error (ErrorCode::OPERATION_NOT_SUPPORTED , __FUNCTION__ );
1258
+ }
1259
+ $ id = $ request ->getPathSegment (3 );
1260
+ $ params = $ request ->getParams ();
1231
1261
$ ids = explode (', ' , $ id );
1232
1262
if (count ($ ids ) > 1 ) {
1233
1263
$ result = array ();
@@ -1243,15 +1273,18 @@ public function delete(Request $request): Response
1243
1273
public function increment (Request $ request ): Response
1244
1274
{
1245
1275
$ table = $ request ->getPathSegment (2 );
1276
+ if (!$ this ->service ->hasTable ($ table )) {
1277
+ return $ this ->responder ->error (ErrorCode::TABLE_NOT_FOUND , $ table );
1278
+ }
1279
+ if ($ this ->service ->getType ($ table ) != 'table ' ) {
1280
+ return $ this ->responder ->error (ErrorCode::OPERATION_NOT_SUPPORTED , __FUNCTION__ );
1281
+ }
1246
1282
$ id = $ request ->getPathSegment (3 );
1247
1283
$ record = $ request ->getBody ();
1248
1284
if ($ record === null ) {
1249
1285
return $ this ->responder ->error (ErrorCode::HTTP_MESSAGE_NOT_READABLE , '' );
1250
1286
}
1251
1287
$ params = $ request ->getParams ();
1252
- if (!$ this ->service ->hasTable ($ table )) {
1253
- return $ this ->responder ->error (ErrorCode::TABLE_NOT_FOUND , $ table );
1254
- }
1255
1288
$ ids = explode (', ' , $ id );
1256
1289
if (is_array ($ record )) {
1257
1290
if (count ($ ids ) != count ($ record )) {
@@ -2406,17 +2439,17 @@ public function getIgnoredTables(): array
2406
2439
{
2407
2440
switch ($ this ->driver ) {
2408
2441
case 'mysql ' :return [];
2409
- case 'pgsql ' :return ['spatial_ref_sys ' ];
2442
+ case 'pgsql ' :return ['spatial_ref_sys ' , ' raster_columns ' , ' raster_overviews ' , ' geography_columns ' , ' geometry_columns ' ];
2410
2443
case 'sqlsrv ' :return [];
2411
2444
}
2412
2445
}
2413
2446
2414
2447
private function getTablesSQL (): String
2415
2448
{
2416
2449
switch ($ this ->driver ) {
2417
- case 'mysql ' :return 'SELECT "TABLE_NAME" FROM "INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_TYPE" IN ( \'BASE TABLE \') AND "TABLE_SCHEMA" = ? ORDER BY BINARY "TABLE_NAME" ' ;
2418
- case 'pgsql ' :return 'SELECT c.relname as "TABLE_NAME" FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ( \'r \') AND n.nspname <> \'pg_catalog \' AND n.nspname <> \'information_schema \' AND n.nspname !~ \'^pg_toast \' AND pg_catalog.pg_table_is_visible(c.oid) AND \'\' <> ? ORDER BY "TABLE_NAME"; ' ;
2419
- case 'sqlsrv ' :return 'SELECT o.name as "TABLE_NAME" FROM sysobjects o WHERE o.xtype = \'U \' ORDER BY "TABLE_NAME" ' ;
2450
+ case 'mysql ' :return 'SELECT "TABLE_NAME", "TABLE_TYPE" FROM "INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_TYPE" IN ( \'BASE TABLE \' , \' VIEW \') AND "TABLE_SCHEMA" = ? ORDER BY BINARY "TABLE_NAME" ' ;
2451
+ case 'pgsql ' :return 'SELECT c.relname as "TABLE_NAME", c.relkind as "TABLE_TYPE" FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ( \'r \' , \' v \') AND n.nspname <> \'pg_catalog \' AND n.nspname <> \'information_schema \' AND n.nspname !~ \'^pg_toast \' AND pg_catalog.pg_table_is_visible(c.oid) AND \'\' <> ? ORDER BY "TABLE_NAME"; ' ;
2452
+ case 'sqlsrv ' :return 'SELECT o.name as "TABLE_NAME", o.xtype as "TABLE_TYPE" FROM sysobjects o WHERE o.xtype IN ( \'U \', \' V \' ) ORDER BY "TABLE_NAME" ' ;
2420
2453
}
2421
2454
}
2422
2455
@@ -2455,13 +2488,36 @@ public function getDatabaseName(): String
2455
2488
public function getTables (): array
2456
2489
{
2457
2490
$ sql = $ this ->getTablesSQL ();
2458
- return $ this ->query ($ sql , [$ this ->database ]);
2491
+ $ results = $ this ->query ($ sql , [$ this ->database ]);
2492
+ foreach ($ results as &$ result ) {
2493
+ switch ($ this ->driver ) {
2494
+ case 'mysql ' :
2495
+ $ map = ['BASE TABLE ' => 'table ' , 'VIEW ' => 'view ' ];
2496
+ $ result ['TABLE_TYPE ' ] = $ map [$ result ['TABLE_TYPE ' ]];
2497
+ break ;
2498
+ case 'pgsql ' :
2499
+ $ map = ['r ' => 'table ' , 'v ' => 'view ' ];
2500
+ $ result ['TABLE_TYPE ' ] = $ map [$ result ['TABLE_TYPE ' ]];
2501
+ break ;
2502
+ case 'sqlsrv ' :
2503
+ $ map = ['U ' => 'table ' , 'V ' => 'view ' ];
2504
+ $ result ['TABLE_TYPE ' ] = $ map [trim ($ result ['TABLE_TYPE ' ])];
2505
+ break ;
2506
+ }
2507
+ }
2508
+ return $ results ;
2459
2509
}
2460
2510
2461
- public function getTableColumns (String $ tableName ): array
2511
+ public function getTableColumns (String $ tableName, String $ type ): array
2462
2512
{
2463
2513
$ sql = $ this ->getTableColumnsSQL ();
2464
- return $ this ->query ($ sql , [$ tableName , $ this ->database ]);
2514
+ $ results = $ this ->query ($ sql , [$ tableName , $ this ->database ]);
2515
+ if ($ type == 'view ' ) {
2516
+ foreach ($ results as &$ result ) {
2517
+ $ result ['IS_NULLABLE ' ] = false ;
2518
+ }
2519
+ }
2520
+ return $ results ;
2465
2521
}
2466
2522
2467
2523
public function getTablePrimaryKeys (String $ tableName ): array
@@ -3494,12 +3550,16 @@ private function isOperationOnColumnAllowed(String $operation, String $tableName
3494
3550
private function setPath (String $ tableName ) /*: void*/
3495
3551
{
3496
3552
$ table = $ this ->reflection ->getTable ($ tableName );
3553
+ $ type = $ table ->getType ($ tableName );
3497
3554
$ pk = $ table ->getPk ();
3498
3555
$ pkName = $ pk ? $ pk ->getName () : '' ;
3499
3556
foreach ($ this ->operations as $ operation => $ method ) {
3500
3557
if (!$ pkName && $ operation != 'list ' ) {
3501
3558
continue ;
3502
3559
}
3560
+ if ($ type != 'table ' && $ operation != 'list ' ) {
3561
+ continue ;
3562
+ }
3503
3563
if (!$ this ->isOperationOnTableAllowed ($ operation , $ tableName )) {
3504
3564
continue ;
3505
3565
}
@@ -4041,6 +4101,7 @@ class ErrorCode
4041
4101
const ACCESS_DENIED = 1012 ;
4042
4102
const INPUT_VALIDATION_FAILED = 1013 ;
4043
4103
const OPERATION_FORBIDDEN = 1014 ;
4104
+ const OPERATION_NOT_SUPPORTED = 1015 ;
4044
4105
4045
4106
private $ values = [
4046
4107
9999 => ["%s " , Response::INTERNAL_SERVER_ERROR ],
@@ -4059,6 +4120,7 @@ class ErrorCode
4059
4120
1012 => ["Access denied for '%s' " , Response::FORBIDDEN ],
4060
4121
1013 => ["Input validation failed for '%s' " , Response::UNPROCESSABLE_ENTITY ],
4061
4122
1014 => ["Operation forbidden " , Response::FORBIDDEN ],
4123
+ 1015 => ["Operation '%s' not supported " , Response::METHOD_NOT_ALLOWED ],
4062
4124
];
4063
4125
4064
4126
public function __construct (int $ code )
@@ -4370,6 +4432,11 @@ public function hasTable(String $table): bool
4370
4432
return $ this ->reflection ->hasTable ($ table );
4371
4433
}
4372
4434
4435
+ public function getType (String $ table ): String
4436
+ {
4437
+ return $ this ->reflection ->getType ($ table );
4438
+ }
4439
+
4373
4440
public function create (String $ tableName , /* object */ $ record , array $ params )
4374
4441
{
4375
4442
$ this ->sanitizeRecord ($ tableName , $ record , '' );
@@ -5231,10 +5298,10 @@ class Response
5231
5298
const UNAUTHORIZED = 401 ;
5232
5299
const FORBIDDEN = 403 ;
5233
5300
const NOT_FOUND = 404 ;
5301
+ const METHOD_NOT_ALLOWED = 405 ;
5234
5302
const CONFLICT = 409 ;
5235
5303
const UNPROCESSABLE_ENTITY = 422 ;
5236
5304
const INTERNAL_SERVER_ERROR = 500 ;
5237
-
5238
5305
private $ status ;
5239
5306
private $ headers ;
5240
5307
private $ body ;
0 commit comments