MAPG-140 add PersistentDataManager and refactor of models

This commit is contained in:
Bence Pőcze 2020-06-18 00:19:42 +02:00
parent 1a4d928143
commit d6750777c2
6 changed files with 295 additions and 93 deletions

View File

@ -1,7 +1,6 @@
<?php namespace MapGuesser\Database\Query; <?php namespace MapGuesser\Database\Query;
use MapGuesser\Interfaces\Database\IConnection; use MapGuesser\Interfaces\Database\IConnection;
use MapGuesser\Interfaces\Database\IResultSet;
use MapGuesser\Database\Utils; use MapGuesser\Database\Utils;
class Modify class Modify
@ -14,8 +13,6 @@ class Modify
private array $attributes = []; private array $attributes = [];
private array $original = [];
private ?string $externalId = null; private ?string $externalId = null;
private bool $autoIncrement = true; private bool $autoIncrement = true;
@ -116,48 +113,15 @@ class Modify
private function update(): void private function update(): void
{ {
/*$diff = $this->generateDiff(); $attributes = $this->attributes;
unset($attributes[$this->idName]);
if (count($diff) === 0) { $set = $this->generateColumnsWithBinding(array_keys($attributes));
return;
}*/
$diff = $this->attributes;
unset($diff[$this->idName]);
$set = $this->generateColumnsWithBinding(array_keys($diff));
$query = 'UPDATE ' . Utils::backtick($this->table) . ' SET ' . $set . ' WHERE ' . Utils::backtick($this->idName) . '=?'; $query = 'UPDATE ' . Utils::backtick($this->table) . ' SET ' . $set . ' WHERE ' . Utils::backtick($this->idName) . '=?';
$stmt = $this->connection->prepare($query); $stmt = $this->connection->prepare($query);
$stmt->execute(array_merge($diff, [$this->idName => $this->attributes[$this->idName]])); $stmt->execute(array_merge($attributes, [$this->idName => $this->attributes[$this->idName]]));
}
private function readFromDB(array $columns): void
{
$select = (new Select($this->connection, $this->table))
->setIdName($this->idName)
->whereId($this->attributes[$this->idName])
->columns($columns);
$this->original = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
}
private function generateDiff(): array
{
$this->readFromDB(array_keys($this->attributes));
$diff = [];
foreach ($this->attributes as $name => $value) {
$original = $this->original[$name];
if ($original != $value) {
$diff[$name] = $value;
}
}
return $diff;
} }
public static function generateColumnsWithBinding(array $columns): string public static function generateColumnsWithBinding(array $columns): string

View File

@ -32,11 +32,14 @@ class Select
private array $limit; private array $limit;
public function __construct(IConnection $connection, string $table) public function __construct(IConnection $connection, ?string $table = null)
{ {
$this->connection = $connection; $this->connection = $connection;
if ($table !== null) {
$this->table = $table; $this->table = $table;
} }
}
public function setIdName(string $idName): Select public function setIdName(string $idName): Select
{ {
@ -52,6 +55,13 @@ class Select
return $this; return $this;
} }
public function from(string $table): Select
{
$this->table = $table;
return $this;
}
public function columns(array $columns): Select public function columns(array $columns): Select
{ {
$this->columns = array_merge($this->columns, $columns); $this->columns = array_merge($this->columns, $columns);

View File

@ -1,49 +0,0 @@
<?php namespace MapGuesser\Model;
abstract class BaseModel
{
protected static array $fields;
protected $id = null;
public static function getFields(): array
{
return array_merge(['id'], static::$fields);
}
public function __construct(array $data)
{
foreach ($data as $key => $value) {
$method = 'set' . str_replace('_', '', ucwords($key, '_'));
if (method_exists($this, $method)) {
$this->$method($value);
}
}
}
public function setId($id): void
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
function toArray(): array
{
$array = [];
foreach (self::getFields() as $key) {
$method = 'get' . str_replace('_', '', ucwords($key, '_'));
if (method_exists($this, $method)) {
$array[$key] = $this->$method();
}
}
return $array;
}
}

View File

@ -0,0 +1,69 @@
<?php namespace MapGuesser\PersistentData\Model;
abstract class Model
{
protected static string $table;
protected static array $fields;
protected static array $relations = [];
protected $id = null;
private array $snapshot = [];
public static function getTable(): string
{
return static::$table;
}
public static function getFields(): array
{
return array_merge(['id'], static::$fields);
}
public static function getRelations(): array
{
return static::$relations;
}
public function setId($id): void
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
public function toArray(): array
{
$array = [];
foreach (self::getFields() as $key) {
$method = 'get' . str_replace('_', '', ucwords($key, '_'));
if (method_exists($this, $method)) {
$array[$key] = $this->$method();
}
}
return $array;
}
public function saveSnapshot(): void
{
$this->snapshot = $this->toArray();
}
public function resetSnapshot(): void
{
$this->snapshot = [];
}
public function getSnapshot(): array
{
return $this->snapshot;
}
}

View File

@ -1,11 +1,13 @@
<?php namespace MapGuesser\Model; <?php namespace MapGuesser\PersistentData\Model;
use MapGuesser\Interfaces\Authentication\IUser; use MapGuesser\Interfaces\Authentication\IUser;
class User extends BaseModel implements IUser class User extends Model implements IUser
{ {
private static array $types = ['user', 'admin']; private static array $types = ['user', 'admin'];
protected static string $table = 'users';
protected static array $fields = ['email', 'password', 'type', 'active']; protected static array $fields = ['email', 'password', 'type', 'active'];
private string $email; private string $email;

View File

@ -0,0 +1,206 @@
<?php namespace MapGuesser\PersistentData;
use MapGuesser\Database\Query\Modify;
use MapGuesser\Database\Query\Select;
use MapGuesser\Interfaces\Database\IResultSet;
use MapGuesser\PersistentData\Model\Model;
class PersistentDataManager
{
public function selectFromDb(Select $select, string $type, bool $withRelations = false): ?Model
{
$table = call_user_func([$type, 'getTable']);
$fields = call_user_func([$type, 'getFields']);
$select->from($table);
//TODO: only with some relations?
if ($withRelations) {
$relations = call_user_func([$type, 'getRelations']);
$columns = [];
foreach ($fields as $field) {
$columns[] = [$table, $field];
}
$columns = array_merge($columns, $this->getRelationColumns($relations));
$this->leftJoinRelations($select, $table, $relations);
$select->columns($columns);
} else {
$select->columns($fields);
}
//TODO: return with array?
$data = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
if ($data === null) {
return null;
}
$model = new $type();
$this->fillWithData($data, $model);
return $model;
}
public function selectFromDbById($id, string $type, bool $withRelations = false): ?Model
{
$select = new Select(\Container::$dbConnection);
$select->whereId($id);
return $this->selectFromDb($select, $type, $withRelations);
}
public function fillWithData(array $data, Model $model): void
{
$relations = $model::getRelations();
$relationData = [];
foreach ($data as $key => $value) {
if ($this->extractRelationData($key, $value, $relationData, $relations)) {
continue;
}
$method = 'set' . str_replace('_', '', ucwords($key, '_'));
if (method_exists($model, $method)) {
$model->$method($value);
}
}
$this->setRelations($model, $relationData);
$model->saveSnapshot();
}
public function loadRelationsFromDb(Model $model, bool $recursive): void
{
foreach ($model::getRelations() as $relation => $relationType) {
$camel = str_replace('_', '', ucwords($relation, '_'));
$methodGet = 'get' . $camel . 'Id';
$methodSet = 'set' . $camel;
$relationId = $model->$methodGet();
if ($relationId !== null) {
$relationModel = $this->selectFromDbById($relationId, $relationType, $recursive);
$model->$methodSet($relationModel);
}
}
}
public function saveToDb(Model $model): void
{
$this->syncRelations($model);
$modified = $model->toArray();
$id = $model->getId();
$modify = new Modify(\Container::$dbConnection, $model::getTable());
if ($id !== null) {
$original = $model->getSnapshot();
foreach ($original as $key => $value) {
if ($value === $modified[$key]) {
unset($modified[$key]);
}
}
if (count($modified) > 0) {
$modify->setId($id);
$modify->fill($modified);
$modify->save();
}
} else {
$modify->fill($modified);
$modify->save();
$model->setId($modify->getId());
}
$model->saveSnapshot();
}
public function deleteFromDb(Model $model): void
{
$modify = new Modify(\Container::$dbConnection, $model::getTable());
$modify->setId($model->getId());
$modify->delete();
$model->setId(null);
$model->resetSnapshot();
}
private function getRelationColumns(array $relations): array
{
$columns = [];
foreach ($relations as $relation => $relationType) {
$relationTable = call_user_func([$relationType, 'getTable']);
foreach (call_user_func([$relationType, 'getFields']) as $relationField) {
$columns[] = [$relationTable, $relationField, $relation . '__' . $relationField];
}
}
return $columns;
}
private function leftJoinRelations(Select $select, string $table, array $relations): void
{
foreach ($relations as $relation => $relationType) {
$relationTable = call_user_func([$relationType, 'getTable']);
$select->leftJoin($relationTable, [$relationTable, 'id'], '=', [$table, $relation . '_id']);
}
}
private function extractRelationData(string $key, $value, array &$relationData, array $relations): bool
{
$found = false;
foreach ($relations as $relation => $relationType) {
if (substr($key, 0, strlen($relation . '__')) === $relation . '__') {
$found = true;
$relationData[$relation][substr($key, strlen($relation . '__'))] = $value;
break;
}
}
return $found;
}
private function setRelations(Model $model, array &$relations): void
{
foreach ($model::getRelations() as $relation => $relationType) {
if (isset($relations[$relation])) {
$object = new $relationType();
$this->fillWithData($relations[$relation], $object);
$method = 'set' . str_replace('_', '', ucwords($relation, '_'));
$model->$method($object);
}
}
}
private function syncRelations(Model $model): void
{
foreach ($model::getRelations() as $relation => $relationType) {
$camel = str_replace('_', '', ucwords($relation, '_'));
$methodGet = 'get' . $camel;
$methodSet = 'set' . $camel . 'Id';
$relationModel = $model->$methodGet();
if ($relationModel !== null) {
$model->$methodSet($relationModel->getId());
}
}
}
}