connection = $connection; $this->table = $table; $this->auditLogger = $auditLogger; } public function setIdName(string $idName): Modify { $this->idName = $idName; return $this; } public function setExternalId($id): Modify { $this->externalId = $id; return $this; } public function setAutoIncrement(bool $autoIncrement = true): Modify { $this->autoIncrement = $autoIncrement; return $this; } public function fill(array $attributes): Modify { $this->attributes = array_merge($this->attributes, $attributes); return $this; } public function set(string $name, $value): Modify { $this->attributes[$name] = $value; return $this; } public function setId($id): Modify { $this->attributes[$this->idName] = $id; return $this; } public function setDiff(array $diff): Modify { $this->diff = $diff; return $this; } public function getId() { return $this->attributes[$this->idName]; } public function save(): void { if (isset($this->attributes[$this->idName])) { $this->update(); } else { $this->insert(); } } public function delete(): void { if (!isset($this->attributes[$this->idName])) { throw new \Exception('No primary key specified!'); } $query = 'DELETE FROM ' . Utils::backtick($this->table) . ' WHERE ' . Utils::backtick($this->idName) . '=?'; $stmt = $this->connection->prepare($query); $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 { if ($this->externalId !== null) { $this->attributes[$this->idName] = $this->externalId; } elseif (!$this->autoIncrement) { $this->attributes[$this->idName] = $this->generateKey(); } $set = Utils::generateColumnsWithBinding(array_keys($this->attributes)); $query = 'INSERT INTO ' . Utils::backtick($this->table) . ' SET ' . $set; $stmt = $this->connection->prepare($query); $stmt->execute($this->attributes); if ($this->autoIncrement) { $this->attributes[$this->idName] = $this->connection->lastId(); } if ($this->auditLogger !== null) { $this->auditLogger->logInsert($this->table, $this->attributes[$this->idName]); } } private function update(): void { if ($this->auditLogger !== null) { $this->generateDiff(); if (count($this->diff) === 0) { return; } } $attributes = $this->attributes; unset($attributes[$this->idName]); $set = Utils::generateColumnsWithBinding(array_keys($attributes)); $query = 'UPDATE ' . Utils::backtick($this->table) . ' SET ' . $set . ' WHERE ' . Utils::backtick($this->idName) . '=?'; $stmt = $this->connection->prepare($query); $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); } } private function generateKey(): string { 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); } }