363 lines
8.7 KiB
PHP
363 lines
8.7 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Vvveb
|
|
*
|
|
* Copyright (C) 2022 Ziadin Givan
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
namespace Vvveb\Controller;
|
|
|
|
use function Vvveb\dashesToCamelCase;
|
|
use function Vvveb\humanReadable;
|
|
use function Vvveb\siteSettings;
|
|
use Vvveb\System\Cache;
|
|
use Vvveb\System\Event;
|
|
use Vvveb\System\Functions\Plural;
|
|
use Vvveb\System\Sites;
|
|
use Vvveb\System\Sqlp\Sqlp;
|
|
|
|
#[\AllowDynamicProperties]
|
|
class Schema extends Base {
|
|
private $models = [];
|
|
|
|
private $tables = [];
|
|
|
|
private $tableNames = [];
|
|
|
|
function type($type) {
|
|
$compare = strtolower($type);
|
|
|
|
switch ($compare) {
|
|
case 'int':
|
|
case 'tinyint':
|
|
case 'smallint':
|
|
case 'double':
|
|
case 'array':
|
|
$type = 'Int';
|
|
|
|
break;
|
|
|
|
case 'decimal':
|
|
$type = 'Float';
|
|
|
|
break;
|
|
|
|
case 'array':
|
|
$type = '[Int]';
|
|
|
|
break;
|
|
|
|
case 'varchar':
|
|
case 'char':
|
|
case 'text':
|
|
case 'enum':
|
|
case 'date':
|
|
case 'datetime':
|
|
case 'timestamp':
|
|
case 'longtext':
|
|
case 'tinytext':
|
|
$type = 'String';
|
|
|
|
break;
|
|
}
|
|
|
|
return $type;
|
|
}
|
|
|
|
function types() {
|
|
$db = \Vvveb\System\Db::getInstance();
|
|
|
|
$tables = $db->getTableNames(DB_NAME);
|
|
$types = include(DIR_APP . 'types.php') ?? [];
|
|
|
|
foreach ($tables as $table) {
|
|
//skip cart table
|
|
if ($table == 'cart') {
|
|
continue;
|
|
}
|
|
$meta = $db->getColumnsMeta($table, true);
|
|
$cols = [];
|
|
|
|
foreach ($meta as $column) {
|
|
$name = $column['name'] ?? '';
|
|
$col = [];
|
|
$col['name'] = $name;
|
|
$col['description'] = $column['c'] ?? '';
|
|
|
|
//column is id (input) type
|
|
if (substr_compare($name, '_id', -3 ,3) === 0) {
|
|
$type = 'ID';
|
|
$relation = substr($name,0, -3);
|
|
|
|
if ($relation != $table && strpos($table, $relation) === 0) {
|
|
$relationType = dashesToCamelCase($table, '_') . 'Type';
|
|
$typeTable = $table;
|
|
$pos = strpos($typeTable, $relation . '_');
|
|
|
|
if ($pos === 0) {
|
|
$typeTable = substr_replace($typeTable, '', $pos, strlen($relation . '_'));
|
|
|
|
$pos = strpos($typeTable, 'to_');
|
|
|
|
if ($pos === 0) {
|
|
$typeTable = substr_replace($typeTable, '', $pos, 3);
|
|
$relationType = dashesToCamelCase($typeTable, '_') . 'Type';
|
|
}
|
|
|
|
$typeTable = Plural::tablePlural($typeTable);
|
|
}
|
|
|
|
if (! isset($types[$relation]['properties'][$typeTable])) {
|
|
$types[$relation]['properties'][$typeTable] = ['name' => $table, 'type' => "[$relationType]"];
|
|
}
|
|
}
|
|
} else {
|
|
if (in_array($name, $tables)) {
|
|
$type = dashesToCamelCase($name, '_') . 'Type'; //. 'Input';
|
|
} else {
|
|
$type = $this->type($column['t'] ?? 'Int');
|
|
}
|
|
}
|
|
|
|
$col['type'] = $type;
|
|
|
|
$cols[$column['name']] = $col;
|
|
}
|
|
|
|
if ($table == 'product_option' || $table == 'product_option_value') {
|
|
$cols += [
|
|
'name' => ['name' => 'name', 'type' => 'string'],
|
|
];
|
|
}
|
|
|
|
$data = [
|
|
'name' => $table,
|
|
'properties' => $cols,
|
|
];
|
|
|
|
//$types[$table] = isset($types[$table]) ? array_replace_recursive($types[$table], $data) : $data;
|
|
if (isset($types[$table]['properties'])) {
|
|
$data['properties'] += $types[$table]['properties'];
|
|
}
|
|
|
|
$types[$table] = $data;
|
|
|
|
//for content table also add name and content columns to relation
|
|
if (substr_compare($table, '_content', -8 ,8) === 0) {
|
|
$relation = substr($table, 0, -8);
|
|
$types[$relation]['properties']['name'] = ['type' => 'char', 'name' => 'name'];
|
|
$types[$relation]['properties']['content'] = ['type' => 'char', 'name' => 'content'];
|
|
$types[$relation]['properties']['slug'] = ['type' => 'char', 'name' => 'slug'];
|
|
}
|
|
}
|
|
|
|
//$types = array_replace($types, include(DIR_APP . 'types.php'));
|
|
|
|
list($types) = Event :: trigger(__CLASS__,__FUNCTION__, $types);
|
|
|
|
return $types;
|
|
}
|
|
|
|
function models() {
|
|
$dirSQL = DIR_ROOT . 'admin' . DS . 'sql' . DS . DB_ENGINE . DS;
|
|
$sqlp = new Sqlp();
|
|
$models = [];
|
|
|
|
$files = glob("$dirSQL*.sql");
|
|
|
|
foreach ($files as $file) {
|
|
$sqlp->parseSqlPfile($file);
|
|
$tree = $sqlp->getModel();
|
|
$model = basename($file, '.sql');
|
|
//skip cart table
|
|
if ($model == 'cart') {
|
|
continue;
|
|
}
|
|
|
|
foreach ($tree as $method => $options) {
|
|
$models[$model][$method] = $options['params'];
|
|
}
|
|
}
|
|
|
|
list($models) = Event :: trigger(__CLASS__,__FUNCTION__, $models);
|
|
|
|
return $models;
|
|
}
|
|
|
|
function schema() {
|
|
$typesText = '';
|
|
$types = $this->types();
|
|
|
|
foreach ($types as $table => $attrs) {
|
|
$properties = '';
|
|
|
|
foreach ($attrs['properties'] as $name => $property) {
|
|
if (substr_compare($name, '_id', -3 ,3) === 0) {
|
|
$type = 'ID';
|
|
} else {
|
|
$type = $this->type($property['type'] ?? 'Int');
|
|
}
|
|
|
|
if (GRAPHQL_CAMELCASE) {
|
|
$name = lcfirst(dashesToCamelCase($name, '_'));
|
|
$type = dashesToCamelCase($type, '_');
|
|
}
|
|
|
|
$properties .= " $name: $type\n";
|
|
}
|
|
|
|
$tableName = dashesToCamelCase($table, '_');
|
|
$typesText .= "type {$tableName}Type {\n$properties}\n\n";
|
|
}
|
|
|
|
$models = $this->models();
|
|
$queries = [];
|
|
$mutations = [];
|
|
|
|
$queriesText = '';
|
|
$mutationsText = '';
|
|
$connectionsText = '';
|
|
|
|
foreach ($models as $model => $methods) {
|
|
$modelComment = "\n\n # " . humanReadable($model) . "\n";
|
|
$mutationsText .= $modelComment;
|
|
$queriesText .= $modelComment;
|
|
|
|
if ($model == 'category' || $model == 'filter' || $model == 'stat') {
|
|
continue;
|
|
}
|
|
|
|
foreach ($methods as $method => $params) {
|
|
$methodName = lcfirst(dashesToCamelCase($method . '_' . $model, '_'));
|
|
$parameters = '';
|
|
|
|
foreach ($params as $name => $param) {
|
|
if ($param['in_out'] == 'IN') {
|
|
$name = $param['name'];
|
|
$type = $this->type($param['type'] ?? 'Int');
|
|
|
|
if (substr_compare($name, '_id', -3 ,3) === 0) {
|
|
if ($param['type'] == 'ARRAY') {
|
|
$type = '[ID]';
|
|
} else {
|
|
$type = 'ID';
|
|
}
|
|
}
|
|
|
|
if (GRAPHQL_CAMELCASE) {
|
|
$name = lcfirst(dashesToCamelCase($name, '_'));
|
|
$type = dashesToCamelCase($type, '_');
|
|
}
|
|
|
|
$parameters .= "$name: $type, ";
|
|
}
|
|
}
|
|
|
|
$parameters = rtrim($parameters, ', ');
|
|
$type = dashesToCamelCase($model, '_');
|
|
|
|
if (strpos($method, 'get') === 0) {
|
|
if ($model == 'category' || $model == 'filter' || $model == 'stat') {
|
|
//$type = 'Taxonomy';
|
|
}
|
|
|
|
$return = $type . 'Type';
|
|
$methodText = " $methodName($parameters):{$return}\n";
|
|
|
|
if (strpos($method, 'getAll') === 0) {
|
|
$return = "[{$type}Type]";
|
|
$modelPlural = lcfirst(dashesToCamelCase(Plural::tablePlural($model), '_'));
|
|
$methodText = " $modelPlural($parameters):{$type}Connection\n";
|
|
|
|
$connectionsText .= $modelComment .
|
|
"type {$type}Connection {\n nodes:[{$type}Type!]!\n pageInfo:PageInfo!\n}\n";
|
|
}
|
|
|
|
if ($method == 'get') {
|
|
$modelName = lcfirst(dashesToCamelCase($model, '_'));
|
|
$methodText = " $modelName($parameters):{$return}\n";
|
|
}
|
|
|
|
$queriesText .= $methodText;
|
|
} else {
|
|
$methodText = " $methodName($parameters):ID\n";
|
|
|
|
$mutationsText .= $methodText;
|
|
}
|
|
}
|
|
}
|
|
|
|
$cache = Cache::getInstance();
|
|
$json = $cache->cache(APP,'graphql-schema',function () {
|
|
$site = siteSettings();
|
|
$data = Sites :: getSiteData();
|
|
|
|
$json = [];
|
|
$json['types'] = $this->types();
|
|
|
|
return $json;
|
|
}, 259200);
|
|
|
|
$text = <<<GQL
|
|
type PageInfo {
|
|
count: Int
|
|
page: Int
|
|
limit: Int
|
|
endCursor: String
|
|
hasNextPage: Boolean!
|
|
hasPreviousPage: Boolean!
|
|
startCursor: String
|
|
}
|
|
|
|
$connectionsText
|
|
|
|
$typesText
|
|
|
|
type RootQueryType {
|
|
|
|
cart(cartId:ID!):CartType
|
|
|
|
$queriesText
|
|
}
|
|
|
|
type MutationType {
|
|
|
|
createCart:CartType
|
|
addCart(cartId:ID!, productId:ID!, quantity:Int, options:String, productVariantId:ID, subscriptionPlanId:ID):CartType
|
|
updateCart(cartId:ID!, key:String!, quantity:Int!, options:String, productVariantId:ID, subscriptionPlanId:ID):CartType
|
|
removeCart(cartId:ID!, key:[String!]!):CartType
|
|
|
|
$mutationsText
|
|
}
|
|
|
|
schema {
|
|
query:RootQueryType
|
|
mutation:MutationType
|
|
}
|
|
GQL;
|
|
|
|
return $text;
|
|
}
|
|
|
|
function index() {
|
|
$text = $this->schema();
|
|
$this->response->setType('text');
|
|
$this->response->output($text);
|
|
}
|
|
}
|