Support reading views
This commit is contained in:
parent
ca23131784
commit
53279412b2
7 changed files with 65 additions and 34 deletions
71
api.php
71
api.php
|
@ -3879,23 +3879,43 @@ namespace Tqdev\PhpCrudApi\Column\Reflection {
|
|||
$columns[$column->getName()] = $column;
|
||||
}
|
||||
// set primary key
|
||||
$columnNames = $reflection->getTablePrimaryKeys($name);
|
||||
if (count($columnNames) == 1) {
|
||||
$columnName = $columnNames[0];
|
||||
if (isset($columns[$columnName])) {
|
||||
$pk = $columns[$columnName];
|
||||
$pk->setPk(true);
|
||||
$columnName = false;
|
||||
if ($type == 'view') {
|
||||
$columnName = 'id';
|
||||
} else {
|
||||
$columnNames = $reflection->getTablePrimaryKeys($name);
|
||||
if (count($columnNames) == 1) {
|
||||
$columnName = $columnNames[0];
|
||||
}
|
||||
}
|
||||
if ($columnName && isset($columns[$columnName])) {
|
||||
$pk = $columns[$columnName];
|
||||
$pk->setPk(true);
|
||||
}
|
||||
// set foreign keys
|
||||
$fks = $reflection->getTableForeignKeys($name);
|
||||
foreach ($fks as $columnName => $table) {
|
||||
$columns[$columnName]->setFk($table);
|
||||
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));
|
||||
}
|
||||
|
||||
public static function fromJson(/* object */$json): ReflectedTable
|
||||
public static function fromJson( /* object */$json): ReflectedTable
|
||||
{
|
||||
$name = $json->name;
|
||||
$type = isset($json->type) ? $json->type : 'table';
|
||||
|
@ -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,7 +7011,9 @@ namespace Tqdev\PhpCrudApi\Middleware\Router {
|
|||
return substr($fullPath, 0, -1 * strlen($path));
|
||||
}
|
||||
}
|
||||
return $fullPath;
|
||||
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
|
||||
{
|
||||
|
@ -9065,16 +9084,16 @@ namespace Tqdev\PhpCrudApi\OpenApi {
|
|||
'integer' => ['type' => 'integer', 'format' => 'int32'],
|
||||
'bigint' => ['type' => 'integer', 'format' => 'int64'],
|
||||
'varchar' => ['type' => 'string'],
|
||||
'clob' => ['type' => 'string', 'format' => 'large-string'], //custom format
|
||||
'clob' => ['type' => 'string', 'format' => 'large-string'], //custom format
|
||||
'varbinary' => ['type' => 'string', 'format' => 'byte'],
|
||||
'blob' => ['type' => 'string', 'format' => 'large-byte'], //custom format
|
||||
'decimal' => ['type' => 'string', 'format' => 'decimal'], //custom format
|
||||
'blob' => ['type' => 'string', 'format' => 'large-byte'], //custom format
|
||||
'decimal' => ['type' => 'string', 'format' => 'decimal'], //custom format
|
||||
'float' => ['type' => 'number', 'format' => 'float'],
|
||||
'double' => ['type' => 'number', 'format' => 'double'],
|
||||
'date' => ['type' => 'string', 'format' => 'date'],
|
||||
'time' => ['type' => 'string', 'format' => 'time'], //custom format
|
||||
'time' => ['type' => 'string', 'format' => 'time'], //custom format
|
||||
'timestamp' => ['type' => 'string', 'format' => 'date-time'],
|
||||
'geometry' => ['type' => 'string', 'format' => 'geometry'], //custom format
|
||||
'geometry' => ['type' => 'string', 'format' => 'geometry'], //custom format
|
||||
'boolean' => ['type' => 'boolean'],
|
||||
];
|
||||
|
||||
|
@ -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);
|
||||
|
|
2
tests/fixtures/blog_mysql.sql
vendored
2
tests/fixtures/blog_mysql.sql
vendored
|
@ -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` (
|
||||
|
|
2
tests/fixtures/blog_pgsql.sql
vendored
2
tests/fixtures/blog_pgsql.sql
vendored
|
@ -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:
|
||||
|
|
2
tests/fixtures/blog_sqlite.sql
vendored
2
tests/fixtures/blog_sqlite.sql
vendored
|
@ -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" (
|
||||
|
|
2
tests/fixtures/blog_sqlsrv.sql
vendored
2
tests/fixtures/blog_sqlsrv.sql
vendored
|
@ -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]
|
||||
|
|
|
@ -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"}
|
||||
|
|
|
@ -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}]}]}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue