2020-08-17 23:46:58 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Basic object to load a mysql table inside an object.
|
|
|
|
*/
|
|
|
|
abstract class Table
|
|
|
|
{
|
|
|
|
public const TYPE_STRING = 1;
|
|
|
|
public const TYPE_INTEGER = 2;
|
|
|
|
public const TYPE_FLOAT = 3;
|
|
|
|
public const TYPE_DATETIME = 4;
|
|
|
|
public const TYPE_BOOL = 5;
|
|
|
|
|
|
|
|
protected const VALUE = 'value';
|
|
|
|
protected const TYPE = 'type';
|
|
|
|
|
|
|
|
protected ?DatabaseInterface $database;
|
|
|
|
protected string $tableName;
|
|
|
|
protected array $fields;
|
|
|
|
protected string $primaryKey;
|
|
|
|
protected bool $isPrimKeyManual = false;
|
|
|
|
|
|
|
|
public function __construct(string $tableName, $id, ?DatabaseInterface & $database)
|
|
|
|
{
|
|
|
|
$this->tableName = $tableName;
|
|
|
|
$this->fields = [];
|
|
|
|
|
|
|
|
$this->database = $database;
|
|
|
|
|
|
|
|
$this->database->Query(sprintf('DESCRIBE %s', $tableName));
|
|
|
|
|
|
|
|
$result = $this->database->getResult();
|
|
|
|
|
|
|
|
foreach ($result as $field) {
|
|
|
|
$sqlType = substr_count(
|
|
|
|
$field['Type'], '(') === 0 ? $field['Type'] : strstr($field['Type'],
|
|
|
|
'(',
|
|
|
|
true
|
|
|
|
);
|
|
|
|
|
|
|
|
switch ($sqlType) {
|
|
|
|
case 'varchar':
|
|
|
|
case 'char':
|
|
|
|
case 'text':
|
|
|
|
case 'longtext':
|
|
|
|
case 'mediumtext':
|
|
|
|
case 'tinytext':
|
|
|
|
$type = self::TYPE_STRING;
|
|
|
|
break;
|
|
|
|
case 'int':
|
|
|
|
case 'smallint':
|
|
|
|
case 'mediumint':
|
|
|
|
case 'bigint':
|
|
|
|
$type = self::TYPE_INTEGER;
|
|
|
|
break;
|
|
|
|
case 'float':
|
|
|
|
case 'decimal':
|
|
|
|
case 'double':
|
|
|
|
case 'real':
|
|
|
|
$type = self::TYPE_FLOAT;
|
|
|
|
break;
|
|
|
|
case 'datetime':
|
|
|
|
case 'date':
|
|
|
|
$type = self::TYPE_DATETIME;
|
|
|
|
break;
|
|
|
|
case 'tinyint':
|
|
|
|
$type = self::TYPE_BOOL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Exception(sprintf('Type %s of field %s couldn\'t be handled', $sqlType, $field['Field']));
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->addField($field['Field'], $type);
|
|
|
|
|
|
|
|
if ($field['Key'] === 'PRI') {
|
|
|
|
$this->primaryKey = $field['Field'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$this->isPrimKeyManual && $id !== null) {
|
|
|
|
$this->loadById($id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getPrimaryKey()
|
|
|
|
{
|
|
|
|
if ($this->primaryKey === null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->getField($this->primaryKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function addField(string $name, int $type): void
|
|
|
|
{
|
|
|
|
if (!self::IsValidType($type)) {
|
|
|
|
throw new Exception(
|
|
|
|
sprintf('Field %s has invalid type of %s!', $name, $type)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->fields[$name] = [self::VALUE => null, self::TYPE => $type];
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function loadById($id): void
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
$this->database->Query(
|
|
|
|
sprintf('SELECT * FROM %s WHERE %s = :id', $this->tableName, $this->primaryKey),
|
|
|
|
['id' => $id]
|
|
|
|
);
|
|
|
|
} catch (Throwable $e) {
|
|
|
|
throw new Exception();
|
|
|
|
}
|
|
|
|
|
|
|
|
$result = $this->database->getResult();
|
|
|
|
|
|
|
|
if (count($result) === 0) {
|
2020-08-20 15:45:59 +02:00
|
|
|
throw new Exception(sprintf('No %s with id %d found!', $this->tableName, $id));
|
2020-08-17 23:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($result[0] as $field => $value) {
|
|
|
|
$this->setField($field, $value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function Flush(): void
|
|
|
|
{
|
|
|
|
$this->database->Delete($this->tableName, []);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function Delete(): void
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
$this->database->Delete($this->tableName, [$this->primaryKey => $this->getPrimaryKey()]);
|
|
|
|
} catch (Throwable $e) {
|
|
|
|
throw new Exception();
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->GetAllFieldNames() as $field) {
|
|
|
|
$this->fields[$field][self::VALUE] = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getField(string $name)
|
|
|
|
{
|
|
|
|
if (!array_key_exists($name, $this->fields)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->fields[$name][self::VALUE];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the value for the given field inside the database.
|
|
|
|
*/
|
|
|
|
protected function setField(string $name, $value): void
|
|
|
|
{
|
|
|
|
if (!$this->HasField($name)) {
|
|
|
|
throw new Exception(sprintf('Field %s doesn\'t exist!', $name));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($value === null) {
|
|
|
|
$this->fields[$name][self::VALUE] = null;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ($this->fields[$name][self::TYPE]) {
|
|
|
|
case self::TYPE_STRING:
|
|
|
|
$this->fields[$name][self::VALUE] = (string)$value;
|
|
|
|
return;
|
|
|
|
case self::TYPE_INTEGER:
|
|
|
|
$this->fields[$name][self::VALUE] = (int)$value;
|
|
|
|
return;
|
|
|
|
case self::TYPE_FLOAT:
|
|
|
|
$this->fields[$name][self::VALUE] = (float)$value;
|
|
|
|
return;
|
|
|
|
case self::TYPE_DATETIME:
|
|
|
|
try {
|
|
|
|
$this->fields[$name][self::VALUE] = new DateTime((string)$value);
|
|
|
|
} catch (Exception $e) {
|
|
|
|
throw new Exception();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case self::TYPE_BOOL:
|
|
|
|
$this->fields[$name][self::VALUE] = (bool)$value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if the table has the given column.
|
|
|
|
*/
|
|
|
|
public function HasField(string $name): bool
|
|
|
|
{
|
|
|
|
return array_key_exists($name, $this->fields);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Saves the whole object into the database.
|
|
|
|
*/
|
|
|
|
public function Save(): void
|
|
|
|
{
|
|
|
|
$fields = [];
|
|
|
|
|
|
|
|
foreach ($this->GetAllFieldNames() as $fieldName) {
|
|
|
|
$field = $this->getField($fieldName);
|
|
|
|
|
|
|
|
if ($field instanceof DateTime) {
|
|
|
|
$fields[$fieldName] = $field->format('Y-m-d H:i:s');
|
|
|
|
} else if (is_bool($field)) {
|
|
|
|
$fields[$fieldName] = (int)$field;
|
|
|
|
} else {
|
|
|
|
$fields[$fieldName] = $field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->isPrimKeyManual) {
|
|
|
|
$this->saveWithManualId($fields);
|
|
|
|
} else {
|
|
|
|
$this->saveWithPrimaryKey($fields);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return string[]
|
|
|
|
*/
|
|
|
|
public function GetAllFieldNames(): array
|
|
|
|
{
|
|
|
|
$fieldNames = [];
|
|
|
|
|
|
|
|
foreach ($this->fields as $name => $field) {
|
|
|
|
$fieldNames[] = $name;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $fieldNames;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-08-19 21:06:45 +02:00
|
|
|
* Checks if the index is a valid backend type.
|
2020-08-17 23:46:58 +02:00
|
|
|
*/
|
|
|
|
public static function IsValidType(int $type): bool
|
|
|
|
{
|
|
|
|
$validTypes = [
|
|
|
|
self::TYPE_STRING,
|
|
|
|
self::TYPE_INTEGER,
|
|
|
|
self::TYPE_FLOAT,
|
|
|
|
self::TYPE_DATETIME,
|
|
|
|
self::TYPE_BOOL,
|
|
|
|
];
|
|
|
|
|
|
|
|
return in_array($type, $validTypes);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function saveWithManualId(array $fields): void
|
|
|
|
{
|
|
|
|
if ($this->getField($this->primaryKey) === null) {
|
|
|
|
throw new Exception('Manual primary key must not be null!');
|
|
|
|
}
|
|
|
|
|
|
|
|
$hasKey = (bool)$this->database->Count(
|
|
|
|
$this->tableName,
|
|
|
|
[$this->primaryKey => $this->getField($this->primaryKey)]
|
|
|
|
);
|
|
|
|
|
|
|
|
if ($hasKey) {
|
|
|
|
$this->database->Update(
|
|
|
|
$this->tableName, $fields, [$this->primaryKey => $this->getField($this->primaryKey)]
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$this->database->Insert($this->tableName, $fields);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function saveWithPrimaryKey(array $fields): void
|
|
|
|
{
|
|
|
|
if ($this->getField($this->primaryKey) !== null) {
|
|
|
|
$this->database->Update(
|
|
|
|
$this->tableName, $fields, [$this->primaryKey => $this->getField($this->primaryKey)]
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$this->setField($this->primaryKey, $this->database->Insert($this->tableName, $fields));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|