@@ -482,36 +482,34 @@ public function jsonSerialize()
482482class ReflectedDatabase implements \JsonSerializable
483483{
484484 private $ name ;
485- private $ tableNames ;
485+ private $ tableTypes ;
486486
487- public function __construct (String $ name , array $ tableNames )
487+ public function __construct (String $ name , array $ tableTypes )
488488 {
489489 $ this ->name = $ name ;
490- $ this ->tableNames = [];
491- foreach ($ tableNames as $ tableName ) {
492- $ this ->tableNames [$ tableName ] = true ;
493- }
490+ $ this ->tableTypes = $ tableTypes ;
494491 }
495492
496493 public static function fromReflection (GenericReflection $ reflection ): ReflectedDatabase
497494 {
498495 $ name = $ reflection ->getDatabaseName ();
499- $ tableNames = [];
496+ $ tableTypes = [];
500497 foreach ($ reflection ->getTables () as $ table ) {
501498 $ tableName = $ table ['TABLE_NAME ' ];
499+ $ tableType = $ table ['TABLE_TYPE ' ];
502500 if (in_array ($ tableName , $ reflection ->getIgnoredTables ())) {
503501 continue ;
504502 }
505- $ tableNames [$ tableName ] = true ;
503+ $ tableTypes [$ tableName ] = $ tableType ;
506504 }
507- return new ReflectedDatabase ($ name , array_keys ( $ tableNames ) );
505+ return new ReflectedDatabase ($ name , $ tableTypes );
508506 }
509507
510508 public static function fromJson ( /* object */ $ json ): ReflectedDatabase
511509 {
512510 $ name = $ json ->name ;
513- $ tableNames = $ json ->tables ;
514- return new ReflectedDatabase ($ name , $ tableNames );
511+ $ tableTypes = ( array ) $ json ->tables ;
512+ return new ReflectedDatabase ($ name , $ tableTypes );
515513 }
516514
517515 public function getName (): String
@@ -521,28 +519,33 @@ public function getName(): String
521519
522520 public function hasTable (String $ tableName ): bool
523521 {
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 ] : '' ;
525528 }
526529
527530 public function getTableNames (): array
528531 {
529- return array_keys ($ this ->tableNames );
532+ return array_keys ($ this ->tableTypes );
530533 }
531534
532535 public function removeTable (String $ tableName ): bool
533536 {
534- if (!isset ($ this ->tableNames [$ tableName ])) {
537+ if (!isset ($ this ->tableTypes [$ tableName ])) {
535538 return false ;
536539 }
537- unset($ this ->tableNames [$ tableName ]);
540+ unset($ this ->tableTypes [$ tableName ]);
538541 return true ;
539542 }
540543
541544 public function serialize ()
542545 {
543546 return [
544547 'name ' => $ this ->name ,
545- 'tables ' => array_keys ( $ this ->tableNames ) ,
548+ 'tables ' => $ this ->tableTypes ,
546549 ];
547550 }
548551
@@ -557,13 +560,15 @@ public function jsonSerialize()
557560class ReflectedTable implements \JsonSerializable
558561{
559562 private $ name ;
563+ private $ type ;
560564 private $ columns ;
561565 private $ pk ;
562566 private $ fks ;
563567
564- public function __construct (String $ name , array $ columns )
568+ public function __construct (String $ name , String $ type , array $ columns )
565569 {
566570 $ this ->name = $ name ;
571+ $ this ->type = $ type ;
567572 $ this ->columns = [];
568573 foreach ($ columns as $ column ) {
569574 $ columnName = $ column ->getName ();
@@ -585,10 +590,10 @@ public function __construct(String $name, array $columns)
585590 }
586591 }
587592
588- public static function fromReflection (GenericReflection $ reflection , String $ name ): ReflectedTable
593+ public static function fromReflection (GenericReflection $ reflection , String $ name, String $ type ): ReflectedTable
589594 {
590595 $ columns = [];
591- foreach ($ reflection ->getTableColumns ($ name ) as $ tableColumn ) {
596+ foreach ($ reflection ->getTableColumns ($ name, $ type ) as $ tableColumn ) {
592597 $ column = ReflectedColumn::fromReflection ($ reflection , $ tableColumn );
593598 $ columns [$ column ->getName ()] = $ column ;
594599 }
@@ -604,19 +609,20 @@ public static function fromReflection(GenericReflection $reflection, String $nam
604609 foreach ($ fks as $ columnName => $ table ) {
605610 $ columns [$ columnName ]->setFk ($ table );
606611 }
607- return new ReflectedTable ($ name , array_values ($ columns ));
612+ return new ReflectedTable ($ name , $ type , array_values ($ columns ));
608613 }
609614
610615 public static function fromJson ( /* object */ $ json ): ReflectedTable
611616 {
612617 $ name = $ json ->name ;
618+ $ type = $ json ->type ;
613619 $ columns = [];
614620 if (isset ($ json ->columns ) && is_array ($ json ->columns )) {
615621 foreach ($ json ->columns as $ column ) {
616622 $ columns [] = ReflectedColumn::fromJson ($ column );
617623 }
618624 }
619- return new ReflectedTable ($ name , $ columns );
625+ return new ReflectedTable ($ name , $ type , $ columns );
620626 }
621627
622628 public function hasColumn (String $ columnName ): bool
@@ -639,6 +645,11 @@ public function getName(): String
639645 return $ this ->name ;
640646 }
641647
648+ public function getType (): String
649+ {
650+ return $ this ->type ;
651+ }
652+
642653 public function columnNames (): array
643654 {
644655 return array_keys ($ this ->columns );
@@ -673,6 +684,7 @@ public function serialize()
673684 {
674685 return [
675686 'name ' => $ this ->name ,
687+ 'type ' => $ this ->type ,
676688 'columns ' => array_values ($ this ->columns ),
677689 ];
678690 }
@@ -871,7 +883,8 @@ private function loadTable(String $tableName, bool $useCache): ReflectedTable
871883 if ($ data != '' ) {
872884 $ table = ReflectedTable::fromJson (json_decode (gzuncompress ($ data )));
873885 } else {
874- $ table = ReflectedTable::fromReflection ($ this ->db ->reflection (), $ tableName );
886+ $ tableType = $ this ->database ->getType ($ tableName );
887+ $ table = ReflectedTable::fromReflection ($ this ->db ->reflection (), $ tableName , $ tableType );
875888 $ data = gzcompress (json_encode ($ table , JSON_UNESCAPED_UNICODE ));
876889 $ this ->cache ->set ("ReflectedTable( $ tableName) " , $ data , $ this ->ttl );
877890 }
@@ -893,6 +906,11 @@ public function hasTable(String $tableName): bool
893906 return $ this ->database ->hasTable ($ tableName );
894907 }
895908
909+ public function getType (String $ tableName ): String
910+ {
911+ return $ this ->database ->getType ($ tableName );
912+ }
913+
896914 public function getTable (String $ tableName ): ReflectedTable
897915 {
898916 if (!isset ($ this ->tables [$ tableName ])) {
@@ -1147,11 +1165,14 @@ public function _list(Request $request): Response
11471165 public function read (Request $ request ): Response
11481166 {
11491167 $ table = $ request ->getPathSegment (2 );
1150- $ id = $ request ->getPathSegment (3 );
1151- $ params = $ request ->getParams ();
11521168 if (!$ this ->service ->hasTable ($ table )) {
11531169 return $ this ->responder ->error (ErrorCode::TABLE_NOT_FOUND , $ table );
11541170 }
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 ();
11551176 if (strpos ($ id , ', ' ) !== false ) {
11561177 $ ids = explode (', ' , $ id );
11571178 $ result = [];
@@ -1171,14 +1192,17 @@ public function read(Request $request): Response
11711192 public function create (Request $ request ): Response
11721193 {
11731194 $ 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+ }
11741201 $ record = $ request ->getBody ();
11751202 if ($ record === null ) {
11761203 return $ this ->responder ->error (ErrorCode::HTTP_MESSAGE_NOT_READABLE , '' );
11771204 }
11781205 $ params = $ request ->getParams ();
1179- if (!$ this ->service ->hasTable ($ table )) {
1180- return $ this ->responder ->error (ErrorCode::TABLE_NOT_FOUND , $ table );
1181- }
11821206 if (is_array ($ record )) {
11831207 $ result = array ();
11841208 foreach ($ record as $ r ) {
@@ -1193,15 +1217,18 @@ public function create(Request $request): Response
11931217 public function update (Request $ request ): Response
11941218 {
11951219 $ 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+ }
11961226 $ id = $ request ->getPathSegment (3 );
1227+ $ params = $ request ->getParams ();
11971228 $ record = $ request ->getBody ();
11981229 if ($ record === null ) {
11991230 return $ this ->responder ->error (ErrorCode::HTTP_MESSAGE_NOT_READABLE , '' );
12001231 }
1201- $ params = $ request ->getParams ();
1202- if (!$ this ->service ->hasTable ($ table )) {
1203- return $ this ->responder ->error (ErrorCode::TABLE_NOT_FOUND , $ table );
1204- }
12051232 $ ids = explode (', ' , $ id );
12061233 if (is_array ($ record )) {
12071234 if (count ($ ids ) != count ($ record )) {
@@ -1223,11 +1250,14 @@ public function update(Request $request): Response
12231250 public function delete (Request $ request ): Response
12241251 {
12251252 $ table = $ request ->getPathSegment (2 );
1226- $ id = $ request ->getPathSegment (3 );
1227- $ params = $ request ->getParams ();
12281253 if (!$ this ->service ->hasTable ($ table )) {
12291254 return $ this ->responder ->error (ErrorCode::TABLE_NOT_FOUND , $ table );
12301255 }
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 ();
12311261 $ ids = explode (', ' , $ id );
12321262 if (count ($ ids ) > 1 ) {
12331263 $ result = array ();
@@ -1243,15 +1273,18 @@ public function delete(Request $request): Response
12431273 public function increment (Request $ request ): Response
12441274 {
12451275 $ 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+ }
12461282 $ id = $ request ->getPathSegment (3 );
12471283 $ record = $ request ->getBody ();
12481284 if ($ record === null ) {
12491285 return $ this ->responder ->error (ErrorCode::HTTP_MESSAGE_NOT_READABLE , '' );
12501286 }
12511287 $ params = $ request ->getParams ();
1252- if (!$ this ->service ->hasTable ($ table )) {
1253- return $ this ->responder ->error (ErrorCode::TABLE_NOT_FOUND , $ table );
1254- }
12551288 $ ids = explode (', ' , $ id );
12561289 if (is_array ($ record )) {
12571290 if (count ($ ids ) != count ($ record )) {
@@ -2406,17 +2439,17 @@ public function getIgnoredTables(): array
24062439 {
24072440 switch ($ this ->driver ) {
24082441 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 ' ];
24102443 case 'sqlsrv ' :return [];
24112444 }
24122445 }
24132446
24142447 private function getTablesSQL (): String
24152448 {
24162449 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" ' ;
24202453 }
24212454 }
24222455
@@ -2455,13 +2488,36 @@ public function getDatabaseName(): String
24552488 public function getTables (): array
24562489 {
24572490 $ 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 ;
24592509 }
24602510
2461- public function getTableColumns (String $ tableName ): array
2511+ public function getTableColumns (String $ tableName, String $ type ): array
24622512 {
24632513 $ 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 ;
24652521 }
24662522
24672523 public function getTablePrimaryKeys (String $ tableName ): array
@@ -3494,12 +3550,16 @@ private function isOperationOnColumnAllowed(String $operation, String $tableName
34943550 private function setPath (String $ tableName ) /*: void*/
34953551 {
34963552 $ table = $ this ->reflection ->getTable ($ tableName );
3553+ $ type = $ table ->getType ($ tableName );
34973554 $ pk = $ table ->getPk ();
34983555 $ pkName = $ pk ? $ pk ->getName () : '' ;
34993556 foreach ($ this ->operations as $ operation => $ method ) {
35003557 if (!$ pkName && $ operation != 'list ' ) {
35013558 continue ;
35023559 }
3560+ if ($ type != 'table ' && $ operation != 'list ' ) {
3561+ continue ;
3562+ }
35033563 if (!$ this ->isOperationOnTableAllowed ($ operation , $ tableName )) {
35043564 continue ;
35053565 }
@@ -4041,6 +4101,7 @@ class ErrorCode
40414101 const ACCESS_DENIED = 1012 ;
40424102 const INPUT_VALIDATION_FAILED = 1013 ;
40434103 const OPERATION_FORBIDDEN = 1014 ;
4104+ const OPERATION_NOT_SUPPORTED = 1015 ;
40444105
40454106 private $ values = [
40464107 9999 => ["%s " , Response::INTERNAL_SERVER_ERROR ],
@@ -4059,6 +4120,7 @@ class ErrorCode
40594120 1012 => ["Access denied for '%s' " , Response::FORBIDDEN ],
40604121 1013 => ["Input validation failed for '%s' " , Response::UNPROCESSABLE_ENTITY ],
40614122 1014 => ["Operation forbidden " , Response::FORBIDDEN ],
4123+ 1015 => ["Operation '%s' not supported " , Response::METHOD_NOT_ALLOWED ],
40624124 ];
40634125
40644126 public function __construct (int $ code )
@@ -4370,6 +4432,11 @@ public function hasTable(String $table): bool
43704432 return $ this ->reflection ->hasTable ($ table );
43714433 }
43724434
4435+ public function getType (String $ table ): String
4436+ {
4437+ return $ this ->reflection ->getType ($ table );
4438+ }
4439+
43734440 public function create (String $ tableName , /* object */ $ record , array $ params )
43744441 {
43754442 $ this ->sanitizeRecord ($ tableName , $ record , '' );
@@ -5231,10 +5298,10 @@ class Response
52315298 const UNAUTHORIZED = 401 ;
52325299 const FORBIDDEN = 403 ;
52335300 const NOT_FOUND = 404 ;
5301+ const METHOD_NOT_ALLOWED = 405 ;
52345302 const CONFLICT = 409 ;
52355303 const UNPROCESSABLE_ENTITY = 422 ;
52365304 const INTERNAL_SERVER_ERROR = 500 ;
5237-
52385305 private $ status ;
52395306 private $ headers ;
52405307 private $ body ;
0 commit comments