web-base/Core/Objects/DatabaseEntity/Controller/DatabaseEntity.class.php

289 lines
8.4 KiB
PHP
Raw Permalink Normal View History

2022-06-17 20:53:35 +02:00
<?php
2022-11-20 17:13:53 +01:00
namespace Core\Objects\DatabaseEntity\Controller;
2022-06-17 20:53:35 +02:00
use ArrayAccess;
2023-01-22 12:32:18 +01:00
use Core\Driver\SQL\Condition\Compare;
2022-11-18 18:06:46 +01:00
use Core\Driver\SQL\Condition\Condition;
use Core\Driver\SQL\Expression\Count;
2022-11-18 18:06:46 +01:00
use Core\Driver\SQL\SQL;
2023-01-07 15:34:05 +01:00
use Core\Objects\Context;
use Core\Objects\DatabaseEntity\Attribute\Transient;
2023-01-07 15:34:05 +01:00
use Core\Objects\DatabaseEntity\Attribute\Visibility;
use JsonSerializable;
2022-06-17 20:53:35 +02:00
abstract class DatabaseEntity implements ArrayAccess, JsonSerializable {
2022-06-17 20:53:35 +02:00
2022-11-20 17:13:53 +01:00
protected static array $entityLogConfig = [
"insert" => false,
"update" => false,
"delete" => false,
"lifetime" => null,
];
2022-06-17 20:53:35 +02:00
private static array $handlers = [];
2022-06-20 19:52:31 +02:00
protected ?int $id;
#[Transient] public array $customData = [];
2022-06-17 20:53:35 +02:00
2022-06-20 19:52:31 +02:00
public function __construct(?int $id = null) {
$this->id = $id;
2022-06-17 20:53:35 +02:00
}
public function offsetExists(mixed $offset): bool {
return property_exists($this, $offset) || array_key_exists($offset, $this->customData);
}
public function offsetGet(mixed $offset): mixed {
if (property_exists($this, $offset)) {
return $this->{$offset};
} else {
return $this->customData[$offset];
}
}
public function offsetSet(mixed $offset, mixed $value): void {
if (property_exists($this, $offset)) {
$this->{$offset} = $value;
} else {
$this->customData[$offset] = $value;
}
}
public function offsetUnset(mixed $offset): void {
if (array_key_exists($offset, $this->customData)) {
unset($this->customData[$offset]);
}
}
2023-01-07 15:34:05 +01:00
public function jsonSerialize(?array $propertyNames = null): array {
2023-01-15 00:32:17 +01:00
$reflectionClass = (new \ReflectionClass(get_called_class()));
$properties = $reflectionClass->getProperties();
2023-01-16 21:47:23 +01:00
while ($reflectionClass->getParentClass()->getName() !== DatabaseEntity::class) {
$reflectionClass = $reflectionClass->getParentClass();
$properties = array_merge($reflectionClass->getProperties(), $properties);
}
2023-01-07 15:34:05 +01:00
$ignoredProperties = ["entityLogConfig", "customData"];
$jsonArray = [];
foreach ($properties as $property) {
$propertyName = $property->getName();
2023-01-15 00:32:17 +01:00
2023-01-07 15:34:05 +01:00
if (in_array($propertyName, $ignoredProperties)) {
continue;
}
2023-01-15 00:32:17 +01:00
if (DatabaseEntityHandler::getAttribute($property, Transient::class)) {
2023-01-07 15:34:05 +01:00
continue;
}
$visibility = DatabaseEntityHandler::getAttribute($property, Visibility::class);
if ($visibility) {
$visibilityType = $visibility->getType();
if ($visibilityType === Visibility::NONE) {
continue;
} else if ($visibilityType === Visibility::BY_GROUP) {
$currentUser = Context::instance()->getUser();
$groups = $visibility->getGroups();
if (!empty($groups)) {
if (!$currentUser || empty(array_intersect(array_keys($currentUser->getGroups()), $groups))) {
continue;
}
}
}
}
if ($propertyNames === null || isset($propertyNames[$propertyName]) || in_array($propertyName, $propertyNames)) {
if ($property->isInitialized($this)) {
$value = $property->getValue($this);
if ($value instanceof \DateTime) {
$value = $value->getTimestamp();
} else if ($value instanceof DatabaseEntity) {
$subPropertyNames = $propertyNames[$propertyName] ?? null;
2023-01-15 00:32:17 +01:00
if ($subPropertyNames === null && $value instanceof $this) {
$subPropertyNames = $propertyNames;
}
2023-01-07 15:34:05 +01:00
$value = $value->jsonSerialize($subPropertyNames);
2023-01-11 14:05:45 +01:00
} else if (is_array($value)) {
$subPropertyNames = $propertyNames[$propertyName] ?? null;
$value = array_map(function ($item) use ($subPropertyNames) {
if ($item instanceof DatabaseEntity) {
$item = $item->jsonSerialize($subPropertyNames);
}
return $item;
}, $value);
2023-01-07 15:34:05 +01:00
}
2023-01-15 00:32:17 +01:00
$jsonArray[$propertyName] = $value;
2023-01-07 15:34:05 +01:00
}
}
}
2023-01-11 15:28:47 +01:00
if ($propertyNames === null && !empty($this->customData)) {
$jsonArray = array_merge($jsonArray, $this->customData);
}
2023-01-07 15:34:05 +01:00
return $jsonArray;
}
public static function toJsonArray(array $entities, ?array $properties = null): array {
return array_map(function ($entity) use ($properties) {
return $entity->jsonSerialize($properties);
}, $entities);
}
2022-06-20 19:52:31 +02:00
2023-01-22 12:32:18 +01:00
// hooks
2022-06-20 19:52:31 +02:00
public function preInsert(array &$row) { }
public function postFetch(SQL $sql, array $row) { }
2023-01-12 20:55:32 +01:00
public function postUpdate() { }
2023-01-09 20:27:01 +01:00
public static function getPredefinedValues(): array { return []; }
public function postDelete() { }
2022-06-20 19:52:31 +02:00
2023-01-22 12:32:18 +01:00
public static function newInstance(\ReflectionClass $reflectionClass, array $row) {
2022-06-20 19:52:31 +02:00
return $reflectionClass->newInstanceWithoutConstructor();
}
public static function find(SQL $sql, int $id, bool $fetchEntities = false, bool $fetchRecursive = false): static|bool|null {
$handler = self::getHandler($sql);
2022-06-20 19:52:31 +02:00
if ($fetchEntities) {
2023-01-22 12:32:18 +01:00
$context = new DatabaseEntityQueryContext();
2022-06-20 19:52:31 +02:00
return DatabaseEntityQuery::fetchOne(self::getHandler($sql))
2023-01-22 12:32:18 +01:00
->withContext($context)
->whereEq($handler->getTableName() . ".id", $id)
2022-06-20 19:52:31 +02:00
->fetchEntities($fetchRecursive)
->execute();
} else {
return $handler->fetchOne($id);
}
}
public static function exists(SQL $sql, int $id): bool {
2023-01-22 12:32:18 +01:00
$count = self::count($sql, new Compare("id", $id));
return $count !== false && $count !== 0;
2022-06-20 19:52:31 +02:00
}
2022-11-20 17:13:53 +01:00
public static function findBy(DatabaseEntityQuery $dbQuery): static|array|bool|null {
return $dbQuery->execute();
2022-06-17 20:53:35 +02:00
}
2022-06-17 23:29:24 +02:00
public static function findAll(SQL $sql, ?Condition $condition = null): ?array {
2023-01-22 12:32:18 +01:00
$query = self::createBuilder($sql, false);
if ($condition) {
$query->where($condition);
}
return $query->execute();
2022-06-17 20:53:35 +02:00
}
2022-11-20 17:13:53 +01:00
public static function createBuilder(SQL $sql, bool $one): DatabaseEntityQuery {
2023-01-22 12:32:18 +01:00
$context = new DatabaseEntityQueryContext();
2022-11-20 17:13:53 +01:00
if ($one) {
2023-01-22 12:32:18 +01:00
return DatabaseEntityQuery::fetchOne(self::getHandler($sql))->withContext($context);
2022-11-20 17:13:53 +01:00
} else {
2023-01-22 12:32:18 +01:00
return DatabaseEntityQuery::fetchAll(self::getHandler($sql))->withContext($context);
2022-11-20 17:13:53 +01:00
}
2022-06-20 19:52:31 +02:00
}
2023-01-09 14:21:11 +01:00
public function save(SQL $sql, ?array $properties = null, bool $saveNM = false): bool {
$handler = self::getHandler($sql);
2023-01-09 14:21:11 +01:00
$res = $handler->insertOrUpdate($this, $properties, $saveNM);
2022-06-17 20:53:35 +02:00
if ($res === false) {
return false;
} else if ($this->id === null) {
2022-08-20 22:17:17 +02:00
$this->id = $res;
2022-11-20 17:13:53 +01:00
$handler->insertNM($this);
2022-08-20 22:17:17 +02:00
}
return true;
}
public function insert(SQL $sql): bool {
$handler = self::getHandler($sql);
$res = $handler->insert($this);
if ($res === false) {
return false;
} else if ($this->id === null) {
2022-06-17 20:53:35 +02:00
$this->id = $res;
}
return true;
}
public function delete(SQL $sql): bool {
$handler = self::getHandler($sql);
2022-06-17 20:53:35 +02:00
if ($this->id === null) {
$handler->getLogger()->error("Cannot delete entity without id");
2022-06-17 20:53:35 +02:00
return false;
}
2022-06-17 23:29:24 +02:00
if ($handler->delete($this->id)) {
$this->postDelete();
2022-06-17 23:29:24 +02:00
$this->id = null;
return true;
}
return false;
2022-06-17 20:53:35 +02:00
}
public static function getHandler(SQL $sql, $obj_or_class = null, $allowOverride = false): DatabaseEntityHandler {
2022-06-17 20:53:35 +02:00
if (!$obj_or_class) {
$obj_or_class = get_called_class();
}
if (!($obj_or_class instanceof \ReflectionClass)) {
$class = new \ReflectionClass($obj_or_class);
} else {
$class = $obj_or_class;
}
if (!$allowOverride) {
// if we are in an extending context, get the database handler for the root entity,
// as we do not persist attributes of the inheriting class
while ($class->getParentClass()->getName() !== DatabaseEntity::class) {
$class = $class->getParentClass();
}
2022-11-27 12:33:27 +01:00
}
2022-06-17 20:53:35 +02:00
$handler = self::$handlers[$class->getShortName()] ?? null;
if (!$handler || $allowOverride) {
$handler = new DatabaseEntityHandler($sql, $class);
2022-06-17 20:53:35 +02:00
self::$handlers[$class->getShortName()] = $handler;
2023-01-10 22:12:05 +01:00
$handler->init();
2022-06-17 20:53:35 +02:00
}
return $handler;
}
public function getId(): ?int {
return $this->id;
}
2022-11-20 17:13:53 +01:00
2023-01-09 14:21:11 +01:00
public static function count(SQL $sql, ?Condition $condition = null, ?array $joins = []): int|bool {
2022-11-20 17:13:53 +01:00
$handler = self::getHandler($sql);
$query = $sql->select(new Count())
2022-11-20 17:13:53 +01:00
->from($handler->getTableName());
if ($condition) {
$query->where($condition);
}
2023-01-09 14:21:11 +01:00
if ($joins) {
foreach ($joins as $join) {
$query->addJoin($join);
}
}
2022-11-20 17:13:53 +01:00
$res = $query->execute();
if (!empty($res)) {
return $res[0]["count"];
}
return false;
}
2022-06-17 20:53:35 +02:00
}