Browse Source

Support for views #409

Maurits van der Schee 6 years ago
parent
commit
64242e6422

+ 111
- 44
api.php View File

@@ -482,36 +482,34 @@ class ReflectedColumn implements \JsonSerializable
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,20 +519,25 @@ class ReflectedDatabase implements \JsonSerializable
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
 
@@ -542,7 +545,7 @@ class ReflectedDatabase implements \JsonSerializable
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 @@ class ReflectedDatabase implements \JsonSerializable
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 @@ class ReflectedTable implements \JsonSerializable
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 @@ class ReflectedTable implements \JsonSerializable
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 @@ class ReflectedTable implements \JsonSerializable
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 @@ class ReflectedTable implements \JsonSerializable
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 @@ class ReflectionService
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 @@ class ReflectionService
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 @@ class RecordController
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 @@ class RecordController
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 @@ class RecordController
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 @@ class RecordController
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 @@ class RecordController
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,7 +2439,7 @@ class GenericReflection
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
     }
@@ -2414,9 +2447,9 @@ class GenericReflection
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 @@ class GenericReflection
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 @@ class OpenApiBuilder
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 @@ class RecordService
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;

+ 1
- 1
src/Tqdev/PhpCrudApi/Record/RecordService.php View File

@@ -50,7 +50,7 @@ class RecordService
50 50
         return $this->reflection->hasTable($table);
51 51
     }
52 52
 
53
-    public function getType(String $table): bool
53
+    public function getType(String $table): String
54 54
     {
55 55
         return $this->reflection->getType($table);
56 56
     }

+ 15
- 0
tests/functional/001_records/075_list_tag_usage.log View File

@@ -0,0 +1,15 @@
1
+GET /records/tag_usage
2
+===
3
+200
4
+Content-Type: application/json
5
+Content-Length: 71
6
+
7
+{"records":[{"name":"funny","count":2},{"name":"important","count":2}]}
8
+===
9
+GET /records/tag_usage/1
10
+===
11
+405
12
+Content-Type: application/json
13
+Content-Length: 56
14
+
15
+{"code":1015,"message":"Operation 'read' not supported"}

Loading…
Cancel
Save