Compare commits
No commits in common. "3893ed22316e84aa4ffab7fbb3d5e823b928b001" and "aa31c857c7ad56690ebbcebfc81f145a93187a0e" have entirely different histories.
3893ed2231
...
aa31c857c7
@ -7,7 +7,6 @@ const ROOT = __DIR__;
|
|||||||
class Container
|
class Container
|
||||||
{
|
{
|
||||||
static SokoWeb\Interfaces\Database\IConnection $dbConnection;
|
static SokoWeb\Interfaces\Database\IConnection $dbConnection;
|
||||||
static SokoWeb\Interfaces\Database\IAuditLogger $auditLogger;
|
|
||||||
static SokoWeb\Routing\RouteCollection $routeCollection;
|
static SokoWeb\Routing\RouteCollection $routeCollection;
|
||||||
static SokoWeb\Interfaces\Session\ISessionHandler $sessionHandler;
|
static SokoWeb\Interfaces\Session\ISessionHandler $sessionHandler;
|
||||||
static SokoWeb\Interfaces\Request\IRequest $request;
|
static SokoWeb\Interfaces\Request\IRequest $request;
|
||||||
|
@ -1,75 +0,0 @@
|
|||||||
<?php namespace SokoWeb\Database;
|
|
||||||
|
|
||||||
use SokoWeb\Interfaces\Database\IAuditLogger;
|
|
||||||
use SokoWeb\Interfaces\Database\IConnection;
|
|
||||||
|
|
||||||
abstract class AuditLoggerBase implements IAuditLogger
|
|
||||||
{
|
|
||||||
const LOG_TYPE_INSERT = 'insert';
|
|
||||||
const LOG_TYPE_UPDATE = 'update';
|
|
||||||
const LOG_TYPE_DELETE = 'delete';
|
|
||||||
|
|
||||||
private IConnection $connection;
|
|
||||||
|
|
||||||
private string $logTable;
|
|
||||||
|
|
||||||
public function __construct(IConnection $connection, string $logTable)
|
|
||||||
{
|
|
||||||
$this->connection = $connection;
|
|
||||||
$this->logTable = $logTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function logInsert(string $localTable, $localId): void
|
|
||||||
{
|
|
||||||
$data = [
|
|
||||||
'local_table' => $localTable,
|
|
||||||
'local_id' => $localId,
|
|
||||||
'type' => static::LOG_TYPE_INSERT,
|
|
||||||
'modifier_id' => $this->getModifierId(),
|
|
||||||
];
|
|
||||||
|
|
||||||
$query = 'INSERT INTO ' . Utils::backtick($this->logTable) . ' SET ' . Utils::generateColumnsWithBinding(array_keys($data));
|
|
||||||
$stmt = $this->connection->prepare($query);
|
|
||||||
$stmt->execute($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function logUpdate(string $localTable, $localId, array $diff): void
|
|
||||||
{
|
|
||||||
$data = [
|
|
||||||
'local_table' => $localTable,
|
|
||||||
'local_id' => $localId,
|
|
||||||
'type' => static::LOG_TYPE_UPDATE,
|
|
||||||
'modifier_id' => $this->getModifierId(),
|
|
||||||
'column' => null,
|
|
||||||
'old' => null,
|
|
||||||
'new' => null,
|
|
||||||
];
|
|
||||||
|
|
||||||
$query = 'INSERT INTO ' . Utils::backtick($this->logTable) . ' SET ' . Utils::generateColumnsWithBinding(array_keys($data));
|
|
||||||
$stmt = $this->connection->prepare($query);
|
|
||||||
|
|
||||||
foreach ($diff as $name => $values) {
|
|
||||||
$data['column'] = $name;
|
|
||||||
$data['old'] = $values['old'];
|
|
||||||
$data['new'] = $values['new'];
|
|
||||||
$stmt->execute($data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function logDelete(string $localTable, $localId, array $attributes): void
|
|
||||||
{
|
|
||||||
$data = [
|
|
||||||
'local_table' => $localTable,
|
|
||||||
'local_id' => $localId,
|
|
||||||
'type' => static::LOG_TYPE_DELETE,
|
|
||||||
'modifier_id' => $this->getModifierId(),
|
|
||||||
'old' => $attributes,
|
|
||||||
];
|
|
||||||
|
|
||||||
$query = 'INSERT INTO ' . Utils::backtick($this->logTable) . ' SET ' . Utils::generateColumnsWithBinding(array_keys($data));
|
|
||||||
$stmt = $this->connection->prepare($query);
|
|
||||||
$stmt->execute($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract protected function getModifierId();
|
|
||||||
}
|
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
use SokoWeb\Interfaces\Database\IConnection;
|
use SokoWeb\Interfaces\Database\IConnection;
|
||||||
use SokoWeb\Database\Utils;
|
use SokoWeb\Database\Utils;
|
||||||
use SokoWeb\Interfaces\Database\IAuditLogger;
|
|
||||||
use SokoWeb\Interfaces\Database\IResultSet;
|
|
||||||
|
|
||||||
class Modify
|
class Modify
|
||||||
{
|
{
|
||||||
@ -11,8 +9,6 @@ class Modify
|
|||||||
|
|
||||||
private string $table;
|
private string $table;
|
||||||
|
|
||||||
private ?IAuditLogger $auditLogger;
|
|
||||||
|
|
||||||
private string $idName = 'id';
|
private string $idName = 'id';
|
||||||
|
|
||||||
private array $attributes = [];
|
private array $attributes = [];
|
||||||
@ -21,13 +17,10 @@ class Modify
|
|||||||
|
|
||||||
private bool $autoIncrement = true;
|
private bool $autoIncrement = true;
|
||||||
|
|
||||||
private ?array $diff = null;
|
public function __construct(IConnection $connection, string $table)
|
||||||
|
|
||||||
public function __construct(IConnection $connection, string $table, ?IAuditLogger $auditLogger = null)
|
|
||||||
{
|
{
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
$this->table = $table;
|
$this->table = $table;
|
||||||
$this->auditLogger = $auditLogger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setIdName(string $idName): Modify
|
public function setIdName(string $idName): Modify
|
||||||
@ -72,13 +65,6 @@ class Modify
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setDiff(array $diff): Modify
|
|
||||||
{
|
|
||||||
$this->diff = $diff;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getId()
|
public function getId()
|
||||||
{
|
{
|
||||||
return $this->attributes[$this->idName];
|
return $this->attributes[$this->idName];
|
||||||
@ -103,10 +89,6 @@ class Modify
|
|||||||
|
|
||||||
$stmt = $this->connection->prepare($query);
|
$stmt = $this->connection->prepare($query);
|
||||||
$stmt->execute([$this->idName => $this->attributes[$this->idName]]);
|
$stmt->execute([$this->idName => $this->attributes[$this->idName]]);
|
||||||
|
|
||||||
if ($this->auditLogger !== null) {
|
|
||||||
$this->auditLogger->logDelete($this->table, $this->attributes[$this->idName], $this->attributes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function insert(): void
|
private function insert(): void
|
||||||
@ -117,7 +99,7 @@ class Modify
|
|||||||
$this->attributes[$this->idName] = $this->generateKey();
|
$this->attributes[$this->idName] = $this->generateKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
$set = Utils::generateColumnsWithBinding(array_keys($this->attributes));
|
$set = $this->generateColumnsWithBinding(array_keys($this->attributes));
|
||||||
|
|
||||||
$query = 'INSERT INTO ' . Utils::backtick($this->table) . ' SET ' . $set;
|
$query = 'INSERT INTO ' . Utils::backtick($this->table) . ' SET ' . $set;
|
||||||
|
|
||||||
@ -127,62 +109,32 @@ class Modify
|
|||||||
if ($this->autoIncrement) {
|
if ($this->autoIncrement) {
|
||||||
$this->attributes[$this->idName] = $this->connection->lastId();
|
$this->attributes[$this->idName] = $this->connection->lastId();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->auditLogger !== null) {
|
|
||||||
$this->auditLogger->logInsert($this->table, $this->attributes[$this->idName]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function update(): void
|
private function update(): void
|
||||||
{
|
{
|
||||||
if ($this->auditLogger !== null) {
|
|
||||||
$this->generateDiff();
|
|
||||||
if (count($this->diff) === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$attributes = $this->attributes;
|
$attributes = $this->attributes;
|
||||||
unset($attributes[$this->idName]);
|
unset($attributes[$this->idName]);
|
||||||
|
|
||||||
$set = Utils::generateColumnsWithBinding(array_keys($attributes));
|
$set = $this->generateColumnsWithBinding(array_keys($attributes));
|
||||||
|
|
||||||
$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($attributes, [$this->idName => $this->attributes[$this->idName]]));
|
$stmt->execute(array_merge($attributes, [$this->idName => $this->attributes[$this->idName]]));
|
||||||
|
|
||||||
if ($this->auditLogger !== null) {
|
|
||||||
$this->auditLogger->logUpdate($this->table, $this->attributes[$this->idName], $this->diff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function generateColumnsWithBinding(array $columns): string
|
||||||
|
{
|
||||||
|
array_walk($columns, function(&$value, $key) {
|
||||||
|
$value = Utils::backtick($value) . '=?';
|
||||||
|
});
|
||||||
|
|
||||||
|
return implode(',', $columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generateKey(): string
|
private function generateKey(): string
|
||||||
{
|
{
|
||||||
return substr(hash('sha256', serialize($this->attributes) . random_bytes(5) . microtime()), 0, 7);
|
return substr(hash('sha256', serialize($this->attributes) . random_bytes(5) . microtime()), 0, 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generateDiff(): void
|
|
||||||
{
|
|
||||||
if (isset($this->diff)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->diff = [];
|
|
||||||
$original = $this->readFromDb(array_keys($this->attributes));
|
|
||||||
foreach ($original as $key => $value) {
|
|
||||||
if ($value !== $this->attributes[$key]) {
|
|
||||||
$this->diff[$key] = ['old' => $value, 'new' => $this->attributes[$key]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function readFromDb($columns): array
|
|
||||||
{
|
|
||||||
return (new Select($this->connection, $this->table))
|
|
||||||
->columns($columns)
|
|
||||||
->where($this->idName, '=', $this->attributes[$this->idName])
|
|
||||||
->execute()
|
|
||||||
->fetch(IResultSet::FETCH_ASSOC);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,4 @@ class Utils {
|
|||||||
{
|
{
|
||||||
return '`' . $name . '`';
|
return '`' . $name . '`';
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function generateColumnsWithBinding(array $columns): string
|
|
||||||
{
|
|
||||||
array_walk($columns, function(&$value, $key) {
|
|
||||||
$value = static::backtick($value) . '=?';
|
|
||||||
});
|
|
||||||
|
|
||||||
return implode(',', $columns);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
<?php namespace SokoWeb\Interfaces\Database;
|
|
||||||
|
|
||||||
interface IAuditLogger
|
|
||||||
{
|
|
||||||
public function logInsert(string $localTable, $localId): void;
|
|
||||||
|
|
||||||
public function logUpdate(string $localTable, $localId, array $diff): void;
|
|
||||||
|
|
||||||
public function logDelete(string $localTable, $localId, array $attributes): void;
|
|
||||||
}
|
|
@ -117,23 +117,19 @@ class PersistentDataManager
|
|||||||
$modified = $model->toArray();
|
$modified = $model->toArray();
|
||||||
$id = $model->getId();
|
$id = $model->getId();
|
||||||
|
|
||||||
$modify = new Modify(\Container::$dbConnection, $model::getTable(), \Container::$auditLogger);
|
$modify = new Modify(\Container::$dbConnection, $model::getTable());
|
||||||
|
|
||||||
if ($id !== null) {
|
if ($id !== null) {
|
||||||
$original = $model->getSnapshot();
|
$original = $model->getSnapshot();
|
||||||
$diff = [];
|
|
||||||
|
|
||||||
foreach ($original as $key => $value) {
|
foreach ($original as $key => $value) {
|
||||||
if ($value === $modified[$key]) {
|
if ($value === $modified[$key]) {
|
||||||
unset($modified[$key]);
|
unset($modified[$key]);
|
||||||
} else {
|
|
||||||
$diff[$key] = ['old' => $value, 'new' => $modified[$key]];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($modified) > 0) {
|
if (count($modified) > 0) {
|
||||||
$modify->setId($id);
|
$modify->setId($id);
|
||||||
$modify->setDiff($diff);
|
|
||||||
$modify->fill($modified);
|
$modify->fill($modified);
|
||||||
$modify->save();
|
$modify->save();
|
||||||
}
|
}
|
||||||
@ -149,9 +145,8 @@ class PersistentDataManager
|
|||||||
|
|
||||||
public function deleteFromDb(Model $model): void
|
public function deleteFromDb(Model $model): void
|
||||||
{
|
{
|
||||||
$modify = new Modify(\Container::$dbConnection, $model::getTable(), \Container::$auditLogger);
|
$modify = new Modify(\Container::$dbConnection, $model::getTable());
|
||||||
$modify->setId($model->getId());
|
$modify->setId($model->getId());
|
||||||
$modify->fill($model->toArray());
|
|
||||||
$modify->delete();
|
$modify->delete();
|
||||||
|
|
||||||
$model->setId(null);
|
$model->setId(null);
|
||||||
|
@ -15,11 +15,9 @@ $dotenv->load();
|
|||||||
class Container
|
class Container
|
||||||
{
|
{
|
||||||
static SokoWeb\Interfaces\Database\IConnection $dbConnection;
|
static SokoWeb\Interfaces\Database\IConnection $dbConnection;
|
||||||
static SokoWeb\Interfaces\Database\IAuditLogger $auditLogger;
|
|
||||||
static SokoWeb\Routing\RouteCollection $routeCollection;
|
static SokoWeb\Routing\RouteCollection $routeCollection;
|
||||||
static SokoWeb\Interfaces\Session\ISessionHandler $sessionHandler;
|
static SokoWeb\Interfaces\Session\ISessionHandler $sessionHandler;
|
||||||
static SokoWeb\Interfaces\Request\IRequest $request;
|
static SokoWeb\Interfaces\Request\IRequest $request;
|
||||||
}
|
}
|
||||||
|
|
||||||
Container::$dbConnection = new SokoWeb\Database\Mysql\Connection($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']);
|
Container::$dbConnection = new SokoWeb\Database\Mysql\Connection($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']);
|
||||||
Container::$auditLogger = new {app}\Database\AuditLogger(Container::$dbConnection, 'audit_log');
|
|
||||||
|
@ -13,16 +13,3 @@ CREATE TABLE `users` (
|
|||||||
UNIQUE KEY `email` (`email`),
|
UNIQUE KEY `email` (`email`),
|
||||||
UNIQUE KEY `google_sub` (`google_sub`)
|
UNIQUE KEY `google_sub` (`google_sub`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
CREATE TABLE `audit_log` (
|
|
||||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
|
||||||
`local_table` varchar(255) NOT NULL,
|
|
||||||
`local_id` int(10) unsigned NOT NULL,
|
|
||||||
`type` enum('insert','update','delete') NOT NULL,
|
|
||||||
`date` timestamp NOT NULL DEFAULT current_timestamp(),
|
|
||||||
`modifier_id` int(10) unsigned NULL,
|
|
||||||
`column` varchar(255) NULL,
|
|
||||||
`old` text NULL,
|
|
||||||
`new` text NULL,
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
<?php namespace {app}\Database;
|
|
||||||
|
|
||||||
use SokoWeb\Database\AuditLoggerBase;
|
|
||||||
|
|
||||||
class AuditLogger extends AuditLoggerBase
|
|
||||||
{
|
|
||||||
protected function getModifierId()
|
|
||||||
{
|
|
||||||
\Container::$request->user()->getUniqueId();
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user