Browse Source

Support reading views

Maurits van der Schee 3 years ago
parent
commit
53279412b2

+ 47
- 24
api.php View File

3879
                 $columns[$column->getName()] = $column;
3879
                 $columns[$column->getName()] = $column;
3880
             }
3880
             }
3881
             // set primary key
3881
             // set primary key
3882
-            $columnNames = $reflection->getTablePrimaryKeys($name);
3883
-            if (count($columnNames) == 1) {
3884
-                $columnName = $columnNames[0];
3885
-                if (isset($columns[$columnName])) {
3886
-                    $pk = $columns[$columnName];
3887
-                    $pk->setPk(true);
3882
+            $columnName = false;
3883
+            if ($type == 'view') {
3884
+                $columnName = 'id';
3885
+            } else {
3886
+                $columnNames = $reflection->getTablePrimaryKeys($name);
3887
+                if (count($columnNames) == 1) {
3888
+                    $columnName = $columnNames[0];
3888
                 }
3889
                 }
3889
             }
3890
             }
3891
+            if ($columnName && isset($columns[$columnName])) {
3892
+                $pk = $columns[$columnName];
3893
+                $pk->setPk(true);
3894
+            }
3890
             // set foreign keys
3895
             // set foreign keys
3891
-            $fks = $reflection->getTableForeignKeys($name);
3892
-            foreach ($fks as $columnName => $table) {
3893
-                $columns[$columnName]->setFk($table);
3896
+            if ($type == 'view') {
3897
+                $tables = $reflection->getTables();
3898
+                foreach ($columns as $columnName => $column) {
3899
+                    if (substr($columnName, -3) == '_id') {
3900
+                        foreach ($tables as $table) {
3901
+                            $tableName = $table['TABLE_NAME'];
3902
+                            $suffix = $tableName . '_id';
3903
+                            if (substr($columnName, -1 * strlen($suffix)) == $suffix) {
3904
+                                $column->setFk($tableName);
3905
+                            }
3906
+                        }
3907
+                    }
3908
+                }
3909
+            } else {
3910
+                $fks = $reflection->getTableForeignKeys($name);
3911
+                foreach ($fks as $columnName => $table) {
3912
+                    $columns[$columnName]->setFk($table);
3913
+                }
3894
             }
3914
             }
3895
             return new ReflectedTable($name, $type, array_values($columns));
3915
             return new ReflectedTable($name, $type, array_values($columns));
3896
         }
3916
         }
3897
 
3917
 
3898
-        public static function fromJson(/* object */$json): ReflectedTable
3918
+        public static function fromJson( /* object */$json): ReflectedTable
3899
         {
3919
         {
3900
             $name = $json->name;
3920
             $name = $json->name;
3901
             $type = isset($json->type) ? $json->type : 'table';
3921
             $type = isset($json->type) ? $json->type : 'table';
4590
             if (!$this->service->hasTable($table)) {
4610
             if (!$this->service->hasTable($table)) {
4591
                 return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table);
4611
                 return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table);
4592
             }
4612
             }
4593
-            if ($this->service->getType($table) != 'table') {
4594
-                return $this->responder->error(ErrorCode::OPERATION_NOT_SUPPORTED, __FUNCTION__);
4595
-            }
4596
             $id = RequestUtils::getPathSegment($request, 3);
4613
             $id = RequestUtils::getPathSegment($request, 3);
4597
             $params = RequestUtils::getParams($request);
4614
             $params = RequestUtils::getParams($request);
4598
             if (strpos($id, ',') !== false) {
4615
             if (strpos($id, ',') !== false) {
6994
                         return substr($fullPath, 0, -1 * strlen($path));
7011
                         return substr($fullPath, 0, -1 * strlen($path));
6995
                     }
7012
                     }
6996
                 }
7013
                 }
6997
-                return $fullPath;
7014
+                if ('/' . basename(__FILE__) == $fullPath) {
7015
+                    return $fullPath;
7016
+                }
6998
             }
7017
             }
6999
             return '/';
7018
             return '/';
7000
         }
7019
         }
