Support reading views

This commit is contained in:
Maurits van der Schee 2020-07-08 16:15:04 +02:00
commit 53279412b2
7 changed files with 65 additions and 34 deletions

43
api.php
View file

@ -3879,19 +3879,39 @@ namespace Tqdev\PhpCrudApi\Column\Reflection {
$columns[$column->getName()] = $column;
}
// set primary key
$columnName = false;
if ($type == 'view') {
$columnName = 'id';
} else {
$columnNames = $reflection->getTablePrimaryKeys($name);
if (count($columnNames) == 1) {
$columnName = $columnNames[0];
if (isset($columns[$columnName])) {
}
}
if ($columnName && isset($columns[$columnName])) {
$pk = $columns[$columnName];
$pk->setPk(true);
}
}
// set foreign keys
if ($type == 'view') {
$tables = $reflection->getTables();
foreach ($columns as $columnName => $column) {
if (substr($columnName, -3) == '_id') {
foreach ($tables as $table) {
$tableName = $table['TABLE_NAME'];
$suffix = $tableName . '_id';
if (substr($columnName, -1 * strlen($suffix)) == $suffix) {
$column->setFk($tableName);
}
}
}
}
} else {
$fks = $reflection->getTableForeignKeys($name);
foreach ($fks as $columnName => $table) {
$columns[$columnName]->setFk($table);
}
}
return new ReflectedTable($name, $type, array_values($columns));
}
@ -4590,9 +4610,6 @@ namespace Tqdev\PhpCrudApi\Controller {
if (!$this->service->hasTable($table)) {
return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table);
}
if ($this->service->getType($table) != 'table') {
return $this->responder->error(ErrorCode::OPERATION_NOT_SUPPORTED, __FUNCTION__);
}
$id = RequestUtils::getPathSegment($request, 3);
$params = RequestUtils::getParams($request);
if (strpos($id, ',') !== false) {
@ -6994,8 +7011,10 @@ namespace Tqdev\PhpCrudApi\Middleware\Router {
return substr($fullPath, 0, -1 * strlen($path));
}
}
if ('/' . basename(__FILE__) == $fullPath) {
return $fullPath;
}
}
return '/';
}
@ -9045,9 +9064,9 @@ namespace Tqdev\PhpCrudApi\OpenApi {
namespace Tqdev\PhpCrudApi\OpenApi {
use Tqdev\PhpCrudApi\Column\ReflectionService;
use Tqdev\PhpCrudApi\Column\Reflection\ReflectedColumn;
use Tqdev\PhpCrudApi\Middleware\Communication\VariableStore;
use Tqdev\PhpCrudApi\OpenApi\OpenApiDefinition;
use Tqdev\PhpCrudApi\Column\Reflection\ReflectedColumn;
class OpenApiRecordsBuilder
{
@ -9264,7 +9283,10 @@ namespace Tqdev\PhpCrudApi\OpenApi {
if (!$pkName && $operation != 'list') {
continue;
}
if ($type != 'table' && $operation != 'list') {
if ($type == 'view' && !in_array($operation, array('read', 'list'))) {
continue;
}
if ($type == 'view' && !$pkName && $operation == 'read') {
continue;
}
if ($operation == 'delete') {
@ -11170,6 +11192,8 @@ namespace Tqdev\PhpCrudApi {
class ResponseFactory
{
const OK = 200;
const MOVED_PERMANENTLY = 301;
const FOUND = 302;
const UNAUTHORIZED = 401;
const FORBIDDEN = 403;
const NOT_FOUND = 404;
@ -11185,8 +11209,7 @@ namespace Tqdev\PhpCrudApi {
public static function fromCsv(int $status, string $csv): ResponseInterface
{
$response = self::from($status, 'text/csv', $csv);
return $response->withHeader('Content-Type', 'text/csv');
return self::from($status, 'text/csv', $csv);
}
public static function fromHtml(int $status, string $html): ResponseInterface
@ -11200,7 +11223,7 @@ namespace Tqdev\PhpCrudApi {
return self::from($status, 'application/json', $content);
}
private static function from(int $status, string $contentType, string $content): ResponseInterface
public static function from(int $status, string $contentType, string $content): ResponseInterface
{
$psr17Factory = new Psr17Factory();
$response = $psr17Factory->createResponse($status);

View file

@ -130,7 +130,7 @@ INSERT INTO `events` (`name`, `datetime`, `visitors`) VALUES
('Launch', '2016-01-01 13:01:01', 0);
DROP VIEW IF EXISTS `tag_usage`;
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`;
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`;
DROP TABLE IF EXISTS `products`;
CREATE TABLE `products` (

View file

@ -127,7 +127,7 @@ CREATE TABLE events (
-- Name: tag_usage; Type: VIEW; Schema: public; Owner: postgres; Tablespace:
--
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";
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";
--
-- Name: products; Type: TABLE; Schema: public; Owner: postgres; Tablespace:

View file

@ -115,7 +115,7 @@ CREATE TABLE "events" (
INSERT INTO "events" ("id", "name", "datetime", "visitors") VALUES (1, 'Launch', '2016-01-01 13:01:01', 0);
DROP VIEW IF EXISTS "tag_usage";
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";
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";
DROP TABLE IF EXISTS "products";
CREATE TABLE "products" (

View file

@ -246,7 +246,7 @@ GO
CREATE VIEW [tag_usage]
AS
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
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
GO
DROP SEQUENCE IF EXISTS [products_id_seq]

View file

@ -3,14 +3,22 @@ GET /records/tag_usage
===
200
Content-Type: application/json; charset=utf-8
Content-Length: 71
Content-Length: 85
{"records":[{"name":"funny","count":2},{"name":"important","count":2}]}
{"records":[{"id":1,"name":"funny","count":2},{"id":2,"name":"important","count":2}]}
===
GET /records/tag_usage/1
===
200
Content-Type: application/json; charset=utf-8
Content-Length: 33
{"id":1,"name":"funny","count":2}
===
DELETE /records/tag_usage/1
===
405
Content-Type: application/json; charset=utf-8
Content-Length: 56
Content-Length: 58
{"code":1015,"message":"Operation 'read' not supported"}
{"code":1015,"message":"Operation 'delete' not supported"}

View file

@ -4,6 +4,6 @@ GET /columns
===
200
Content-Type: application/json; charset=utf-8
Content-Length: 2799
Content-Length: 2840
{"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}]}]}
{"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}]}]}