createSelect($select, $type, $useRelations, $withRelations); $data = $select->execute()->fetch(IResultSet::FETCH_ASSOC); if ($data === null) { return null; } $model = new $type(); $this->fillWithData($data, $model, $withRelations); return $model; } public function selectMultipleFromDb(Select $select, string $type, bool $useRelations = false, array $withRelations = []): Generator { $select = $this->createSelect($select, $type, $useRelations, $withRelations); $result = $select->execute(); while ($data = $result->fetch(IResultSet::FETCH_ASSOC)) { $model = new $type(); $this->fillWithData($data, $model, $withRelations); yield $model; } } public function selectFromDbById($id, string $type, bool $useRelations = false) { $select = new Select(\Container::$dbConnection); $select->whereId($id); return $this->selectFromDb($select, $type, $useRelations); } public function fillWithData(array &$data, Model $model, array $withRelations = [], ?string $modelKey = null): void { $relations = $model::getRelations(); if (count($withRelations)) { $relations = array_intersect($relations, $withRelations); } while (key($data)) { $key = key($data); $value = current($data); $relation = key($relations); if (strpos($key, '__') === false) { $method = 'set' . str_replace('_', '', ucwords($key, '_')); if (method_exists($model, $method) && isset($value)) { $model->$method($value); } next($data); } else if (isset($modelKey) && substr($key, 0, strlen($modelKey . '__')) === $modelKey . '__') { $key = substr($key, strlen($modelKey) + 2); $method = 'set' . str_replace('_', '', ucwords($key, '_')); if (method_exists($model, $method) && isset($value)) { $model->$method($value); } next($data); } else if (substr($key, 0, strlen($relation . '__')) === $relation . '__') { $relationType = current($relations); $relationModel = new $relationType(); $this->fillWithData($data, $relationModel, $withRelations, $relation); $method = 'set' . str_replace('_', '', ucwords($relation, '_')); $model->$method($relationModel); next($relations); } else { return; } } $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 createSelect(Select $select, string $type, bool $useRelations = false, array $withRelations = []): Select { $table = call_user_func([$type, 'getTable']); $fields = call_user_func([$type, 'getFields']); $columns = []; foreach ($fields as $field) { $columns[] = [$table, $field]; } $select->from($table); if ($useRelations) { $relations = call_user_func([$type, 'getRelations']); if (count($withRelations)) { $relations = array_intersect($relations, $withRelations); } $columns = array_merge($columns, $this->getRelationColumns($relations, $withRelations)); $this->leftJoinRelations($select, $table, $relations, $withRelations); $select->columns($columns); } else { $select->columns($columns); } return $select; } private function getRelationColumns(array $relations, array $withRelations): 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]; } $nextOrderRelations = call_user_func([$relationType, 'getRelations']); if (count($withRelations)) { $nextOrderRelations = array_intersect($nextOrderRelations, $withRelations); } $columns = array_merge($columns, $this->getRelationColumns($nextOrderRelations, $withRelations)); } return $columns; } private function leftJoinRelations(Select $select, string $table, array $relations, array $withRelations): void { foreach ($relations as $relation => $relationType) { $relationTable = call_user_func([$relationType, 'getTable']); $select->leftJoin($relationTable, [$relationTable, 'id'], '=', [$table, $relation . '_id']); $nextOrderRelations = call_user_func([$relationType, 'getRelations']); if (count($withRelations)) { $nextOrderRelations = array_intersect($nextOrderRelations, $withRelations); } $this->leftJoinRelations($select, $relationTable, $nextOrderRelations, $withRelations); } } 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()); } } } }