setConnection($connection) ->setLogger($logger); } protected PDO $connection; protected string $table; protected LoggerInterface $logger; public function getConnection(): PDO { return $this->connection; } public function getTable(): string { return $this->table; } public function getLogger(): LoggerInterface { return $this->logger; } public function setConnection(PDO $pdo): Repository { $this->connection = $pdo; return $this; } public function setTable(string $table): Repository { $this->table = $table; return $this; } public function setLogger(LoggerInterface $logger): Repository { $this->logger = $logger; return $this; } abstract protected function fieldsForUpdate(): array; abstract protected function valuesForUpdate(ModelInterface $model): array; protected function idProperty(): string { return 'getId'; } protected function idField(): string { return 'id'; } public function update(ModelInterface $model, ModelInterface $old): void { $query = "UPDATE `{$this->getTable()}` SET "; $model_values = $this->valuesForUpdate($model); $old_values = $this->valuesForUpdate($old); $columns = []; $values = []; foreach ($this->fieldsForUpdate() as $i => $column) { if (isset($model_values[$i]) and $old_values[$i] !== $model_values[$i]) { $columns []= "`{$column}` = ?"; $values []= $model_values[$i]; } } if (count($columns) === 0) { return; } $query .= implode(', ', $columns) . " WHERE {$this->idField()} = ?"; $values []= $old->{$this->idProperty()}(); $st = $this->getConnection()->prepare($query); $st->execute($values); } abstract protected function fieldsForInsert(): array; abstract protected function valuesForInsert(ModelInterface $model): array; protected function insert(ModelInterface $model): void { $fields = $this->fieldsForInsert(); $fields_string = implode(', ', array_map(function($field) { return "`{$field}`"; }, $fields)); $fields_questions = implode(', ', array_fill(0, count($fields), '?')); $query = "INSERT INTO `{$this->getTable()}` ({$fields_string}) VALUES ({$fields_questions})"; $values = $this->valuesForInsert($model); $st = $this->getConnection()->prepare($query); $st->execute($values); } abstract protected function defaultFind(ModelInterface $model): ModelInterface; public function save(ModelInterface &$model): void { try { $old = $this->defaultFind($model); $this->update($model, $old); } catch (BlankResult $e) { $this->insert($model); $model->setId($this->getConnection()->lastInsertId()); } catch(PDOException $e) { $this->getLogger()->error($e); throw $e; } } abstract public function load(array $row): ModelInterface; abstract protected function fieldsForCreate(): array; abstract protected function valuesForCreate(array $data): array; abstract protected function defaultSearch(array $data): ModelInterface; public function create(array $data): ModelInterface { try { return $this->defaultSearch($data); } catch (PDOException | BlankResult $e) { $data[$this->idField()] = 0; return $this->load($data); } } protected function getId(ModelInterface $model): int { return $model->getId(); } public function resetIndex(): void { $query = "ALTER TABLE `{$this->getTable()}` AUTO_INCREMENT = 1"; $this->getConnection()->query($query); } public function optimize(): void { $query = "OPTIMIZE TABLE `{$this->getTable()}`"; $this->getConnection()->query($query); } public function delete(ModelInterface $model): void { $query = "DELETE FROM `{$this->getTable()}` WHERE `{$this->idField()}` = ?"; $st = $this->getConnection()->prepare($query); $st->execute([$this->getId($model)]); $this->resetIndex(); $this->optimize(); } protected function fetchOne(string $query, ?array $values = null): ModelInterface { if ($values !== null) { $st = $this->getConnection()->prepare($query); $st->execute($values); } else { $st = $this->getConnection()->query($query); } $row = $st->fetch(PDO::FETCH_ASSOC); if (!$row) { throw new BlankResult(); } return $this->load($row); } protected function fetchMany(string $query, ?array $values = null): array { if ($values !== null) { $st = $this->getConnection()->prepare($query); $st->execute($values); } else { $st = $this->getConnection()->query($query); } $rows = $st->fetchAll(PDO::FETCH_ASSOC); if (!$rows) { throw new BlankResult(); } return array_map([$this, 'load'], $rows); } public function fetchAll(): array { $query = "SELECT * FROM `{$this->getTable()}`"; return $this->fetchMany($query); } public function fetchById(int $id): ModelInterface { $query = "SELECT * FROM `{$this->getTable()}` WHERE `{$this->idField()}` = ?"; return $this->fetchOne($query, [$id]); } }