9045
 namespace Tqdev\PhpCrudApi\OpenApi {
9064
 namespace Tqdev\PhpCrudApi\OpenApi {
9046
 
9065
 
9047
     use Tqdev\PhpCrudApi\Column\ReflectionService;
9066
     use Tqdev\PhpCrudApi\Column\ReflectionService;
9067
+    use Tqdev\PhpCrudApi\Column\Reflection\ReflectedColumn;
9048
     use Tqdev\PhpCrudApi\Middleware\Communication\VariableStore;
9068
     use Tqdev\PhpCrudApi\Middleware\Communication\VariableStore;
9049
     use Tqdev\PhpCrudApi\OpenApi\OpenApiDefinition;
9069
     use Tqdev\PhpCrudApi\OpenApi\OpenApiDefinition;
9050
-    use Tqdev\PhpCrudApi\Column\Reflection\ReflectedColumn;
9051
 
9070
 
9052
     class OpenApiRecordsBuilder
9071
     class OpenApiRecordsBuilder
9053
     {
9072
     {
9065
             'integer' => ['type' => 'integer', 'format' => 'int32'],
9084
             'integer' => ['type' => 'integer', 'format' => 'int32'],
9066
             'bigint' => ['type' => 'integer', 'format' => 'int64'],
9085
             'bigint' => ['type' => 'integer', 'format' => 'int64'],
9067
             'varchar' => ['type' => 'string'],
9086
             'varchar' => ['type' => 'string'],
9068
-            'clob' => ['type' => 'string', 'format' => 'large-string'],   //custom format
9087
+            'clob' => ['type' => 'string', 'format' => 'large-string'], //custom format
9069
             'varbinary' => ['type' => 'string', 'format' => 'byte'],
9088
             'varbinary' => ['type' => 'string', 'format' => 'byte'],
9070
-            'blob' => ['type' => 'string', 'format' => 'large-byte'],     //custom format
9071
-            'decimal' => ['type' => 'string', 'format' => 'decimal'],     //custom format
9089
+            'blob' => ['type' => 'string', 'format' => 'large-byte'], //custom format
9090
+            'decimal' => ['type' => 'string', 'format' => 'decimal'], //custom format
9072
             'float' => ['type' => 'number', 'format' => 'float'],
9091
             'float' => ['type' => 'number', 'format' => 'float'],
9073
             'double' => ['type' => 'number', 'format' => 'double'],
9092
             'double' => ['type' => 'number', 'format' => 'double'],
9074
             'date' => ['type' => 'string', 'format' => 'date'],
9093
             'date' => ['type' => 'string', 'format' => 'date'],
9075
-            'time' => ['type' => 'string', 'format' => 'time'],           //custom format
9094
+            'time' => ['type' => 'string', 'format' => 'time'], //custom format
9076
             'timestamp' => ['type' => 'string', 'format' => 'date-time'],
9095
             'timestamp' => ['type' => 'string', 'format' => 'date-time'],
9077
-            'geometry' => ['type' => 'string', 'format' => 'geometry'],   //custom format
9096
+            'geometry' => ['type' => 'string', 'format' => 'geometry'], //custom format
9078
             'boolean' => ['type' => 'boolean'],
9097
             'boolean' => ['type' => 'boolean'],
9079
         ];
9098
         ];
9080
 
9099
 
9264
                 if (!$pkName && $operation != 'list') {
9283
                 if (!$pkName && $operation != 'list') {
9265
                     continue;
9284
                     continue;
9266
                 }
9285
                 }
9267
-                if ($type != 'table' && $operation != 'list') {
9286
+                if ($type == 'view' && !in_array($operation, array('read', 'list'))) {
9287
+                    continue;
9288
+                }
9289
+                if ($type == 'view' && !$pkName && $operation == 'read') {
9268
                     continue;
9290
                     continue;
9269
                 }
9291
                 }
9270
                 if ($operation == 'delete') {
9292
                 if ($operation == 'delete') {
11170
     class ResponseFactory
11192
     class ResponseFactory
11171
     {
11193
     {
11172
         const OK = 200;
11194
         const OK = 200;
11195
+        const MOVED_PERMANENTLY = 301;
11196
+        const FOUND = 302;
11173
         const UNAUTHORIZED = 401;
11197
         const UNAUTHORIZED = 401;
11174
         const FORBIDDEN = 403;
11198
         const FORBIDDEN = 403;
11175
         const NOT_FOUND = 404;
11199
         const NOT_FOUND = 404;
11185
 
11209
 
11186
         public static function fromCsv(int $status, string $csv): ResponseInterface
11210
         public static function fromCsv(int $status, string $csv): ResponseInterface
11187
         {
11211
         {
11188
-            $response = self::from($status, 'text/csv', $csv);
11189
-            return $response->withHeader('Content-Type', 'text/csv');
11212
+            return self::from($status, 'text/csv', $csv);
11190
         }
11213
         }
11191
 
11214
 
11192
         public static function fromHtml(int $status, string $html): ResponseInterface
11215
         public static function fromHtml(int $status, string $html): ResponseInterface
11200
             return self::from($status, 'application/json', $content);
11223
             return self::from($status, 'application/json', $content);
11201
         }
11224
         }
11202
 
11225
 
11203
-        private static function from(int $status, string $contentType, string $content): ResponseInterface
11226
+        public static function from(int $status, string $contentType, string $content): ResponseInterface
11204
         {
11227
         {
11205
             $psr17Factory = new Psr17Factory();
11228
             $psr17Factory = new Psr17Factory();
11206
             $response = $psr17Factory->createResponse($status);
11229
             $response = $psr17Factory->createResponse($status);

+ 1
- 1
tests/fixtures/blog_mysql.sql View File

130
 ('Launch', '2016-01-01 13:01:01', 0);
130
 ('Launch', '2016-01-01 13:01:01', 0);
131
 
131
 
132
 DROP VIEW IF EXISTS `tag_usage`;
132
 DROP VIEW IF EXISTS `tag_usage`;
133
-CREATE VIEW `tag_usage` AS select `name`, count(`name`) AS `count` from `tags`, `post_tags` where `tags`.`id` = `post_tags`.`tag_id` group by `name` order by `count` desc, `name`;
133
+CREATE VIEW `tag_usage` AS select `tags`.`id` as `id`, `name`, count(`name`) AS `count` from `tags`, `post_tags` where `tags`.`id` = `post_tags`.`tag_id` group by `tags`.`id`, `name` order by `count` desc, `name`;
134
 
134
 
135
 DROP TABLE IF EXISTS `products`;
135
 DROP TABLE IF EXISTS `products`;
136
 CREATE TABLE `products` (
136
 CREATE TABLE `products` (

+ 1
- 1
tests/fixtures/blog_pgsql.sql View File

127
 -- Name: tag_usage; Type: VIEW; Schema: public; Owner: postgres; Tablespace:
127
 -- Name: tag_usage; Type: VIEW; Schema: public; Owner: postgres; Tablespace:
128
 --
128
 --
129
 
129
 
130
-CREATE VIEW "tag_usage" AS select "name", count("name") AS "count" from "tags", "post_tags" where "tags"."id" = "post_tags"."tag_id" group by "name" order by "count" desc, "name";
130
+CREATE VIEW "tag_usage" AS select "tags"."id" as "id", "name", count("name") AS "count" from "tags", "post_tags" where "tags"."id" = "post_tags"."tag_id" group by "tags"."id", "name" order by "count" desc, "name";
131
 
131
 
132
 --
132
 --
133
 -- Name: products; Type: TABLE; Schema: public; Owner: postgres; Tablespace:
133
 -- Name: products; Type: TABLE; Schema: public; Owner: postgres; Tablespace:

+ 1
- 1
tests/fixtures/blog_sqlite.sql View File

115
 INSERT INTO "events" ("id", "name", "datetime", "visitors") VALUES (1, 'Launch', '2016-01-01 13:01:01', 0);
115
 INSERT INTO "events" ("id", "name", "datetime", "visitors") VALUES (1, 'Launch', '2016-01-01 13:01:01', 0);
116
 
116
 
117
 DROP VIEW IF EXISTS "tag_usage";
117
 DROP VIEW IF EXISTS "tag_usage";
118
-CREATE VIEW "tag_usage" AS select "name", count("name") AS "count" from "tags", "post_tags" where "tags"."id" = "post_tags"."tag_id" group by "name" order by "count" desc, "name";
118
+CREATE VIEW "tag_usage" AS select "tags"."id" as "id", "name", count("name") AS "count" from "tags", "post_tags" where "tags"."id" = "post_tags"."tag_id" group by "tags"."id", "name" order by "count" desc, "name";
119
 
119
 
120
 DROP TABLE IF EXISTS "products";
120
 DROP TABLE IF EXISTS "products";
121
 CREATE TABLE "products" (
121
 CREATE TABLE "products" (

+ 1
- 1
tests/fixtures/blog_sqlsrv.sql View File

246
 
246
 
247
 CREATE VIEW [tag_usage]
247
 CREATE VIEW [tag_usage]
248
 AS
248
 AS
249
-SELECT top 100 PERCENT name, COUNT_BIG(name) AS [count] FROM tags, post_tags WHERE tags.id = post_tags.tag_id GROUP BY name ORDER BY [count] DESC, name
249
+SELECT top 100 PERCENT tags.id as id, name, COUNT_BIG(name) AS [count] FROM tags, post_tags WHERE tags.id = post_tags.tag_id GROUP BY tags.id, name ORDER BY [count] DESC, name
250
 GO
250
 GO
251
 
251
 
252
 DROP SEQUENCE IF EXISTS [products_id_seq]
252
 DROP SEQUENCE IF EXISTS [products_id_seq]

+ 12
- 4
tests/functional/001_records/075_list_tag_usage.log View File

3
 ===
3
 ===
4
 200
4
 200
5
 Content-Type: application/json; charset=utf-8
5
 Content-Type: application/json; charset=utf-8
6
-Content-Length: 71
6
+Content-Length: 85
7
 
7
 
8
-{"records":[{"name":"funny","count":2},{"name":"important","count":2}]}
8
+{"records":[{"id":1,"name":"funny","count":2},{"id":2,"name":"important","count":2}]}
9
 ===
9
 ===
10
 GET /records/tag_usage/1
10
 GET /records/tag_usage/1
11
 ===
11
 ===
12
+200
13
+Content-Type: application/json; charset=utf-8
14
+Content-Length: 33
15
+
16
+{"id":1,"name":"funny","count":2}
17
+===
18
+DELETE /records/tag_usage/1
19
+===
12
 405
20
 405
13
 Content-Type: application/json; charset=utf-8
21
 Content-Type: application/json; charset=utf-8
14
-Content-Length: 56
22
+Content-Length: 58
15
 
23
 
16
-{"code":1015,"message":"Operation 'read' not supported"}
24
+{"code":1015,"message":"Operation 'delete' not supported"}

+ 2
- 2
tests/functional/003_columns/001_get_database.log View File

4
 ===
4
 ===
5
 200
5
 200
6
 Content-Type: application/json; charset=utf-8
6
 Content-Type: application/json; charset=utf-8
7
-Content-Length: 2799
7
+Content-Length: 2840
8
 
8
 
9
-{"tables":[{"name":"barcodes","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"product_id","type":"integer","fk":"products"},{"name":"hex","type":"varchar","length":255},{"name":"bin","type":"blob"},{"name":"ip_address","type":"varchar","length":15,"nullable":true}]},{"name":"categories","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"name","type":"varchar","length":255},{"name":"icon","type":"blob","nullable":true}]},{"name":"comments","type":"table","columns":[{"name":"id","type":"bigint","pk":true},{"name":"post_id","type":"integer","fk":"posts"},{"name":"message","type":"varchar","length":255},{"name":"category_id","type":"integer","fk":"categories"}]},{"name":"countries","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"name","type":"varchar","length":255},{"name":"shape","type":"geometry"}]},{"name":"events","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"name","type":"varchar","length":255},{"name":"datetime","type":"timestamp","nullable":true},{"name":"visitors","type":"bigint","nullable":true}]},{"name":"kunsthåndværk","type":"table","columns":[{"name":"id","type":"varchar","length":36,"pk":true},{"name":"Umlauts ä_ö_ü-COUNT","type":"integer"},{"name":"user_id","type":"integer","fk":"users"},{"name":"invisible_id","type":"varchar","length":36,"nullable":true,"fk":"invisibles"}]},{"name":"nopk","type":"table","columns":[{"name":"id","type":"varchar","length":36}]},{"name":"post_tags","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"post_id","type":"integer","fk":"posts"},{"name":"tag_id","type":"integer","fk":"tags"}]},{"name":"posts","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"user_id","type":"integer","fk":"users"},{"name":"category_id","type":"integer","fk":"categories"},{"name":"content","type":"varchar","length":255}]},{"name":"products","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"name","type":"varchar","length":255},{"name":"price","type":"decimal","precision":10,"scale":2},{"name":"properties","type":"clob"},{"name":"created_at","type":"timestamp"},{"name":"deleted_at","type":"timestamp","nullable":true}]},{"name":"tag_usage","type":"view","columns":[{"name":"name","type":"varchar","length":255},{"name":"count","type":"bigint"}]},{"name":"tags","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"name","type":"varchar","length":255},{"name":"is_important","type":"boolean"}]},{"name":"users","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"username","type":"varchar","length":255},{"name":"password","type":"varchar","length":255},{"name":"location","type":"geometry","nullable":true}]}]}
9
+{"tables":[{"name":"barcodes","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"product_id","type":"integer","fk":"products"},{"name":"hex","type":"varchar","length":255},{"name":"bin","type":"blob"},{"name":"ip_address","type":"varchar","length":15,"nullable":true}]},{"name":"categories","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"name","type":"varchar","length":255},{"name":"icon","type":"blob","nullable":true}]},{"name":"comments","type":"table","columns":[{"name":"id","type":"bigint","pk":true},{"name":"post_id","type":"integer","fk":"posts"},{"name":"message","type":"varchar","length":255},{"name":"category_id","type":"integer","fk":"categories"}]},{"name":"countries","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"name","type":"varchar","length":255},{"name":"shape","type":"geometry"}]},{"name":"events","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"name","type":"varchar","length":255},{"name":"datetime","type":"timestamp","nullable":true},{"name":"visitors","type":"bigint","nullable":true}]},{"name":"kunsthåndværk","type":"table","columns":[{"name":"id","type":"varchar","length":36,"pk":true},{"name":"Umlauts ä_ö_ü-COUNT","type":"integer"},{"name":"user_id","type":"integer","fk":"users"},{"name":"invisible_id","type":"varchar","length":36,"nullable":true,"fk":"invisibles"}]},{"name":"nopk","type":"table","columns":[{"name":"id","type":"varchar","length":36}]},{"name":"post_tags","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"post_id","type":"integer","fk":"posts"},{"name":"tag_id","type":"integer","fk":"tags"}]},{"name":"posts","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"user_id","type":"integer","fk":"users"},{"name":"category_id","type":"integer","fk":"categories"},{"name":"content","type":"varchar","length":255}]},{"name":"products","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"name","type":"varchar","length":255},{"name":"price","type":"decimal","precision":10,"scale":2},{"name":"properties","type":"clob"},{"name":"created_at","type":"timestamp"},{"name":"deleted_at","type":"timestamp","nullable":true}]},{"name":"tag_usage","type":"view","columns":[{"name":"id","type":"integer","pk":true},{"name":"name","type":"varchar","length":255},{"name":"count","type":"bigint"}]},{"name":"tags","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"name","type":"varchar","length":255},{"name":"is_important","type":"boolean"}]},{"name":"users","type":"table","columns":[{"name":"id","type":"integer","pk":true},{"name":"username","type":"varchar","length":255},{"name":"password","type":"varchar","length":255},{"name":"location","type":"geometry","nullable":true}]}]}

Loading…
Cancel
Save