From bb1aa4044a03023ff3d8bddc826b905a4d12df98 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Tue, 3 Sep 2019 14:49:42 +0200 Subject: [PATCH 1/2] Draft conditional data --- DataStorage/Database/DataMapperAbstract.php | 129 +++++++++++++++----- 1 file changed, 98 insertions(+), 31 deletions(-) diff --git a/DataStorage/Database/DataMapperAbstract.php b/DataStorage/Database/DataMapperAbstract.php index 3b7400a0f..747cbd509 100644 --- a/DataStorage/Database/DataMapperAbstract.php +++ b/DataStorage/Database/DataMapperAbstract.php @@ -81,6 +81,16 @@ class DataMapperAbstract implements DataMapperInterface * @since 1.0.0 */ protected static array $columns = []; + + /** + * Conditional. + * + * Most often used for localizations + * + * @var array> + * @since 1.0.0 + */ + protected static array $conditionals = []; /** * Has many relation. @@ -361,6 +371,7 @@ class DataMapperAbstract implements DataMapperInterface if ($relations === RelationType::ALL) { self::createHasMany($refClass, $obj, $objId); + self::createConditionals($refClass, $obj, $objId); } return $objId; @@ -390,6 +401,7 @@ class DataMapperAbstract implements DataMapperInterface if ($relations === RelationType::ALL) { self::createHasManyArray($obj, $objId); + self::createConditionalsArray($refClass, $obj, $objId); } return $objId; @@ -1401,6 +1413,7 @@ class DataMapperAbstract implements DataMapperInterface if ($relations === RelationType::ALL) { self::updateHasMany($refClass, $obj, $objId, --$depth); + self::updateConditionals($refClass, $obj, $objId); } if (empty($objId)) { @@ -1447,6 +1460,7 @@ class DataMapperAbstract implements DataMapperInterface if ($relations === RelationType::ALL) { self::updateHasManyArray($obj, $objId, --$depth); + self::updateConditionalsArray($refClass, $obj, $objId); } if ($update) { @@ -1662,6 +1676,7 @@ class DataMapperAbstract implements DataMapperInterface if ($relations !== RelationType::NONE) { self::deleteHasMany($refClass, $obj, $objId, $relations); + self::deleteConditionals($refClass, $obj, $objId); } self::deleteModel($obj, $objId, $relations, $refClass); @@ -1669,6 +1684,8 @@ class DataMapperAbstract implements DataMapperInterface return $objId; } + // @todo: implement todo delete + /** * Populate data. * @@ -1708,9 +1725,9 @@ class DataMapperAbstract implements DataMapperInterface foreach ($result as $element) { if (isset($element[static::$primaryField])) { - $row[$element[static::$primaryField]] = self::populateAbstractArray($element); + $row[$element[static::$primaryField]] = self::populateAbstractArray($element, [], static::$columns); } else { - $row[] = self::populateAbstractArray($element); + $row[] = self::populateAbstractArray($element, [], static::$columns); } } @@ -1745,7 +1762,7 @@ class DataMapperAbstract implements DataMapperInterface $obj = new $class(); } - return self::populateAbstract($result, $obj); + return self::populateAbstract($result, $obj, static::$columns); } /** @@ -1853,6 +1870,39 @@ class DataMapperAbstract implements DataMapperInterface } } + /** + * Populate data. + * + * @param mixed $obj Object to add the relations to + * + * @return void + * + * @todo accept reflection class as parameter + * + * @since 1.0.0 + */ + public static function getConditionals($key, string $table) : array + { + $query = new Builder(self::$db); + $query->prefix(self::$db->getPrefix()) + ->select(...static::$conditionals[$table]['values']) + ->from($table) + ->where(static::$conditionals[$table]['dest'], '=', $key); + + foreach (static::$conditionals[$table]['filter'] as $column => $filter) { + if ($filter !== null) { + $query->where($column, '=', $filter); + } + } + + $sth = self::$db->con->prepare($query->toSql()); + $sth->execute(); + + $results = $sth->fetch(\PDO::FETCH_ASSOC); + + return $results === false ? [] : $results; + } + /** * Populate data. * @@ -1958,12 +2008,12 @@ class DataMapperAbstract implements DataMapperInterface * * @since 1.0.0 */ - public static function populateAbstract(array $result, $obj) + public static function populateAbstract(array $result, $obj, array $columns) { $refClass = new \ReflectionClass($obj); foreach ($result as $column => $value) { - if (!isset(static::$columns[$column]['internal']) /* && $refClass->hasProperty(static::$columns[$column]['internal']) */) { + if (!isset($columns[$column]['internal']) /* && $refClass->hasProperty($columns[$column]['internal']) */) { continue; } @@ -1971,9 +2021,9 @@ class DataMapperAbstract implements DataMapperInterface $aValue = []; $arrayPath = ''; - if (\stripos(static::$columns[$column]['internal'], '/') !== false) { + if (\stripos($columns[$column]['internal'], '/') !== false) { $hasPath = true; - $path = \explode('/', static::$columns[$column]['internal']); + $path = \explode('/', $columns[$column]['internal']); $refProp = $refClass->getProperty($path[0]); if (!($accessible = $refProp->isPublic())) { @@ -1984,17 +2034,17 @@ class DataMapperAbstract implements DataMapperInterface $arrayPath = \implode('/', $path); $aValue = $refProp->getValue($obj); } else { - $refProp = $refClass->getProperty(static::$columns[$column]['internal']); + $refProp = $refClass->getProperty($columns[$column]['internal']); if (!($accessible = $refProp->isPublic())) { $refProp->setAccessible(true); } } - if (\in_array(static::$columns[$column]['type'], ['string', 'int', 'float', 'bool'])) { + if (\in_array($columns[$column]['type'], ['string', 'int', 'float', 'bool'])) { // todo: what is this or condition for? seems to be wrong if obj null then it doesn't work anyways if ($value !== null || $refProp->getValue($obj) !== null) { - \settype($value, static::$columns[$column]['type']); + \settype($value, $columns[$column]['type']); } if ($hasPath) { @@ -2002,24 +2052,24 @@ class DataMapperAbstract implements DataMapperInterface } $refProp->setValue($obj, $value); - } elseif (static::$columns[$column]['type'] === 'DateTime') { + } elseif ($columns[$column]['type'] === 'DateTime') { $value = $value === null ? null : new \DateTime($value); if ($hasPath) { $value = ArrayUtils::setArray($arrayPath, $aValue, $value, '/', true); } $refProp->setValue($obj, $value); - } elseif (static::$columns[$column]['type'] === 'Json') { + } elseif ($columns[$column]['type'] === 'Json') { if ($hasPath) { $value = ArrayUtils::setArray($arrayPath, $aValue, $value, '/', true); } $refProp->setValue($obj, \json_decode($value, true)); - } elseif (static::$columns[$column]['type'] === 'Serializable') { + } elseif ($columns[$column]['type'] === 'Serializable') { $member = $refProp->getValue($obj); $member->unserialize($value); } else { - throw new \UnexpectedValueException('Value "' . static::$columns[$column]['type'] . '" is not supported.'); + throw new \UnexpectedValueException('Value "' . $columns[$column]['type'] . '" is not supported.'); } if (!$accessible) { @@ -2039,12 +2089,11 @@ class DataMapperAbstract implements DataMapperInterface * * @since 1.0.0 */ - public static function populateAbstractArray(array $result) : array + public static function populateAbstractArray(array $result, array $obj, array $columns) : array { - $obj = []; foreach ($result as $column => $value) { - if (isset(static::$columns[$column]['internal'])) { - $path = static::$columns[$column]['internal']; + if (isset($columns[$column]['internal'])) { + $path = $columns[$column]['internal']; if (\stripos($path, '/') !== false) { $path = \explode('/', $path); @@ -2052,11 +2101,11 @@ class DataMapperAbstract implements DataMapperInterface $path = \implode('/', $path); } - if (\in_array(static::$columns[$column]['type'], ['string', 'int', 'float', 'bool'])) { - \settype($value, static::$columns[$column]['type']); - } elseif (static::$columns[$column]['type'] === 'DateTime') { + if (\in_array($columns[$column]['type'], ['string', 'int', 'float', 'bool'])) { + \settype($value, $columns[$column]['type']); + } elseif ($columns[$column]['type'] === 'DateTime') { $value = $value === null ? null : new \DateTime($value); - } elseif (static::$columns[$column]['type'] === 'Json') { + } elseif ($columns[$column]['type'] === 'Json') { $value = \json_decode($value, true); } @@ -2184,7 +2233,7 @@ class DataMapperAbstract implements DataMapperInterface continue; } - $obj[$value] = self::populateAbstractArray(self::getRaw($value)); + $obj[$value] = self::populateAbstractArray(self::getRaw($value), [], static::$columns); self::addInitializedArray(static::class, $value, $obj[$value]); } @@ -2492,11 +2541,12 @@ class DataMapperAbstract implements DataMapperInterface return; } - $hasMany = !empty(static::$hasMany); - $ownsOne = !empty(static::$ownsOne); - $belongsTo = !empty(static::$belongsTo); + $hasMany = !empty(static::$hasMany); + $ownsOne = !empty(static::$ownsOne); + $belongsTo = !empty(static::$belongsTo); + $hasConditionals = !empty(static::$conditionals); - if (!($hasMany || $ownsOne || $belongsTo)) { + if (!($hasMany || $ownsOne || $belongsTo || $hasConditionals)) { return; } @@ -2513,6 +2563,12 @@ class DataMapperAbstract implements DataMapperInterface if ($belongsTo) { self::populateBelongsTo($obj[$key], $depth); } + + if ($hasConditionals) { + foreach (static::$conditionals as $table => $conditional) { + $obj[$key] = self::populateAbstract(self::getConditionals($key, $table), $obj[$key], $conditional['values']); + } + } } } @@ -2537,14 +2593,19 @@ class DataMapperAbstract implements DataMapperInterface return; } - $hasMany = !empty(static::$hasMany); - $ownsOne = !empty(static::$ownsOne); - $belongsTo = !empty(static::$belongsTo); + $hasMany = !empty(static::$hasMany); + $ownsOne = !empty(static::$ownsOne); + $belongsTo = !empty(static::$belongsTo); + $hasConditionals = !empty(static::$conditionals); - if (!($hasMany || $ownsOne || $belongsTo)) { + if (!($hasMany || $ownsOne || $belongsTo || $hasConditionals)) { return; } + if ($conditionals) { + self::populateConditionalsArray(); + } + foreach ($obj as $key => $value) { /* loading relations from relations table and populating them and then adding them to the object */ if ($hasMany) { @@ -2558,6 +2619,12 @@ class DataMapperAbstract implements DataMapperInterface if ($belongsTo) { self::populateBelongsToArray($obj[$key], $depth); } + + if ($hasConditionals) { + foreach (static::$conditionals as $table => $conditional) { + $obj[$key] = self::populateAbstractArray(self::getConditionals($key, $table), $obj[$key], $conditional['values']); + } + } } } From 743ba4a93783e11dd1a1144365e3c34f6ff8309b Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Tue, 3 Sep 2019 15:38:09 +0200 Subject: [PATCH 2/2] Implement missing conditional functionality (delete, update) --- DataStorage/Database/DataMapperAbstract.php | 115 +++++++++++++++++++- 1 file changed, 109 insertions(+), 6 deletions(-) diff --git a/DataStorage/Database/DataMapperAbstract.php b/DataStorage/Database/DataMapperAbstract.php index 747cbd509..9f75af570 100644 --- a/DataStorage/Database/DataMapperAbstract.php +++ b/DataStorage/Database/DataMapperAbstract.php @@ -31,6 +31,7 @@ use phpOMS\Utils\ArrayUtils; * @license OMS License 1.0 * @link https://orange-management.org * @since 1.0.0 + * @todo: currently hasmany, owns one etc. are not using joins. In some cases this could improve the performance instead of separately querying the database */ class DataMapperAbstract implements DataMapperInterface { @@ -1326,6 +1327,47 @@ class DataMapperAbstract implements DataMapperInterface self::$db->con->prepare($query->toSql())->execute(); } + private static function updateConditionals(object $obj, $objId, \ReflectionClass $refClass = null, int $relations = RelationType::ALL, int $depth = 1) : void + { + foreach (static::$conditionals as $table => $contidional) { + $query = new Builder(self::$db); + $query->prefix(self::$db->getPrefix()) + ->update($table) + ->where($table . '.' . $conditional['dst'], '=', $objId); + + foreach ($conditional['columns'] as $key => $column) { + $propertyName = \stripos($column['internal'], '/') !== false ? \explode('/', $column['internal'])[0] : $column['internal']; + + $refClass = $refClass ?? new \ReflectionClass($obj); + $property = $refClass->getProperty($propertyName); + + if (!($isPublic = $property->isPublic())) { + $property->setAccessible(true); + } + + if ($column['name'] !== $conditional['dst']) { + $tValue = $property->getValue($obj); + if (\stripos($column['internal'], '/') !== false) { + $path = \explode('/', $column['internal']); + + \array_shift($path); + $path = \implode('/', $path); + $tValue = ArrayUtils::getArray($path, $tValue, '/'); + } + $value = self::parseValue($column['type'], $tValue); + + $query->set([$table . '.' . $column['name'] => $value]); + } + + if (!$isPublic) { + $property->setAccessible(false); + } + } + + self::$db->con->prepare($query->toSql())->execute(); + } + } + /** * Update object in db. * @@ -1382,6 +1424,48 @@ class DataMapperAbstract implements DataMapperInterface self::$db->con->prepare($query->toSql())->execute(); } + /** + * Update object in db. + * + * @param array $obj Model to update + * @param mixed $objId Model id + * @param int $relations Create all relations as well + * @param int $depth Depth of relations to update (default = 1 = none) + * + * @return void + * + * @since 1.0.0 + */ + private static function updateConditionalsArray(array $obj, $objId, int $relations = RelationType::ALL, int $depth = 1) : void + { + foreach (static::$conditionals as $table => $contidional) { + $query = new Builder(self::$db); + $query->prefix(self::$db->getPrefix()) + ->update($table) + ->where($table . '.' . $conditional['dst'], '=', $objId); + + foreach ($conditional['columns'] as $key => $column) { + $path = $column['internal']; + if (\stripos($column['internal'], '/') !== false) { + $path = \explode('/', $column['internal']); + + \array_shift($path); // todo: why am I doing this? + $path = \implode('/', $path); + } + + $property = ArrayUtils::getArray($path, $obj, '/'); + + if ($column['name'] !== $conditional['dst']) { + $value = self::parseValue($column['type'], $property); + + $query->set([$table . '.' . $column['name'] => $value]); + } + } + + self::$db->con->prepare($query->toSql())->execute(); + } + } + /** * Update object in db. * @@ -1590,6 +1674,25 @@ class DataMapperAbstract implements DataMapperInterface return $obj; } + private static function deleteConditionals($key) : void + { + foreach (static::$conditionals as $table => $conditional) { + $query = new Builder(self::$db); + $query->prefix(self::$db->getPrefix()) + ->delete() + ->from($table) + ->where(static::$conditionals[$table]['dst'], '=', $key); + + foreach (static::$conditionals[$table]['filter'] as $column => $filter) { + if ($filter !== null) { + $query->where($column, '=', $filter); + } + } + + self::$db->con->prepare($query->toSql())->execute(); + } + } + /** * Delete object in db. * @@ -1676,7 +1779,7 @@ class DataMapperAbstract implements DataMapperInterface if ($relations !== RelationType::NONE) { self::deleteHasMany($refClass, $obj, $objId, $relations); - self::deleteConditionals($refClass, $obj, $objId); + self::deleteConditionals($objId); } self::deleteModel($obj, $objId, $relations, $refClass); @@ -1684,7 +1787,7 @@ class DataMapperAbstract implements DataMapperInterface return $objId; } - // @todo: implement todo delete + // @todo: implement array delete /** * Populate data. @@ -1885,9 +1988,9 @@ class DataMapperAbstract implements DataMapperInterface { $query = new Builder(self::$db); $query->prefix(self::$db->getPrefix()) - ->select(...static::$conditionals[$table]['values']) + ->select(...static::$conditionals[$table]['columns']) ->from($table) - ->where(static::$conditionals[$table]['dest'], '=', $key); + ->where(static::$conditionals[$table]['dst'], '=', $key); foreach (static::$conditionals[$table]['filter'] as $column => $filter) { if ($filter !== null) { @@ -2566,7 +2669,7 @@ class DataMapperAbstract implements DataMapperInterface if ($hasConditionals) { foreach (static::$conditionals as $table => $conditional) { - $obj[$key] = self::populateAbstract(self::getConditionals($key, $table), $obj[$key], $conditional['values']); + $obj[$key] = self::populateAbstract(self::getConditionals($key, $table), $obj[$key], $conditional['columns']); } } } @@ -2622,7 +2725,7 @@ class DataMapperAbstract implements DataMapperInterface if ($hasConditionals) { foreach (static::$conditionals as $table => $conditional) { - $obj[$key] = self::populateAbstractArray(self::getConditionals($key, $table), $obj[$key], $conditional['values']); + $obj[$key] = self::populateAbstractArray(self::getConditionals($key, $table), $obj[$key], $conditional['columns']); } } }