Skip to content

Commit 5327941

Browse files
committed
Support reading views
1 parent ca23131 commit 5327941

File tree

7 files changed

+65
-34
lines changed

7 files changed

+65
-34
lines changed

api.php

+47-24
Original file line numberDiff line numberDiff line change
@@ -3879,23 +3879,43 @@ public static function fromReflection(GenericReflection $reflection, string $nam
38793879
$columns[$column->getName()] = $column;
38803880
}
38813881
// 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];
38883889
}
38893890
}
3891+
if ($columnName && isset($columns[$columnName])) {
3892+
$pk = $columns[$columnName];
3893+
$pk->setPk(true);
3894+
}
38903895
// 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+
}
38943914
}
38953915
return new ReflectedTable($name, $type, array_values($columns));
38963916
}
38973917

3898-
public static function fromJson(/* object */$json): ReflectedTable
3918+
public static function fromJson( /* object */$json): ReflectedTable
38993919
{
39003920
$name = $json->name;
39013921
$type = isset($json->type) ? $json->type : 'table';
@@ -4590,9 +4610,6 @@ public function read(ServerRequestInterface $request): ResponseInterface
45904610
if (!$this->service->hasTable($table)) {
45914611
return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table);
45924612
}
4593-
if ($this->service->getType($table) != 'table') {
4594-
return $this->responder->error(ErrorCode::OPERATION_NOT_SUPPORTED, __FUNCTION__);
4595-
}
45964613
$id = RequestUtils::getPathSegment($request, 3);
45974614
$params = RequestUtils::getParams($request);
45984615
if (strpos($id, ',') !== false) {
@@ -6994,7 +7011,9 @@ private function detectBasePath(string $basePath): string
69947011
return substr($fullPath, 0, -1 * strlen($path));
69957012
}
69967013
}
6997-
return $fullPath;
7014+
if ('/' . basename(__FILE__) == $fullPath) {
7015+
return $fullPath;
7016+
}
69987017
}
69997018
return '/';
70007019
}
@@ -9045,9 +9064,9 @@ public function jsonSerialize()
90459064
namespace Tqdev\PhpCrudApi\OpenApi {
90469065

90479066
use Tqdev\PhpCrudApi\Column\ReflectionService;
9067+
use Tqdev\PhpCrudApi\Column\Reflection\ReflectedColumn;
90489068
use Tqdev\PhpCrudApi\Middleware\Communication\VariableStore;
90499069
use Tqdev\PhpCrudApi\OpenApi\OpenApiDefinition;
9050-
use Tqdev\PhpCrudApi\Column\Reflection\ReflectedColumn;
90519070

90529071
class OpenApiRecordsBuilder
90539072
{
@@ -9065,16 +9084,16 @@ class OpenApiRecordsBuilder
90659084
'integer' => ['type' => 'integer', 'format' => 'int32'],
90669085
'bigint' => ['type' => 'integer', 'format' => 'int64'],
90679086
'varchar' => ['type' => 'string'],
9068-
'clob' => ['type' => 'string', 'format' => 'large-string'], //custom format
9087+
'clob' => ['type' => 'string', 'format' => 'large-string'], //custom format
90699088
'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
90729091
'float' => ['type' => 'number', 'format' => 'float'],
90739092
'double' => ['type' => 'number', 'format' => 'double'],
90749093
'date' => ['type' => 'string', 'format' => 'date'],
9075-
'time' => ['type' => 'string', 'format' => 'time'], //custom format
9094+
'time' => ['type' => 'string', 'format' => 'time'], //custom format
90769095
'timestamp' => ['type' => 'string', 'format' => 'date-time'],
9077-
'geometry' => ['type' => 'string', 'format' => 'geometry'], //custom format
9096+
'geometry' => ['type' => 'string', 'format' => 'geometry'], //custom format
90789097
'boolean' => ['type' => 'boolean'],
90799098
];
90809099

@@ -9264,7 +9283,10 @@ private function setComponentSchema(string $tableName, array $references) /*: vo
92649283
if (!$pkName && $operation != 'list') {
92659284
continue;
92669285
}
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') {
92689290
continue;
92699291
}
92709292
if ($operation == 'delete') {
@@ -11170,6 +11192,8 @@ public static function getTableNames(ServerRequestInterface $request, Reflection
1117011192
class ResponseFactory
1117111193
{
1117211194
const OK = 200;
11195+
const MOVED_PERMANENTLY = 301;
11196+
const FOUND = 302;
1117311197
const UNAUTHORIZED = 401;
1117411198
const FORBIDDEN = 403;
1117511199
const NOT_FOUND = 404;
@@ -11185,8 +11209,7 @@ public static function fromXml(int $status, string $xml): ResponseInterface
1118511209

1118611210
public static function fromCsv(int $status, string $csv): ResponseInterface
1118711211
{
11188-
$response = self::from($status, 'text/csv', $csv);
11189-
return $response->withHeader('Content-Type', 'text/csv');
11212+
return self::from($status, 'text/csv', $csv);
1119011213
}
1119111214

1119211215
public static function fromHtml(int $status, string $html): ResponseInterface
@@ -11200,7 +11223,7 @@ public static function fromObject(int $status, $body): ResponseInterface
1120011223
return self::from($status, 'application/json', $content);
1120111224
}
1120211225

11203-
private static function from(int $status, string $contentType, string $content): ResponseInterface
11226+
public static function from(int $status, string $contentType, string $content): ResponseInterface
1120411227
{
1120511228
$psr17Factory = new Psr17Factory();
1120611229
$response = $psr17Factory->createResponse($status);

tests/fixtures/blog_mysql.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ INSERT INTO `events` (`name`, `datetime`, `visitors`) VALUES
130130
('Launch', '2016-01-01 13:01:01', 0);
131131

132132
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`;
134134

135135
DROP TABLE IF EXISTS `products`;
136136
CREATE TABLE `products` (

tests/fixtures/blog_pgsql.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ CREATE TABLE events (
127127
-- Name: tag_usage; Type: VIEW; Schema: public; Owner: postgres; Tablespace:
128128
--
129129

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";
131131

132132
--
133133
-- Name: products; Type: TABLE; Schema: public; Owner: postgres; Tablespace:

tests/fixtures/blog_sqlite.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ CREATE TABLE "events" (
115115
INSERT INTO "events" ("id", "name", "datetime", "visitors") VALUES (1, 'Launch', '2016-01-01 13:01:01', 0);
116116

117117
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";
119119

120120
DROP TABLE IF EXISTS "products";
121121
CREATE TABLE "products" (

tests/fixtures/blog_sqlsrv.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ GO
246246

247247
CREATE VIEW [tag_usage]
248248
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
250250
GO
251251

252252
DROP SEQUENCE IF EXISTS [products_id_seq]

tests/functional/001_records/075_list_tag_usage.log

+12-4
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,22 @@ GET /records/tag_usage
33
===
44
200
55
Content-Type: application/json; charset=utf-8
6-
Content-Length: 71
6+
Content-Length: 85
77

8-
{"records":[{"name":"funny","count":2},{"name":"important","count":2}]}
8+
{"records":[{"id":1,"name":"funny","count":2},{"id":2,"name":"important","count":2}]}
99
===
1010
GET /records/tag_usage/1
1111
===
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+
===
1220
405
1321
Content-Type: application/json; charset=utf-8
14-
Content-Length: 56
22+
Content-Length: 58
1523

16-
{"code":1015,"message":"Operation 'read' not supported"}
24+
{"code":1015,"message":"Operation 'delete' not supported"}

tests/functional/003_columns/001_get_database.log

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ GET /columns
44
===
55
200
66
Content-Type: application/json; charset=utf-8
7-
Content-Length: 2799
7+
Content-Length: 2840
88

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}]}]}

0 commit comments

Comments
 (0)