Core v2.3, N:M Relations
This commit is contained in:
@@ -90,7 +90,7 @@ class Context {
|
||||
session_write_close();
|
||||
}
|
||||
|
||||
private function loadSession(int $userId, int $sessionId) {
|
||||
private function loadSession(int $userId, int $sessionId): void {
|
||||
$this->session = Session::init($this, $userId, $sessionId);
|
||||
$this->user = $this->session?->getUser();
|
||||
if ($this->user) {
|
||||
@@ -128,12 +128,13 @@ class Context {
|
||||
|
||||
public function updateLanguage(string $lang): bool {
|
||||
if ($this->sql) {
|
||||
$language = Language::findBuilder($this->sql)
|
||||
$language = Language::findBy(Language::createBuilder($this->sql, true)
|
||||
->where(new CondOr(
|
||||
new CondLike("name", "%$lang%"), // english
|
||||
new Compare("code", $lang), // de_DE
|
||||
new CondLike("code", $lang . "_%"))) // de -> de_%
|
||||
->execute();
|
||||
new CondLike("code", "${lang}_%") // de -> de_%
|
||||
))
|
||||
);
|
||||
if ($language) {
|
||||
$this->setLanguage($language);
|
||||
return true;
|
||||
@@ -176,14 +177,13 @@ class Context {
|
||||
}
|
||||
|
||||
public function loadApiKey(string $apiKey): bool {
|
||||
$this->user = User::findBuilder($this->sql)
|
||||
$this->user = User::findBy(User::createBuilder($this->sql, true)
|
||||
->addJoin(new Join("INNER","ApiKey", "ApiKey.user_id", "User.id"))
|
||||
->where(new Compare("ApiKey.api_key", $apiKey))
|
||||
->where(new Compare("valid_until", $this->sql->currentTimestamp(), ">"))
|
||||
->where(new Compare("ApiKey.active", true))
|
||||
->where(new Compare("User.confirmed", true))
|
||||
->fetchEntities()
|
||||
->execute();
|
||||
->fetchEntities());
|
||||
|
||||
return $this->user !== null;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Core\Objects\DatabaseEntity;
|
||||
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||
|
||||
class ApiKey extends DatabaseEntity {
|
||||
|
||||
|
||||
18
Core/Objects/DatabaseEntity/Attribute/Multiple.php
Normal file
18
Core/Objects/DatabaseEntity/Attribute/Multiple.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Core\Objects\DatabaseEntity\Attribute;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||
class Multiple {
|
||||
|
||||
private string $className;
|
||||
|
||||
public function __construct(string $className) {
|
||||
$this->className = $className;
|
||||
}
|
||||
|
||||
public function getClassName(): string {
|
||||
return $this->className;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Core\Objects\DatabaseEntity;
|
||||
namespace Core\Objects\DatabaseEntity\Controller;
|
||||
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Driver\SQL\Condition\Condition;
|
||||
@@ -8,6 +8,13 @@ use Core\Driver\SQL\SQL;
|
||||
|
||||
abstract class DatabaseEntity {
|
||||
|
||||
protected static array $entityLogConfig = [
|
||||
"insert" => false,
|
||||
"update" => false,
|
||||
"delete" => false,
|
||||
"lifetime" => null,
|
||||
];
|
||||
|
||||
private static array $handlers = [];
|
||||
protected ?int $id;
|
||||
|
||||
@@ -51,8 +58,8 @@ abstract class DatabaseEntity {
|
||||
return $res !== false && $res[0]["count"] !== 0;
|
||||
}
|
||||
|
||||
public static function findBuilder(SQL $sql): DatabaseEntityQuery {
|
||||
return DatabaseEntityQuery::fetchOne(self::getHandler($sql));
|
||||
public static function findBy(DatabaseEntityQuery $dbQuery): static|array|bool|null {
|
||||
return $dbQuery->execute();
|
||||
}
|
||||
|
||||
public static function findAll(SQL $sql, ?Condition $condition = null): ?array {
|
||||
@@ -60,17 +67,22 @@ abstract class DatabaseEntity {
|
||||
return $handler->fetchMultiple($condition);
|
||||
}
|
||||
|
||||
public static function findAllBuilder(SQL $sql): DatabaseEntityQuery {
|
||||
return DatabaseEntityQuery::fetchAll(self::getHandler($sql));
|
||||
public static function createBuilder(SQL $sql, bool $one): DatabaseEntityQuery {
|
||||
if ($one) {
|
||||
return DatabaseEntityQuery::fetchOne(self::getHandler($sql));
|
||||
} else {
|
||||
return DatabaseEntityQuery::fetchAll(self::getHandler($sql));
|
||||
}
|
||||
}
|
||||
|
||||
public function save(SQL $sql, ?array $columns = null): bool {
|
||||
public function save(SQL $sql, ?array $columns = null, bool $saveNM = false): bool {
|
||||
$handler = self::getHandler($sql);
|
||||
$res = $handler->insertOrUpdate($this, $columns);
|
||||
$res = $handler->insertOrUpdate($this, $columns, $saveNM);
|
||||
if ($res === false) {
|
||||
return false;
|
||||
} else if ($this->id === null) {
|
||||
$this->id = $res;
|
||||
$handler->insertNM($this);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -127,4 +139,22 @@ abstract class DatabaseEntity {
|
||||
public function getId(): ?int {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public static function count(SQL $sql, ?Condition $condition = null): int|bool {
|
||||
$handler = self::getHandler($sql);
|
||||
$query = $sql->select($sql->count())
|
||||
->from($handler->getTableName());
|
||||
|
||||
if ($condition) {
|
||||
$query->where($condition);
|
||||
}
|
||||
|
||||
$res = $query->execute();
|
||||
|
||||
if (!empty($res)) {
|
||||
return $res[0]["count"];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Core\Objects\DatabaseEntity;
|
||||
namespace Core\Objects\DatabaseEntity\Controller;
|
||||
|
||||
use Core\Driver\Logger\Logger;
|
||||
use Core\Driver\SQL\Column\BoolColumn;
|
||||
use Core\Driver\SQL\Column\Column;
|
||||
use Core\Driver\SQL\Column\DateTimeColumn;
|
||||
use Core\Driver\SQL\Column\EnumColumn;
|
||||
use Core\Driver\SQL\Column\IntColumn;
|
||||
use Core\Driver\SQL\Column\JsonColumn;
|
||||
use Core\Driver\SQL\Column\StringColumn;
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Driver\SQL\Condition\CondAnd;
|
||||
use Core\Driver\SQL\Condition\CondBool;
|
||||
use Core\Driver\SQL\Condition\CondIn;
|
||||
use Core\Driver\SQL\Condition\Condition;
|
||||
use Core\Driver\SQL\Column\DoubleColumn;
|
||||
use Core\Driver\SQL\Column\FloatColumn;
|
||||
use Core\Driver\SQL\Condition\CondNot;
|
||||
use Core\Driver\SQL\Condition\CondOr;
|
||||
use Core\Driver\SQL\Constraint\ForeignKey;
|
||||
use Core\Driver\SQL\Join;
|
||||
use Core\Driver\SQL\Query\CreateProcedure;
|
||||
use Core\Driver\SQL\Query\CreateTable;
|
||||
use Core\Driver\SQL\Query\Insert;
|
||||
use Core\Driver\SQL\Query\Select;
|
||||
use Core\Driver\SQL\SQL;
|
||||
use Core\Driver\SQL\Strategy\CascadeStrategy;
|
||||
use Core\Driver\SQL\Strategy\SetNullStrategy;
|
||||
use Core\Driver\SQL\Strategy\UpdateStrategy;
|
||||
use Core\Driver\SQL\Type\CurrentColumn;
|
||||
use Core\Driver\SQL\Type\CurrentTable;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Enum;
|
||||
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Json;
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Multiple;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Transient;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Unique;
|
||||
use PHPUnit\Util\Exception;
|
||||
|
||||
class DatabaseEntityHandler {
|
||||
class DatabaseEntityHandler implements Persistable {
|
||||
|
||||
private \ReflectionClass $entityClass;
|
||||
private string $tableName;
|
||||
@@ -35,6 +48,7 @@ class DatabaseEntityHandler {
|
||||
private array $properties;
|
||||
private array $relations;
|
||||
private array $constraints;
|
||||
private array $nmRelations;
|
||||
private SQL $sql;
|
||||
private Logger $logger;
|
||||
|
||||
@@ -50,8 +64,9 @@ class DatabaseEntityHandler {
|
||||
$this->tableName = $this->entityClass->getShortName();
|
||||
$this->columns = []; // property name => database column name
|
||||
$this->properties = []; // property name => \ReflectionProperty
|
||||
$this->relations = []; // property name => referenced table name
|
||||
$this->relations = []; // property name => DatabaseEntityHandler
|
||||
$this->constraints = []; // \Driver\SQL\Constraint\Constraint
|
||||
$this->nmRelations = []; // table name => NMRelation
|
||||
|
||||
foreach ($this->entityClass->getProperties() as $property) {
|
||||
$propertyName = $property->getName();
|
||||
@@ -60,6 +75,10 @@ class DatabaseEntityHandler {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($property->isStatic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$propertyType = $property->getType();
|
||||
$columnName = self::getColumnName($propertyName);
|
||||
if (!($propertyType instanceof \ReflectionNamedType)) {
|
||||
@@ -93,18 +112,46 @@ class DatabaseEntityHandler {
|
||||
$this->columns[$propertyName] = new BoolColumn($columnName, $defaultValue ?? false);
|
||||
} else if ($propertyTypeName === 'DateTime') {
|
||||
$this->columns[$propertyName] = new DateTimeColumn($columnName, $nullable, $defaultValue);
|
||||
/*} else if ($propertyName === 'array') {
|
||||
$many = self::getAttribute($property, Many::class);
|
||||
if ($many) {
|
||||
$requestedType = $many->getValue();
|
||||
if (isClass($requestedType)) {
|
||||
$requestedClass = new \ReflectionClass($requestedType);
|
||||
/*} else if ($propertyName === 'array') {
|
||||
$many = self::getAttribute($property, Many::class);
|
||||
if ($many) {
|
||||
$requestedType = $many->getValue();
|
||||
if (isClass($requestedType)) {
|
||||
$requestedClass = new \ReflectionClass($requestedType);
|
||||
} else {
|
||||
$this->raiseError("Cannot persist class '$className': Property '$propertyName' has non persist-able type: $requestedType");
|
||||
}
|
||||
} else {
|
||||
$this->raiseError("Cannot persist class '$className': Property '$propertyName' has non persist-able type: $requestedType");
|
||||
$this->raiseError("Cannot persist class '$className': Property '$propertyName' has non persist-able type: $propertyTypeName");
|
||||
}*/
|
||||
} 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?");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
} else {
|
||||
$this->raiseError("Cannot persist class '$className': Property '$propertyName' has non persist-able type: $propertyTypeName");
|
||||
}*/
|
||||
} catch (\Exception $ex) {
|
||||
$this->raiseError("Cannot persist class '$className' property '$propertyTypeName': " . $ex->getMessage());
|
||||
}
|
||||
} else if ($propertyTypeName !== "mixed") {
|
||||
try {
|
||||
$requestedClass = new \ReflectionClass($propertyTypeName);
|
||||
@@ -167,6 +214,10 @@ class DatabaseEntityHandler {
|
||||
return $this->relations;
|
||||
}
|
||||
|
||||
public function getNMRelations(): array {
|
||||
return $this->nmRelations;
|
||||
}
|
||||
|
||||
public function getColumnNames(): array {
|
||||
$columns = ["$this->tableName.id"];
|
||||
foreach ($this->columns as $column) {
|
||||
@@ -238,6 +289,14 @@ class DatabaseEntityHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// init n:m / 1:n properties with empty arrays
|
||||
foreach ($this->nmRelations as $nmRelation) {
|
||||
foreach ($nmRelation->getProperties($this) as $property) {
|
||||
$property->setAccessible(true);
|
||||
$property->setValue($entity, []);
|
||||
}
|
||||
}
|
||||
|
||||
$this->properties["id"]->setAccessible(true);
|
||||
$this->properties["id"]->setValue($entity, $row["id"]);
|
||||
$entity->postFetch($this->sql, $row);
|
||||
@@ -248,6 +307,190 @@ class DatabaseEntityHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public function updateNM(DatabaseEntity $entity): bool {
|
||||
if (empty($this->nmRelations)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->nmRelations as $nmTable => $nmRelation) {
|
||||
|
||||
$thisIdColumn = $nmRelation->getIdColumn($this);
|
||||
$thisTableName = $this->getTableName();
|
||||
$dataColumns = $nmRelation->getDataColumns();
|
||||
$otherHandler = $nmRelation->getOtherHandler($this);
|
||||
$refIdColumn = $nmRelation->getIdColumn($otherHandler);
|
||||
|
||||
|
||||
// delete from n:m table if no longer exists
|
||||
$deleteStatement = $this->sql->delete($nmTable)
|
||||
->where(new Compare($thisIdColumn, $entity->getId()));
|
||||
|
||||
if (!empty($dataColumns)) {
|
||||
$conditions = [];
|
||||
foreach ($dataColumns[$thisTableName] as $propertyName => $columnName) {
|
||||
$property = $this->properties[$propertyName];
|
||||
$entityIds = array_keys($property->getValue($entity));
|
||||
if (!empty($entityIds)) {
|
||||
$conditions[] = new CondAnd(
|
||||
new CondBool($columnName),
|
||||
new CondNot(new CondIn(new Column($refIdColumn), $entityIds)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($conditions)) {
|
||||
$deleteStatement->where(new CondOr(...$conditions));
|
||||
}
|
||||
} else {
|
||||
$property = next($nmRelation->getProperties($this));
|
||||
$entityIds = array_keys($property->getValue($entity));
|
||||
if (!empty($entityIds)) {
|
||||
$deleteStatement->where(
|
||||
new CondNot(new CondIn(new Column($refIdColumn), $entityIds))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$deleteStatement->execute();
|
||||
}
|
||||
|
||||
return $this->insertNM($entity, true);
|
||||
}
|
||||
|
||||
public function insertNM(DatabaseEntity $entity, bool $ignoreExisting = true): bool {
|
||||
|
||||
if (empty($this->nmRelations)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$success = true;
|
||||
foreach ($this->nmRelations as $nmTable => $nmRelation) {
|
||||
$otherHandler = $nmRelation->getOtherHandler($this);
|
||||
$thisIdColumn = $nmRelation->getIdColumn($this);
|
||||
$thisTableName = $this->getTableName();
|
||||
$refIdColumn = $nmRelation->getIdColumn($otherHandler);
|
||||
$dataColumns = $nmRelation->getDataColumns();
|
||||
|
||||
$columns = [
|
||||
$thisIdColumn,
|
||||
$refIdColumn,
|
||||
];
|
||||
|
||||
if (!empty($dataColumns)) {
|
||||
$columns = array_merge($columns, array_values($dataColumns[$thisTableName]));
|
||||
}
|
||||
|
||||
$statement = $this->sql->insert($nmTable, $columns);
|
||||
if ($ignoreExisting) {
|
||||
$statement->onDuplicateKeyStrategy(new UpdateStrategy($nmRelation->getAllColumns(), [
|
||||
$thisIdColumn => $entity->getId()
|
||||
]));
|
||||
}
|
||||
|
||||
foreach ($nmRelation->getProperties($this) as $property) {
|
||||
$property->setAccessible(true);
|
||||
$relEntities = $property->getValue($entity);
|
||||
foreach ($relEntities as $relEntity) {
|
||||
$nmRow = [$entity->getId(), $relEntity->getId()];
|
||||
if (!empty($dataColumns)) {
|
||||
foreach (array_keys($dataColumns[$thisTableName]) as $propertyName) {
|
||||
$nmRow[] = $property->getName() === $propertyName;
|
||||
}
|
||||
}
|
||||
$statement->addRow(...$nmRow);
|
||||
}
|
||||
}
|
||||
|
||||
$success = $statement->execute() && $success;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
public function fetchNMRelations(array $entities, bool $recursive = false) {
|
||||
|
||||
if ($recursive) {
|
||||
foreach ($entities as $entity) {
|
||||
foreach ($this->relations as $propertyName => $relHandler) {
|
||||
$relEntity = $this->properties[$propertyName]->getValue($entity);
|
||||
if ($relEntity) {
|
||||
$relHandler->fetchNMRelations([$relEntity->getId() => $relEntity], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->nmRelations)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entityIds = array_keys($entities);
|
||||
foreach ($this->nmRelations as $nmTable => $nmRelation) {
|
||||
$otherHandler = $nmRelation->getOtherHandler($this);
|
||||
|
||||
$thisIdColumn = $nmRelation->getIdColumn($this);
|
||||
$thisProperties = $nmRelation->getProperties($this);
|
||||
$thisTableName = $this->getTableName();
|
||||
|
||||
$refIdColumn = $nmRelation->getIdColumn($otherHandler);
|
||||
$refProperties = $nmRelation->getProperties($otherHandler);
|
||||
$refTableName = $otherHandler->getTableName();
|
||||
|
||||
$dataColumns = $nmRelation->getDataColumns();
|
||||
|
||||
$relEntityQuery = DatabaseEntityQuery::fetchAll($otherHandler)
|
||||
->addJoin(new Join("INNER", $nmTable, "$nmTable.$refIdColumn", "$refTableName.id"))
|
||||
->where(new CondIn(new Column($thisIdColumn), $entityIds))
|
||||
->getQuery();
|
||||
|
||||
$relEntityQuery->addColumn($thisIdColumn);
|
||||
foreach ($dataColumns as $tableDataColumns) {
|
||||
foreach ($tableDataColumns as $columnName) {
|
||||
$relEntityQuery->addColumn($columnName);
|
||||
}
|
||||
}
|
||||
|
||||
$rows = $relEntityQuery->execute();
|
||||
if (!is_array($rows)) {
|
||||
$this->logger->error("Error fetching n:m relations from table: '$nmTable': " . $this->sql->getLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
$relEntities = [];
|
||||
foreach ($rows as $row) {
|
||||
$relId = $row["id"];
|
||||
if (!isset($relEntities[$relId])) {
|
||||
$relEntity = $otherHandler->entityFromRow($row);
|
||||
$relEntities[$relId] = $relEntity;
|
||||
}
|
||||
|
||||
$thisEntity = $entities[$row[$thisIdColumn]];
|
||||
$relEntity = $relEntities[$relId];
|
||||
$mappings = [
|
||||
[$refProperties, $refTableName, $relEntity, $thisEntity],
|
||||
[$thisProperties, $thisTableName, $thisEntity, $relEntity],
|
||||
];
|
||||
|
||||
foreach ($mappings as $mapping) {
|
||||
list($properties, $tableName, $targetEntity, $entityToAdd) = $mapping;
|
||||
foreach ($properties as $propertyName => $property) {
|
||||
$addToProperty = empty($dataColumns);
|
||||
if (!$addToProperty) {
|
||||
$columnName = $dataColumns[$tableName][$propertyName] ?? null;
|
||||
$addToProperty = ($columnName && $this->sql->parseBool($row[$columnName]));
|
||||
}
|
||||
|
||||
if ($addToProperty) {
|
||||
$targetArray = $property->getValue($targetEntity);
|
||||
$targetArray[$entityToAdd->getId()] = $entityToAdd;
|
||||
$property->setValue($targetEntity, $targetArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getSelectQuery(): Select {
|
||||
return $this->sql->select(...$this->getColumnNames())
|
||||
->from($this->tableName);
|
||||
@@ -259,11 +502,14 @@ class DatabaseEntityHandler {
|
||||
->first()
|
||||
->execute();
|
||||
|
||||
if ($res === false || $res === null) {
|
||||
return $res;
|
||||
} else {
|
||||
return $this->entityFromRow($res);
|
||||
if ($res !== false && $res !== null) {
|
||||
$res = $this->entityFromRow($res);
|
||||
if ($res instanceof DatabaseEntity) {
|
||||
$this->fetchNMRelations([$res->getId() => $res]);
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function fetchMultiple(?Condition $condition = null): ?array {
|
||||
@@ -284,12 +530,56 @@ class DatabaseEntityHandler {
|
||||
$entities[$entity->getId()] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
$this->fetchNMRelations($entities);
|
||||
return $entities;
|
||||
}
|
||||
}
|
||||
|
||||
public function getTableQuery(): CreateTable {
|
||||
$query = $this->sql->createTable($this->tableName)
|
||||
public function getCreateQueries(SQL $sql): array {
|
||||
|
||||
$queries = [];
|
||||
$queries[] = $this->getTableQuery($sql);
|
||||
|
||||
$table = $this->getTableName();
|
||||
$entityLogConfig = $this->entityClass->getProperty("entityLogConfig");
|
||||
$entityLogConfig->setAccessible(true);
|
||||
$entityLogConfig = $entityLogConfig->getValue();
|
||||
|
||||
if (isset($entityLogConfig["insert"]) && $entityLogConfig["insert"] === true) {
|
||||
$queries[] = $sql->createTrigger("${table}_trg_insert")
|
||||
->after()->insert($table)
|
||||
->exec(new CreateProcedure($sql, "InsertEntityLog"), [
|
||||
"tableName" => new CurrentTable(),
|
||||
"entityId" => new CurrentColumn("id"),
|
||||
"lifetime" => $entityLogConfig["lifetime"] ?? 90,
|
||||
]);
|
||||
}
|
||||
|
||||
if (isset($entityLogConfig["update"]) && $entityLogConfig["update"] === true) {
|
||||
$queries[] = $sql->createTrigger("${table}_trg_update")
|
||||
->after()->update($table)
|
||||
->exec(new CreateProcedure($sql, "UpdateEntityLog"), [
|
||||
"tableName" => new CurrentTable(),
|
||||
"entityId" => new CurrentColumn("id"),
|
||||
]);
|
||||
}
|
||||
|
||||
if (isset($entityLogConfig["delete"]) && $entityLogConfig["delete"] === true) {
|
||||
$queries[] = $sql->createTrigger("${table}_trg_delete")
|
||||
->after()->delete($table)
|
||||
->exec(new CreateProcedure($sql, "DeleteEntityLog"), [
|
||||
"tableName" => new CurrentTable(),
|
||||
"entityId" => new CurrentColumn("id"),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
return $queries;
|
||||
}
|
||||
|
||||
public function getTableQuery(SQL $sql): CreateTable {
|
||||
$query = $sql->createTable($this->tableName)
|
||||
->onlyIfNotExists()
|
||||
->addSerial("id")
|
||||
->primaryKey("id");
|
||||
@@ -305,12 +595,7 @@ class DatabaseEntityHandler {
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function createTable(): bool {
|
||||
$query = $this->getTableQuery();
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
private function prepareRow(DatabaseEntity $entity, string $action, ?array $columns = null) {
|
||||
private function prepareRow(DatabaseEntity $entity, string $action, ?array $columns = null): bool|array {
|
||||
$row = [];
|
||||
foreach ($this->columns as $propertyName => $column) {
|
||||
if ($columns && !in_array($column->getName(), $columns)) {
|
||||
@@ -344,7 +629,7 @@ class DatabaseEntityHandler {
|
||||
return $row;
|
||||
}
|
||||
|
||||
public function update(DatabaseEntity $entity, ?array $columns = null) {
|
||||
public function update(DatabaseEntity $entity, ?array $columns = null, bool $saveNM = false) {
|
||||
$row = $this->prepareRow($entity, "update", $columns);
|
||||
if ($row === false) {
|
||||
return false;
|
||||
@@ -358,10 +643,15 @@ class DatabaseEntityHandler {
|
||||
$query->set($columnName, $value);
|
||||
}
|
||||
|
||||
return $query->execute();
|
||||
$res = $query->execute();
|
||||
if ($res && $saveNM) {
|
||||
$res = $this->updateNM($entity);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function insert(DatabaseEntity $entity) {
|
||||
public function insert(DatabaseEntity $entity): bool|int {
|
||||
$row = $this->prepareRow($entity, "insert");
|
||||
if ($row === false) {
|
||||
return false;
|
||||
@@ -391,12 +681,12 @@ class DatabaseEntityHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public function insertOrUpdate(DatabaseEntity $entity, ?array $columns = null) {
|
||||
public function insertOrUpdate(DatabaseEntity $entity, ?array $columns = null, bool $saveNM = false) {
|
||||
$id = $entity->getId();
|
||||
if ($id === null) {
|
||||
return $this->insert($entity);
|
||||
} else {
|
||||
return $this->update($entity, $columns);
|
||||
return $this->update($entity, $columns, $saveNM);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,4 +705,30 @@ class DatabaseEntityHandler {
|
||||
public function getSQL(): SQL {
|
||||
return $this->sql;
|
||||
}
|
||||
|
||||
public function getInsertQuery(DatabaseEntity|array $entities): ?Insert {
|
||||
|
||||
if (empty($entities)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$firstEntity = (is_array($entities) ? current($entities) : $entities);
|
||||
$firstRow = $this->prepareRow($firstEntity, "insert");
|
||||
|
||||
$statement = $this->sql->insert($this->tableName, array_keys($firstRow))
|
||||
->addRow(...array_values($firstRow));
|
||||
|
||||
if (is_array($entities)) {
|
||||
foreach ($entities as $entity) {
|
||||
if ($entity === $firstEntity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row = $this->prepareRow($entity, "insert");
|
||||
$statement->addRow(...array_values($row));
|
||||
}
|
||||
}
|
||||
|
||||
return $statement;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Core\Objects\DatabaseEntity;
|
||||
namespace Core\Objects\DatabaseEntity\Controller;
|
||||
|
||||
use Core\Driver\Logger\Logger;
|
||||
use Core\Driver\SQL\Condition\Condition;
|
||||
@@ -14,18 +14,25 @@ use Core\Driver\SQL\SQL;
|
||||
*/
|
||||
class DatabaseEntityQuery {
|
||||
|
||||
const FETCH_NONE = 0;
|
||||
const FETCH_DIRECT = 1;
|
||||
const FETCH_RECURSIVE = 2;
|
||||
|
||||
private Logger $logger;
|
||||
private DatabaseEntityHandler $handler;
|
||||
private Select $selectQuery;
|
||||
private int $resultType;
|
||||
private bool $logVerbose;
|
||||
|
||||
private int $fetchSubEntities;
|
||||
|
||||
private function __construct(DatabaseEntityHandler $handler, int $resultType) {
|
||||
$this->handler = $handler;
|
||||
$this->selectQuery = $handler->getSelectQuery();
|
||||
$this->logger = new Logger("DB-EntityQuery", $handler->getSQL());
|
||||
$this->resultType = $resultType;
|
||||
$this->logVerbose = false;
|
||||
$this->fetchSubEntities = self::FETCH_NONE;
|
||||
|
||||
if ($this->resultType === SQL::FETCH_ONE) {
|
||||
$this->selectQuery->first();
|
||||
@@ -50,6 +57,11 @@ class DatabaseEntityQuery {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function offset(int $offset): static {
|
||||
$this->selectQuery->offset($offset);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function where(Condition ...$condition): DatabaseEntityQuery {
|
||||
$this->selectQuery->where(...$condition);
|
||||
return $this;
|
||||
@@ -74,6 +86,7 @@ class DatabaseEntityQuery {
|
||||
public function fetchEntities(bool $recursive = false): DatabaseEntityQuery {
|
||||
|
||||
// $this->selectQuery->dump();
|
||||
$this->fetchSubEntities = ($recursive ? self::FETCH_RECURSIVE : self::FETCH_DIRECT);
|
||||
|
||||
$relIndex = 1;
|
||||
foreach ($this->handler->getRelations() as $propertyName => $relationHandler) {
|
||||
@@ -136,9 +149,19 @@ class DatabaseEntityQuery {
|
||||
$entities[$entity->getId()] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->fetchSubEntities !== self::FETCH_NONE) {
|
||||
$this->handler->fetchNMRelations($entities, $this->fetchSubEntities === self::FETCH_RECURSIVE);
|
||||
}
|
||||
|
||||
return $entities;
|
||||
} else if ($this->resultType === SQL::FETCH_ONE) {
|
||||
return $this->handler->entityFromRow($res);
|
||||
$entity = $this->handler->entityFromRow($res);
|
||||
if ($entity instanceof DatabaseEntity && $this->fetchSubEntities !== self::FETCH_NONE) {
|
||||
$this->handler->fetchNMRelations([$entity->getId() => $entity], $this->fetchSubEntities === self::FETCH_RECURSIVE);
|
||||
}
|
||||
|
||||
return $entity;
|
||||
} else {
|
||||
$this->handler->getLogger()->error("Invalid result type for query builder, must be FETCH_ALL or FETCH_ONE");
|
||||
return null;
|
||||
@@ -149,4 +172,8 @@ class DatabaseEntityQuery {
|
||||
$this->selectQuery->addJoin($join);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getQuery(): Select {
|
||||
return $this->selectQuery;
|
||||
}
|
||||
}
|
||||
134
Core/Objects/DatabaseEntity/Controller/NMRelation.class.php
Normal file
134
Core/Objects/DatabaseEntity/Controller/NMRelation.class.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace Core\Objects\DatabaseEntity\Controller;
|
||||
|
||||
# TODO: Allow more than 2 relations here?
|
||||
|
||||
use Core\Driver\SQL\Query\CreateTable;
|
||||
use Core\Driver\SQL\SQL;
|
||||
use Core\Driver\SQL\Strategy\CascadeStrategy;
|
||||
|
||||
class NMRelation implements Persistable {
|
||||
|
||||
private DatabaseEntityHandler $handlerA;
|
||||
private DatabaseEntityHandler $handlerB;
|
||||
private array $properties;
|
||||
|
||||
public function __construct(DatabaseEntityHandler $handlerA, DatabaseEntityHandler $handlerB) {
|
||||
$this->handlerA = $handlerA;
|
||||
$this->handlerB = $handlerB;
|
||||
$tableNameA = $handlerA->getTableName();
|
||||
$tableNameB = $handlerB->getTableName();
|
||||
if ($tableNameA === $tableNameB) {
|
||||
throw new \Exception("Cannot create N:M Relation with only one table");
|
||||
}
|
||||
|
||||
$this->properties = [
|
||||
$tableNameA => [],
|
||||
$tableNameB => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function addProperty(DatabaseEntityHandler $src, \ReflectionProperty $property): void {
|
||||
$this->properties[$src->getTableName()][$property->getName()] = $property;
|
||||
}
|
||||
|
||||
public function getIdColumn(DatabaseEntityHandler $handler): string {
|
||||
return DatabaseEntityHandler::getColumnName($handler->getTableName()) . "_id";
|
||||
}
|
||||
|
||||
public function getDataColumns(): array {
|
||||
|
||||
$referenceCount = 0;
|
||||
$columnsNeeded = false;
|
||||
|
||||
// if in one of the relations we have multiple references, we need to differentiate
|
||||
foreach ($this->properties as $refProperties) {
|
||||
$referenceCount += count($refProperties);
|
||||
if ($referenceCount > 1) {
|
||||
$columnsNeeded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$columns = [];
|
||||
if ($columnsNeeded) {
|
||||
foreach ($this->properties as $tableName => $properties) {
|
||||
$columns[$tableName] = [];
|
||||
foreach ($properties as $property) {
|
||||
$columnName = DatabaseEntityHandler::getColumnName($tableName) . "_" .
|
||||
DatabaseEntityHandler::getColumnName($property->getName());
|
||||
$columns[$tableName][$property->getName()] = $columnName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
public function getAllColumns(): array {
|
||||
$relIdA = $this->getIdColumn($this->handlerA);
|
||||
$relIdB = $this->getIdColumn($this->handlerB);
|
||||
|
||||
$columns = [$relIdA, $relIdB];
|
||||
|
||||
foreach ($this->getDataColumns() as $dataColumns) {
|
||||
foreach ($dataColumns as $columnName) {
|
||||
$columns[] = $columnName;
|
||||
}
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
public function getTableQuery(SQL $sql): CreateTable {
|
||||
|
||||
$tableNameA = $this->handlerA->getTableName();
|
||||
$tableNameB = $this->handlerB->getTableName();
|
||||
|
||||
$columns = $this->getAllColumns();
|
||||
list ($relIdA, $relIdB) = $columns;
|
||||
$dataColumns = array_slice($columns, 2);
|
||||
$query = $sql->createTable(self::buildTableName($tableNameA, $tableNameB))
|
||||
->addInt($relIdA)
|
||||
->addInt($relIdB)
|
||||
->foreignKey($relIdA, $tableNameA, "id", new CascadeStrategy())
|
||||
->foreignKey($relIdB, $tableNameB, "id", new CascadeStrategy());
|
||||
|
||||
foreach ($dataColumns as $dataColumn) {
|
||||
$query->addBool($dataColumn, false);
|
||||
}
|
||||
|
||||
$query->unique(...$columns);
|
||||
return $query;
|
||||
}
|
||||
|
||||
public static function buildTableName(string ...$tables): string {
|
||||
sort($tables);
|
||||
return "NM_" . implode("_", $tables);
|
||||
}
|
||||
|
||||
public function dependsOn(): array {
|
||||
return [$this->handlerA->getTableName(), $this->handlerB->getTableName()];
|
||||
}
|
||||
|
||||
public function getTableName(): string {
|
||||
return self::buildTableName(...$this->dependsOn());
|
||||
}
|
||||
|
||||
public function getCreateQueries(SQL $sql): array {
|
||||
return [$this->getTableQuery($sql)];
|
||||
}
|
||||
|
||||
public function getProperties(DatabaseEntityHandler $handler): array {
|
||||
return $this->properties[$handler->getTableName()];
|
||||
}
|
||||
|
||||
public function getOtherHandler(DatabaseEntityHandler $handler): DatabaseEntityHandler {
|
||||
if ($handler === $this->handlerA) {
|
||||
return $this->handlerB;
|
||||
} else {
|
||||
return $this->handlerA;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Core\Objects\DatabaseEntity\Controller;
|
||||
|
||||
use Core\Driver\SQL\SQL;
|
||||
|
||||
interface Persistable {
|
||||
|
||||
public function dependsOn(): array;
|
||||
public function getTableName(): string;
|
||||
public function getCreateQueries(SQL $sql): array;
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use Core\Driver\SQL\Expression\CurrentTimeStamp;
|
||||
use Core\Driver\SQL\SQL;
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||
|
||||
class GpgKey extends DatabaseEntity {
|
||||
|
||||
|
||||
@@ -3,14 +3,27 @@
|
||||
namespace Core\Objects\DatabaseEntity;
|
||||
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||
|
||||
class Group extends DatabaseEntity {
|
||||
|
||||
const ADMIN = 1;
|
||||
const MODERATOR = 3;
|
||||
const SUPPORT = 2;
|
||||
|
||||
const GROUPS = [
|
||||
self::ADMIN => "Administrator",
|
||||
self::MODERATOR => "Moderator",
|
||||
self::SUPPORT => "Support",
|
||||
];
|
||||
|
||||
#[MaxLength(32)] public string $name;
|
||||
#[MaxLength(10)] public string $color;
|
||||
|
||||
public function __construct(?int $id = null) {
|
||||
public function __construct(?int $id, string $name, string $color) {
|
||||
parent::__construct($id);
|
||||
$this->name = $name;
|
||||
$this->color = $color;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array {
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
|
||||
namespace Core\Objects\DatabaseEntity {
|
||||
|
||||
use Core\Driver\SQL\SQL;
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Transient;
|
||||
use Core\Objects\lang\LanguageModule;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||
|
||||
// TODO: language from cookie?
|
||||
class Language extends DatabaseEntity {
|
||||
|
||||
const AMERICAN_ENGLISH = 1;
|
||||
const GERMAN_STANDARD = 2;
|
||||
|
||||
const LANG_CODE_PATTERN = "/^[a-zA-Z]{2}_[a-zA-Z]{2}$/";
|
||||
|
||||
#[MaxLength(5)] private string $code;
|
||||
|
||||
124
Core/Objects/DatabaseEntity/MailQueueItem.class.php
Normal file
124
Core/Objects/DatabaseEntity/MailQueueItem.class.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Core\Objects\DatabaseEntity;
|
||||
|
||||
use Core\API\Mail\Send;
|
||||
use Core\Driver\SQL\Expression\CurrentTimeStamp;
|
||||
use Core\Objects\Context;
|
||||
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
||||
use Core\Objects\DatabaseEntity\Attribute\EnumArr;
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||
|
||||
class MailQueueItem extends DatabaseEntity {
|
||||
|
||||
protected static array $entityLogConfig = [
|
||||
"update" => true,
|
||||
"delete" => true,
|
||||
"insert" => true,
|
||||
"lifetime" => 30
|
||||
];
|
||||
|
||||
const STATUS_WAITING = "waiting";
|
||||
const STATUS_SUCCESS = "success";
|
||||
const STATUS_ERROR = "error";
|
||||
const STATUS_ITEMS = [self::STATUS_WAITING, self::STATUS_SUCCESS, self::STATUS_ERROR];
|
||||
|
||||
#[MaxLength(64)]
|
||||
private string $from;
|
||||
|
||||
#[MaxLength(64)]
|
||||
private string $to;
|
||||
|
||||
private string $subject;
|
||||
private string $body;
|
||||
|
||||
#[MaxLength(64)]
|
||||
private ?string $replyTo;
|
||||
|
||||
#[MaxLength(64)]
|
||||
private ?string $replyName;
|
||||
|
||||
#[MaxLength(64)]
|
||||
private ?string $gpgFingerprint;
|
||||
|
||||
#[EnumArr(self::STATUS_ITEMS)]
|
||||
#[DefaultValue(self::STATUS_WAITING)]
|
||||
private string $status;
|
||||
|
||||
#[DefaultValue(5)]
|
||||
private int $retryCount;
|
||||
|
||||
#[DefaultValue(CurrentTimeStamp::class)]
|
||||
private \DateTime $nextTry;
|
||||
|
||||
private ?string $errorMessage;
|
||||
|
||||
public function __construct(string $fromMail, string $toMail, string $subject, string $body,
|
||||
?string $replyTo, ?string $replyName, ?string $gpgFingerprint) {
|
||||
parent::__construct();
|
||||
$this->from = $fromMail;
|
||||
$this->to = $toMail;
|
||||
$this->subject = $subject;
|
||||
$this->body = $body;
|
||||
$this->replyTo = $replyTo;
|
||||
$this->replyName = $replyName;
|
||||
$this->gpgFingerprint = $gpgFingerprint;
|
||||
$this->retryCount = 5;
|
||||
$this->nextTry = new \DateTime();
|
||||
$this->errorMessage = null;
|
||||
$this->status = self::STATUS_WAITING;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array {
|
||||
return [
|
||||
"id" => $this->getId(),
|
||||
"from" => $this->from,
|
||||
"to" => $this->to,
|
||||
"gpgFingerprint" => $this->gpgFingerprint,
|
||||
"subject" => $this->subject,
|
||||
"message" => $this->body,
|
||||
"status" => $this->status,
|
||||
"reply" => [
|
||||
"to" => $this->replyTo,
|
||||
"name" => $this->replyName,
|
||||
],
|
||||
"retryCount" => $this->retryCount,
|
||||
"nextTry" => $this->nextTry->getTimestamp(),
|
||||
"errorMessage" => $this->errorMessage,
|
||||
];
|
||||
}
|
||||
|
||||
public function send(Context $context): bool {
|
||||
|
||||
$args = [
|
||||
"to" => $this->to,
|
||||
"subject" => $this->subject,
|
||||
"body" => $this->body,
|
||||
"replyTo" => $this->replyTo,
|
||||
"replyName" => $this->replyName,
|
||||
"gpgFingerprint" => $this->gpgFingerprint,
|
||||
"async" => false
|
||||
];
|
||||
|
||||
$req = new Send($context);
|
||||
$success = $req->execute($args);
|
||||
$this->errorMessage = $req->getLastError();
|
||||
|
||||
$delay = [0, 720, 360, 60, 30, 1];
|
||||
$minutes = $delay[max(0, min(count($delay) - 1, $this->retryCount))];
|
||||
if ($this->retryCount > 0) {
|
||||
$this->retryCount--;
|
||||
$this->nextTry = (new \DateTime())->modify("+$minutes minute");
|
||||
} else if (!$success) {
|
||||
$this->status = self::STATUS_ERROR;
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
$this->status = self::STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
$this->save($context->getSQL());
|
||||
return $success;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use Core\API\Parameter\Parameter;
|
||||
use Core\Driver\SQL\Expression\CurrentTimeStamp;
|
||||
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||
|
||||
class News extends DatabaseEntity {
|
||||
|
||||
@@ -16,6 +17,7 @@ class News extends DatabaseEntity {
|
||||
|
||||
public function __construct(?int $id = null) {
|
||||
parent::__construct($id);
|
||||
$this->publishedAt = new \DateTime();
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Core\Objects\DatabaseEntity;
|
||||
|
||||
use Core\API\Parameter\Parameter;
|
||||
use Core\Driver\SQL\Expression\CurrentTimeStamp;
|
||||
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Enum;
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
|
||||
class Notification extends DatabaseEntity {
|
||||
|
||||
#[Enum('default', 'message', 'warning')] private string $type;
|
||||
#[DefaultValue(CurrentTimeStamp::class)] private \DateTime $createdAt;
|
||||
#[MaxLength(32)] public string $title;
|
||||
#[MaxLength(256)] public string $message;
|
||||
|
||||
public function __construct(?int $id = null) {
|
||||
parent::__construct($id);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array {
|
||||
return [
|
||||
"id" => $this->getId(),
|
||||
"createdAt" => $this->createdAt->format(Parameter::DATE_TIME_FORMAT),
|
||||
"title" => $this->title,
|
||||
"message" => $this->message
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Json;
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Transient;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||
|
||||
class Session extends DatabaseEntity {
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use Core\Driver\SQL\Expression\CurrentTimeStamp;
|
||||
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Enum;
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||
|
||||
class SystemLog extends DatabaseEntity {
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use Core\Objects\DatabaseEntity\Attribute\Enum;
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\TwoFactor\KeyBasedTwoFactorToken;
|
||||
use Core\Objects\TwoFactor\TimeBasedTwoFactorToken;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||
|
||||
abstract class TwoFactorToken extends DatabaseEntity {
|
||||
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
|
||||
namespace Core\Objects\DatabaseEntity;
|
||||
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Driver\SQL\Expression\CurrentTimeStamp;
|
||||
use Core\Driver\SQL\Join;
|
||||
use Core\Driver\SQL\SQL;
|
||||
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Transient;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Multiple;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Unique;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||
|
||||
class User extends DatabaseEntity {
|
||||
|
||||
@@ -20,30 +19,16 @@ class User extends DatabaseEntity {
|
||||
#[MaxLength(64)] public ?string $profilePicture;
|
||||
private ?\DateTime $lastOnline;
|
||||
#[DefaultValue(CurrentTimeStamp::class)] public \DateTime $registeredAt;
|
||||
public bool $confirmed;
|
||||
#[DefaultValue(1)] public Language $language;
|
||||
#[DefaultValue(false)] public bool $confirmed;
|
||||
#[DefaultValue(Language::AMERICAN_ENGLISH)] public Language $language;
|
||||
public ?GpgKey $gpgKey;
|
||||
private ?TwoFactorToken $twoFactorToken;
|
||||
|
||||
#[Transient] private array $groups;
|
||||
#[Multiple(Group::class)]
|
||||
public array $groups;
|
||||
|
||||
public function __construct(?int $id = null) {
|
||||
parent::__construct($id);
|
||||
$this->groups = [];
|
||||
}
|
||||
|
||||
public function postFetch(SQL $sql, array $row) {
|
||||
parent::postFetch($sql, $row);
|
||||
$this->groups = [];
|
||||
|
||||
$groups = Group::findAllBuilder($sql)
|
||||
->addJoin(new Join("INNER", "UserGroup", "UserGroup.group_id", "Group.id"))
|
||||
->where(new Compare("UserGroup.user_id", $this->id))
|
||||
->execute();
|
||||
|
||||
if ($groups) {
|
||||
$this->groups = $groups;
|
||||
}
|
||||
}
|
||||
|
||||
public function getUsername(): string {
|
||||
|
||||
@@ -6,6 +6,7 @@ use Core\Driver\SQL\SQL;
|
||||
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
||||
use Core\Objects\DatabaseEntity\Attribute\EnumArr;
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||
|
||||
class UserToken extends DatabaseEntity {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user