diff --git a/src/Alias/Model/Repository.php b/src/Alias/Model/Repository.php index 412ff37..ae24b4a 100644 --- a/src/Alias/Model/Repository.php +++ b/src/Alias/Model/Repository.php @@ -1,11 +1,13 @@ table; } - public function getColumns(): array - { - return $this->columns ?? array_merge($this->getRequiredColumns(), $this->getOptionalColumns()); - } - public function getRequiredColumns(): array - { - if (isset($this->optional_columns) and !isset($this->required_columns)) { - return array_diff($this->getColumns(), $this->getOptionalColumns()); - } - return $this->required_columns ?? $this->getColumns(); - } - public function getOptionalColumns(): array - { - if (isset($this->required_columns) and !isset($this->optional_columns)) { - return array_diff($this->getColumns(), $this->getRequiredColumns()); - } - return $this->optional_columns ?? []; - } - public function getProperties(): array - { - return $this->properties ?? $this->getColumns(); - } public function setConnection(Connection $connection): Repository { @@ -95,229 +71,113 @@ abstract class Repository implements RepositoryInterface $this->table = $table; return $this; } - public function setColumns(array $columns): RepositoryInterface - { - foreach ($columns as $column) { - $this->addColumn($column); - } - return $this; - } - public function setRequiredColumns(array $columns): RepositoryInterface - { - foreach ($columns as $item) { - $this->addRequiredColumn($item); - } - return $this; - } - public function setOptionalColumns(array $columns): RepositoryInterface - { - foreach ($columns as $item) { - $this->addOptionalColumn($item); - } - return $this; - } - public function setProperties(array $properties): RepositoryInterface - { - foreach ($properties as $property) { - $this->addProperty($property); - } - return $this; - } - public function addColumn(string $column): RepositoryInterface - { - $this->columns []= $column; - return $this; - } - public function addRequiredColumn(string $column): RepositoryInterface - { - $this->required_columns []= $column; - return $this; - } - public function addOptionalColumn(string $column): RepositoryInterface - { - $this->optional_columns []= $column; - return $this; - } - public function addProperty(string $property): RepositoryInterface - { - $this->properties []= $property; - return $this; - } - - public function getNewModel(): Model - { - $class = $this->getModel(); - return (new $class()) - ->setRepository($this); - } - public function getMethod(string $property, bool $get = true): string - { - $m = str_replace(' ', '', ucwords(str_replace('_', ' ', $property))); - if ($get) { - return "get{$m}"; - } - return "set{$m}"; - } - public function getProperty(string $method): string - { - $parts = preg_split('/(?=[A-Z])/', $method); - if (in_array(strtolower($parts[0]), ['get', 'set'])) { - array_shift($parts); - } - return strtolower(implode('_', $parts)); - } - - protected function compareProp(string $property, ?array $data = null): bool - { - if ($data === null) { - return in_array($property, $this->getRequiredColumns()); - } - return in_array($property, $this->getOptionalColumns()) and key_exists($property, $data); - } - protected function compareProperty(string $property, ?array $data = null, ?string &$tmp_property = null): bool - { - $tmp_property = $property; - if ($this->compareProp($tmp_property, $data)) { - return true; - } - $tmp_property = "{$property}_id"; - return $this->compareProp($tmp_property, $data); - } - protected function validateDefaultProperty(mixed $value): bool - { - $defaults = [ - 0, - '', - null - ]; - return in_array($value, $defaults); - } - protected function editModel(Model $model, string $getter, string $setter, mixed $value, bool $optional = false): Model + public function save(Model &$model): void { try { - $val = $model->{$getter}(); - if ($optional and $this->validateDefaultProperty($val)) { - $model->{$setter}($value); - } - } catch (\Error|\Exception $e) { - $model->{$setter}($value); + $old = $this->defaultFind($model); + $this->update($model, $old); + } catch (BlankResult $e) { + $this->insert($model); + $model->setId($this->getConnection()->getPDO()->lastInsertId()); } - return $model; } - public function mapModel(Model $model, array $data): Model + public function update(Model $model, Model $old): void { - /** - * Default Model map - * It is assumed that the Model properties are named like the table columns - */ - foreach ($this->getProperties() as $property) { - $getter = $this->getMethod($property); - $setter = $this->getMethod($property, false); - if ($this->compareProperty($property, tmp_property: $tmp)) { - $model = $this->editModel($model, $getter, $setter, $data[$tmp]); - continue; - } - if (!method_exists($model, $setter)) { - continue; - } - if ($this->compareProperty($property, $data, $tmp)) { - $model = $this->editModel($model, $getter, $setter, $data[$tmp], true); - } - try { - $value = $model->{$getter}(); - if ($this->validateDefaultProperty($value)) { - error_log("Notice: {$property} has default value in " . get_called_class()); - } - } catch (\Error | \Exception $e) { - error_log("Warning: Missing {$property} in data for " . get_called_class() . "::fillData"); - } - } - return $model; - } - public function mapTable(Model $model, array $data): array - { - /** - * Default table map - * It is assumed that the table columns are named the same as the Model properties - */ - foreach ($this->getRequiredColumns() as $column) { - $m = $this->getMethod($column); - if (!method_exists($model, $m)) { - error_log("Warning: Missing getter for {$column} in " . get_called_class() . "::mapArray"); - continue; - } - if (!isset($data[$column])) { - $data[$column] = $model->{$m}(); - } - } - foreach ($this->getOptionalColumns() as $column) { - $m = $this->getMethod($column); - if (!method_exists($model, $m)) { - continue; - } - if (!isset($data[$column])) { - $data[$column] = $model->{$m}(); - } - } - return $data; - } + $model_values = $this->valuesForUpdate($model); + $old_values = $this->valuesForUpdate($old); - public function load(array $data): Model - { - return $this->mapModel($this->getNewModel() - ->setId($data['id']), $data); - } - public function save(Model $model): void - { - if (!$model->isDirty() and !$model->isNew()) { + $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; } - if (!$model->isNew()) { - $this->update($model); - return; - } - $values = array_replace(array_flip($this->getColumns()), $this->mapTable($model, [])); - $cols = array_fill(0, count($values), '?'); - $query = $this->getQueryBuilder()->insert($this->getTable())->columns($this->getColumns())->values($cols); - $this->getConnection()->execute($query, array_values($values)); - } - public function update(Model $model): void - { - if (!$model->isDirty() and !$model->isNew()) { - return; - } - $values = $this->mapTable($model, []); - $cols = array_map(function($column) { - return "{$column} = ?"; - }, $values); + $values = array_values($values); - $values []= $model->getId(); - $query = $this->getQueryBuilder()->update($this->getTable())->set($cols)->where(['id = ?']); + $values []= $old->{$this->idProperty()}(); + $query = $this->getQueryBuilder()->update($this->getTable())->set($columns)->where(["{$this->idField()}} = ?"]); $this->getConnection()->execute($query, $values); } public function create(array $data): Model { - return $this->mapModel($this->getNewModel() - ->setNew(), $data); - } - public function edit(Model $model, array $data): Model - { - foreach ($this->getColumns() as $col) { - if (isset($data[$col])) { - $m = $this->getMethod($col, false); - if (!method_exists($model, $m)) { - continue; - } - $model->{$m}($data[$col]); - } + try { + return $this->defaultSearch($data); + } catch (PDOException | BlankResult $e) { + $data[$this->idField()] = 0; + return $this->load($data); } - return $model; } public function delete(Model $model): void { $query = $this->getQueryBuilder()->delete($this->getTable())->where(['id = ?']); $this->getConnection()->execute($query, [$model->getId()]); + $this->resetIndex(); + $this->optimize(); + } + + protected function idProperty(): string + { + return 'getId'; + } + protected function idField(): string + { + return 'id'; + } + protected function insert(Model $model): void + { + $fields = $this->fieldsForInsert(); + $fields_string = array_map(function($field) { + return "`{$field}`"; + }, $fields); + $fields_questions = array_fill(0, count($fields), '?'); + $query = $this->getQueryBuilder()->insert($this->getTable())->columns($fields_string)->values($fields_questions); + $values = $this->valuesForInsert($model); + $this->getConnection()->execute($query, $values); + } + protected function resetIndex(): void + { + $query = "ALTER TABLE `{$this->getTable()}` AUTO_INCREMENT = 1"; + $this->getConnection()->query($query); + } + protected function optimize(): void + { + $query = "OPTIMIZE TABLE `{$this->getTable()}`"; + $this->getConnection()->query($query); + } + + protected function fetchOne(string $query, ?array $values = null): Model + { + if ($values !== null) { + $rs = $this->getConnection()->prepare($query); + $rs->execute($values); + } else { + $rs = $this->getConnection()->query($query); + } + $row = $rs->getFirstAsArray(); + if (!$row) { + throw new BlankResult(); + } + return $this->load($row); + } + protected function fetchMany(string $query, ?array $values = null): array + { + if ($values !== null) { + $rs = $this->getConnection()->prepare($query); + $rs->execute($values); + } else { + $rs = $this->getConnection()->query($query); + } + $rows = $rs->getAsArray(); + if (!$rows) { + throw new BlankResult(); + } + return array_map([$this, 'load'], $rows); } public function fetchById(int $id): Model @@ -327,13 +187,20 @@ abstract class Repository implements RepositoryInterface ->from($this->getTable()) ->where(['id = ?']) ->limit(1); - return $this->load($this->getConnection()->execute($query, [$id])->getFirstAsArray()); + return $this->fetchOne($query, [$id]); } public function fetchAll(): array { $query = $this->getQueryBuilder() ->select() ->from($this->getTable()); - return array_map([$this, 'load'], $this->getConnection()->query($query)->getAsArray()); + return $this->fetchMany($query); } + + abstract protected function fieldsForUpdate(): array; + abstract protected function fieldsForInsert(): array; + abstract protected function valuesForUpdate(Model $model): array; + abstract protected function valuesForInsert(Model $model): array; + abstract protected function defaultFind(Model $model): Model; + abstract protected function defaultSearch(array $data): Model; } diff --git a/src/Concept/Model/Repository.php b/src/Concept/Model/Repository.php index fac68fa..998d365 100644 --- a/src/Concept/Model/Repository.php +++ b/src/Concept/Model/Repository.php @@ -27,26 +27,6 @@ interface Repository * @return string */ public function getTable(): string; - /** - * Get table columns - * @return array - */ - public function getColumns(): array; - /** - * Get required columns - * @return array - */ - public function getRequiredColumns(): array; - /** - * Get optional columns - * @return array - */ - public function getOptionalColumns(): array; - /** - * Get Model properties - * @return array - */ - public function getProperties(): array; /** * @param Connection $connection @@ -73,53 +53,6 @@ interface Repository * @return Repository */ public function setTable(string $table): Repository; - /** - * Set columns in table - * @param array $columns - * @return Repository - */ - public function setColumns(array $columns): Repository; - /** - * Set required columns - * Optional - * @param array $columns - * @return Repository - */ - public function setRequiredColumns(array $columns): Repository; - /** - * Set optional columns - * @param array $columns - * @return Repository - */ - public function setOptionalColumns(array $columns): Repository; - /** - * Set Model properties - * @param array $properties - * @return Repository - */ - public function setProperties(array $properties): Repository; - - /** - * @param string $column - * @return Repository - */ - public function addColumn(string $column): Repository; - /** - * @param string $column - * @return Repository - */ - public function addRequiredColumn(string $column): Repository; - /** - * @param string $column - * @return Repository - */ - public function addOptionalColumn(string $column): Repository; - /** - * @param string $property - * @param $value - * @return Repository - */ - public function addProperty(string $property): Repository; /** * Set up the Repository @@ -133,39 +66,6 @@ interface Repository */ public function setup(): Repository; - /** - * Get clean empty Model - * @return Model - */ - public function getNewModel(): Model; - /** - * Get Model method - * @param string $property - * @param bool $get - * @return string - */ - public function getMethod(string $property, bool $get = true): string; - /** - * @param string $method - * @return string - */ - public function getProperty(string $method): string; - - /** - * Fill empty Model with data - * @param Model $model - * @param array $data - * @return Model - */ - public function mapModel(Model $model, array $data): Model; - /** - * Fill data array with Model values. Accepts a preset data array - * @param Model $model - * @param array $data - * @return array - */ - public function mapTable(Model $model, array $data): array; - /** * Transform result array to Model * @param array $data @@ -177,26 +77,19 @@ interface Repository * @param Model $model * @return void */ - public function save(Model $model): void; + public function save(Model &$model): void; /** * Update table value with Model * @param Model $model * @return void */ - public function update(Model $model): void; + public function update(Model $model, Model $old): void; /** * Create new Model with data * @param array $data * @return Model */ public function create(array $data): Model; - /** - * Edit Model with data - * @param Model $model - * @param array $data - * @return Model - */ - public function edit(Model $model, array $data): Model; /** * Delete Model from table * @param Model $model