database api start
This commit is contained in:
parent
a8af7fa700
commit
f14a7a4762
97
Core/API/DatabaseAPI.class.php
Normal file
97
Core/API/DatabaseAPI.class.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Core\API {
|
||||
|
||||
abstract class DatabaseAPI extends Request {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Core\API\Database {
|
||||
|
||||
use Core\API\DatabaseAPI;
|
||||
use Core\API\Parameter\StringType;
|
||||
use Core\Objects\Context;
|
||||
|
||||
class Status extends DatabaseAPI {
|
||||
|
||||
public function __construct(Context $context, bool $externalCall = false) {
|
||||
parent::__construct($context, $externalCall, []);
|
||||
}
|
||||
|
||||
protected function _execute(): bool {
|
||||
$sql = $this->context->getSQL();
|
||||
$status = $sql->getStatus();
|
||||
|
||||
$this->result["status"] = $status;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class Migrate extends DatabaseAPI {
|
||||
public function __construct(Context $context, bool $externalCall = false) {
|
||||
parent::__construct($context, $externalCall, [
|
||||
"className" => new StringType("className", 256)
|
||||
]);
|
||||
}
|
||||
|
||||
protected function _execute(): bool {
|
||||
$className = $this->getParam("className");
|
||||
if (!preg_match("/[a-zA-Z0-9]+/", $className)) {
|
||||
return $this->createError("Invalid class name");
|
||||
}
|
||||
|
||||
$class = null;
|
||||
foreach (["Site", "Core"] as $baseDir) {
|
||||
$classPath = "\\$baseDir\\Objects\\DatabaseEntity\\$className";
|
||||
if (isClass($classPath)) {
|
||||
$class = new \ReflectionClass($classPath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($class === null) {
|
||||
return $this->createError("Class not found");
|
||||
}
|
||||
|
||||
$sql = $this->context->getSQL();
|
||||
$handler = call_user_func("$classPath::getHandler", $sql, null, true);
|
||||
$persistables = array_merge([
|
||||
$handler->getTableName() => $handler
|
||||
], $handler->getNMRelations());
|
||||
|
||||
foreach ($persistables as $tableName => $persistable) {
|
||||
// first check if table exists
|
||||
if (!$sql->tableExists($tableName)) {
|
||||
$sql->startTransaction();
|
||||
$success = true;
|
||||
try {
|
||||
foreach ($persistable->getCreateQueries($sql) as $query) {
|
||||
if (!$query->execute()) {
|
||||
$this->lastError = "Error migrating table: " . $sql->getLastError();
|
||||
$success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$success = false;
|
||||
$this->lastError = "Error migrating table: " . $ex->getMessage();
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
$sql->rollback();
|
||||
return false;
|
||||
} else {
|
||||
$sql->commit();
|
||||
}
|
||||
} else {
|
||||
// TODO: Alter table ...
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,18 +2,21 @@
|
||||
|
||||
namespace Core\Driver\SQL\Condition;
|
||||
|
||||
use Core\Driver\SQL\MySQL;
|
||||
use Core\Driver\SQL\SQL;
|
||||
|
||||
class Compare extends Condition {
|
||||
|
||||
private string $operator;
|
||||
private string $column;
|
||||
private $value;
|
||||
private mixed $value;
|
||||
private bool $binary;
|
||||
|
||||
public function __construct(string $col, $val, string $operator = '=') {
|
||||
public function __construct(string $col, $val, string $operator = '=', bool $binary = false) {
|
||||
$this->operator = $operator;
|
||||
$this->column = $col;
|
||||
$this->value = $val;
|
||||
$this->binary = $binary;
|
||||
}
|
||||
|
||||
public function getColumn(): string { return $this->column; }
|
||||
@ -30,6 +33,11 @@ class Compare extends Condition {
|
||||
}
|
||||
}
|
||||
|
||||
return $sql->columnName($this->column) . $this->operator . $sql->addValue($this->value, $params);
|
||||
$condition = $sql->columnName($this->column) . $this->operator . $sql->addValue($this->value, $params);
|
||||
if ($this->binary && $sql instanceof MySQL) {
|
||||
$condition = "BINARY $condition";
|
||||
}
|
||||
|
||||
return $condition;
|
||||
}
|
||||
}
|
@ -4,6 +4,9 @@ namespace Core\Driver\SQL;
|
||||
|
||||
use Core\API\Parameter\Parameter;
|
||||
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Driver\SQL\Condition\CondLike;
|
||||
use Core\Driver\SQL\Expression\Count;
|
||||
use DateTime;
|
||||
use Core\Driver\SQL\Column\Column;
|
||||
use Core\Driver\SQL\Column\IntColumn;
|
||||
@ -453,6 +456,18 @@ class MySQL extends SQL {
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function tableExists(string $tableName): bool {
|
||||
$tableSchema = $this->connectionData->getProperty("database");
|
||||
$res = $this->select(new Count())
|
||||
->from("information_schema.TABLES")
|
||||
->where(new Compare("TABLE_NAME", $tableName, "=", true))
|
||||
->where(new Compare("TABLE_SCHEMA", $tableSchema, "=", true))
|
||||
->where(new CondLike(new Column("TABLE_TYPE"), "BASE TABLE"))
|
||||
->execute();
|
||||
|
||||
return $res && $res[0]["count"] > 0;
|
||||
}
|
||||
}
|
||||
|
||||
class RowIteratorMySQL extends RowIterator {
|
||||
|
@ -14,8 +14,11 @@ use Core\Driver\SQL\Column\DateTimeColumn;
|
||||
use Core\Driver\SQL\Column\BoolColumn;
|
||||
use Core\Driver\SQL\Column\JsonColumn;
|
||||
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Driver\SQL\Condition\CondLike;
|
||||
use Core\Driver\SQL\Condition\CondRegex;
|
||||
use Core\Driver\SQL\Expression\Add;
|
||||
use Core\Driver\SQL\Expression\Count;
|
||||
use Core\Driver\SQL\Expression\CurrentTimeStamp;
|
||||
use Core\Driver\SQL\Expression\Expression;
|
||||
use Core\Driver\SQL\Query\CreateProcedure;
|
||||
@ -444,6 +447,17 @@ class PostgreSQL extends SQL {
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function tableExists(string $tableName): bool {
|
||||
$tableSchema = $this->connectionData->getProperty("database");
|
||||
$res = $this->select(new Count())
|
||||
->from("pg_tables")
|
||||
->whereEq("tablename", $tableName)
|
||||
->whereEq("schemaname", $tableSchema)
|
||||
->execute();
|
||||
|
||||
return $res && $res[0]["count"] > 0;
|
||||
}
|
||||
}
|
||||
|
||||
class RowIteratorPostgreSQL extends RowIterator {
|
||||
|
@ -127,6 +127,9 @@ abstract class SQL {
|
||||
public abstract function connect();
|
||||
public abstract function disconnect();
|
||||
|
||||
// Schema
|
||||
public abstract function tableExists(string $tableName): bool;
|
||||
|
||||
/**
|
||||
* @param Query $query
|
||||
* @param int $fetchType
|
||||
|
@ -112,7 +112,7 @@ abstract class DatabaseEntity implements ArrayAccess, JsonSerializable {
|
||||
|
||||
public function preInsert(array &$row) { }
|
||||
public function postFetch(SQL $sql, array $row) { }
|
||||
public static function getPredefinedValues(SQL $sql): array { return []; }
|
||||
public static function getPredefinedValues(): array { return []; }
|
||||
|
||||
public static function fromRow(SQL $sql, array $row): static {
|
||||
$handler = self::getHandler($sql);
|
||||
|
@ -146,32 +146,38 @@ class DatabaseEntityHandler implements Persistable {
|
||||
} else if ($propertyTypeName === 'DateTime') {
|
||||
$this->columns[$propertyName] = new DateTimeColumn($columnName, $nullable, $defaultValue);
|
||||
} else if ($propertyTypeName === "array") {
|
||||
$multiple = self::getAttribute($property, Multiple::class);
|
||||
if (!$multiple) {
|
||||
$this->raiseError("Cannot persist class '$className': Property '$propertyName' has non persist-able type: $propertyTypeName. " .
|
||||
"Is the 'Multiple' attribute missing?");
|
||||
}
|
||||
$json = self::getAttribute($property, Json::class);
|
||||
if ($json) {
|
||||
$this->columns[$propertyName] = new JsonColumn($columnName, $nullable, $defaultValue);
|
||||
} else {
|
||||
|
||||
try {
|
||||
$refClass = $multiple->getClassName();
|
||||
$requestedClass = new \ReflectionClass($refClass);
|
||||
if ($requestedClass->isSubclassOf(DatabaseEntity::class)) {
|
||||
$nmTableName = NMRelation::buildTableName($this->getTableName(), $requestedClass->getShortName());
|
||||
$nmRelation = $this->nmRelations[$nmTableName] ?? null;
|
||||
if (!$nmRelation) {
|
||||
$otherHandler = DatabaseEntity::getHandler($this->sql, $requestedClass);
|
||||
$otherNM = $otherHandler->getNMRelations();
|
||||
$nmRelation = $otherNM[$nmTableName] ?? (new NMRelation($this, $otherHandler));
|
||||
$this->nmRelations[$nmTableName] = $nmRelation;
|
||||
}
|
||||
|
||||
$this->nmRelations[$nmTableName]->addProperty($this, $property);
|
||||
} else {
|
||||
$this->raiseError("Cannot persist class '$className': Property '$propertyName' of type multiple can " .
|
||||
"only reference DatabaseEntity types, but got: $refClass");
|
||||
$multiple = self::getAttribute($property, Multiple::class);
|
||||
if (!$multiple) {
|
||||
$this->raiseError("Cannot persist class '$className': Property '$propertyName' has non persist-able type: $propertyTypeName. " .
|
||||
"Is the 'Multiple' attribute missing?");
|
||||
}
|
||||
|
||||
try {
|
||||
$refClass = $multiple->getClassName();
|
||||
$requestedClass = new \ReflectionClass($refClass);
|
||||
if ($requestedClass->isSubclassOf(DatabaseEntity::class)) {
|
||||
$nmTableName = NMRelation::buildTableName($this->getTableName(), $requestedClass->getShortName());
|
||||
$nmRelation = $this->nmRelations[$nmTableName] ?? null;
|
||||
if (!$nmRelation) {
|
||||
$otherHandler = DatabaseEntity::getHandler($this->sql, $requestedClass);
|
||||
$otherNM = $otherHandler->getNMRelations();
|
||||
$nmRelation = $otherNM[$nmTableName] ?? (new NMRelation($this, $otherHandler));
|
||||
$this->nmRelations[$nmTableName] = $nmRelation;
|
||||
}
|
||||
|
||||
$this->nmRelations[$nmTableName]->addProperty($this, $property);
|
||||
} else {
|
||||
$this->raiseError("Cannot persist class '$className': Property '$propertyName' of type multiple can " .
|
||||
"only reference DatabaseEntity types, but got: $refClass");
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$this->raiseError("Cannot persist class '$className' property '$propertyTypeName': " . $ex->getMessage());
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$this->raiseError("Cannot persist class '$className' property '$propertyTypeName': " . $ex->getMessage());
|
||||
}
|
||||
} else if ($propertyTypeName !== "mixed") {
|
||||
try {
|
||||
@ -630,7 +636,7 @@ class DatabaseEntityHandler implements Persistable {
|
||||
// pre defined values
|
||||
$getPredefinedValues = $this->entityClass->getMethod("getPredefinedValues");
|
||||
$getPredefinedValues->setAccessible(true);
|
||||
$predefinedValues = $getPredefinedValues->invoke(null, $sql);
|
||||
$predefinedValues = $getPredefinedValues->invoke(null);
|
||||
if ($predefinedValues) {
|
||||
$queries[] = $this->getInsertQuery($predefinedValues);
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ class Group extends DatabaseEntity {
|
||||
return User::toJsonArray($users, ["id", "name", "fullName", "profilePicture"]);
|
||||
}
|
||||
|
||||
public static function getPredefinedValues(SQL $sql): array {
|
||||
public static function getPredefinedValues(): array {
|
||||
return [
|
||||
new Group(Group::ADMIN, Group::GROUPS[Group::ADMIN], "#dc3545"),
|
||||
new Group(Group::MODERATOR, Group::GROUPS[Group::MODERATOR], "#28a745"),
|
||||
|
@ -141,7 +141,7 @@ namespace Core\Objects\DatabaseEntity {
|
||||
return array_key_exists($module, $this->entries);
|
||||
}
|
||||
|
||||
public static function getPredefinedValues(SQL $sql): array {
|
||||
public static function getPredefinedValues(): array {
|
||||
return [
|
||||
new Language(Language::AMERICAN_ENGLISH, "en_US", 'American English'),
|
||||
new Language(Language::GERMAN_STANDARD, "de_DE", 'Deutsch Standard'),
|
||||
|
Loading…
Reference in New Issue
Block a user