diff --git a/DataStorage/Database/DataMapperAbstract.php b/DataStorage/Database/DataMapperAbstract.php index a7a642436..de3fcf817 100644 --- a/DataStorage/Database/DataMapperAbstract.php +++ b/DataStorage/Database/DataMapperAbstract.php @@ -378,9 +378,12 @@ class DataMapperAbstract implements DataMapperInterface } foreach (static::$columns as $key => $column) { - if (isset(static::$ownsOne[$propertyName])) { - self::createOwnsOne($propertyName, $property, $obj); - } elseif ($column['internal'] === $propertyName) { + if (isset(static::$ownsOne[$propertyName]) && $column['internal'] === $propertyName) { + $id = self::createOwnsOne($propertyName, $property, $obj); + $value = self::parseValue($column['type'], $id); + + $query->insert($column['name'])->value($value, $column['type']); + } elseif ($column['internal'] === $propertyName && $column['type'] !== static::$primaryField) { $value = self::parseValue($column['type'], $property->getValue($obj)); $query->insert($column['name'])->value($value, $column['type']); @@ -509,7 +512,7 @@ class DataMapperAbstract implements DataMapperInterface } } - private static function createHasOne() + private static function createHasOne(\ReflectionClass $reflectionClass, $obj) { foreach (static::$hasOne as $propertyName => $rel) { @@ -599,16 +602,26 @@ class DataMapperAbstract implements DataMapperInterface */ private static function parseValue(string $type, $value) { - if ($type === 'DateTime') { - return isset($value) ? $value->format('Y-m-d H:i:s') : null; + if (is_null($value)) { + return null; + } elseif ($type === 'DateTime') { + return $value->format('Y-m-d H:i:s'); } elseif ($type === 'json') { - return isset($value) ? json_encode($value) : ''; + return json_encode($value); } elseif ($type === 'Serializable') { return $value->serialize(); } elseif ($type === 'jsonSerializable') { return $value->jsonSerializable(); } elseif (is_object($value)) { return $value->getId(); + } elseif ($type === 'int') { + return (int) $value; + } elseif ($type === 'string') { + return (string) $value; + } elseif ($type === 'float') { + return (float) $value; + } elseif ($type === 'bool') { + return (bool) $value; } return $value; @@ -752,20 +765,8 @@ class DataMapperAbstract implements DataMapperInterface $reflectionProperty->setAccessible(true); } - // relation table vs relation defined in same table as object e.g. comments - if ($values !== false) { - $objects = $mapper::get($values); - $reflectionProperty->setValue($obj, $objects); - } else { - // todo: replace getId() with lookup by primaryKey and the assoziated member variable and get value - $query = $mapper::find()->where(static::$hasMany[$member]['table'] . '.' . static::$hasMany[$member]['dst'], '=', $obj->getId()); - $sth = self::$db->con->prepare($query->toSql()); - $sth->execute(); - - $results = $sth->fetchAll(\PDO::FETCH_ASSOC); - $objects = $mapper::populateIterable($results); - $reflectionProperty->setValue($obj, $objects); - } + $objects = $mapper::get($values); + $reflectionProperty->setValue($obj, $objects); if (!$accessible) { $reflectionProperty->setAccessible(false); @@ -781,10 +782,12 @@ class DataMapperAbstract implements DataMapperInterface * * @return mixed * + * @todo accept reflection class as parameter + * * @since 1.0.0 * @author Dennis Eichhorn */ - public static function populateOneToOne(&$obj) + public static function populateHasOne(&$obj) { $reflectionClass = new \ReflectionClass(get_class($obj)); @@ -810,6 +813,44 @@ class DataMapperAbstract implements DataMapperInterface } } + /** + * Populate data. + * + * @param mixed $obj Object to add the relations to + * + * @return mixed + * + * @todo accept reflection class as parameter + * + * @since 1.0.0 + * @author Dennis Eichhorn + */ + public static function populateOwnsOne(&$obj) + { + $reflectionClass = new \ReflectionClass(get_class($obj)); + + foreach (static::$ownsOne as $member => $one) { + // todo: is that if necessary? performance is suffering for sure! + if ($reflectionClass->hasProperty($member)) { + $reflectionProperty = $reflectionClass->getProperty($member); + + if (!($accessible = $reflectionProperty->isPublic())) { + $reflectionProperty->setAccessible(true); + } + + /** @var DataMapperAbstract $mapper */ + $mapper = static::$ownsOne[$member]['mapper']; + + $value = $mapper::get($reflectionProperty->getValue($obj)); + $reflectionProperty->setValue($obj, $value); + + if (!$accessible) { + $reflectionProperty->setAccessible(false); + } + } + } + } + /** * Populate data. * @@ -1043,7 +1084,7 @@ class DataMapperAbstract implements DataMapperInterface $hasOne = count(static::$hasOne) > 0; $ownsOne = count(static::$ownsOne) > 0; - if ($relations !== RelationType::NONE && ($hasMany || $hasOne)) { + if ($relations !== RelationType::NONE && ($hasMany || $hasOne || $ownsOne)) { foreach ($obj as $key => $value) { /* loading relations from relations table and populating them and then adding them to the object */ if ($relations !== RelationType::NONE) { @@ -1052,7 +1093,11 @@ class DataMapperAbstract implements DataMapperInterface } if ($hasOne) { - self::populateOneToOne($obj[$key]); + self::populateHasOne($obj[$key]); + } + + if ($ownsOne) { + self::populateOwnsOne($obj[$key]); } } } @@ -1117,41 +1162,39 @@ class DataMapperAbstract implements DataMapperInterface $result = []; foreach (static::$hasMany as $member => $value) { - if (!isset($value['relationmapper'])) { - $query = new Builder(self::$db); - $query->prefix(self::$db->getPrefix()); + $query = new Builder(self::$db); + $query->prefix(self::$db->getPrefix()); - if ($relations === RelationType::ALL) { - $query->select($value['table'] . '.' . $value['src']) - ->from($value['table']) - ->where($value['table'] . '.' . $value['dst'], '=', $primaryKey); - } elseif ($relations === RelationType::NEWEST) { + if ($relations === RelationType::ALL) { + $src = $value['src'] ?? $value['mapper']::$primaryField; - /* - SELECT c.*, p1.* - FROM customer c - JOIN purchase p1 ON (c.id = p1.customer_id) - LEFT OUTER JOIN purchase p2 ON (c.id = p2.customer_id AND - (p1.date < p2.date OR p1.date = p2.date AND p1.id < p2.id)) - WHERE p2.id IS NULL; - */ - /* - $query->select(static::$table . '.' . static::$primaryField, $value['table'] . '.' . $value['src']) - ->from(static::$table) - ->join($value['table']) - ->on(static::$table . '.' . static::$primaryField, '=', $value['table'] . '.' . $value['dst']) - ->leftOuterJoin($value['table']) - ->on(new And('1', new And(new Or('d1', 'd2'), 'id'))) - ->where($value['table'] . '.' . $value['dst'], '=', 'NULL'); - */ - } + $query->select($value['table'] . '.' . $src) + ->from($value['table']) + ->where($value['table'] . '.' . $value['dst'], '=', $primaryKey); + } elseif ($relations === RelationType::NEWEST) { - $sth = self::$db->con->prepare($query->toSql()); - $sth->execute(); - $result[$member] = $sth->fetchAll(\PDO::FETCH_COLUMN); - } else { - $result[$member] = false; + /* + SELECT c.*, p1.* + FROM customer c + JOIN purchase p1 ON (c.id = p1.customer_id) + LEFT OUTER JOIN purchase p2 ON (c.id = p2.customer_id AND + (p1.date < p2.date OR p1.date = p2.date AND p1.id < p2.id)) + WHERE p2.id IS NULL; + */ + /* + $query->select(static::$table . '.' . static::$primaryField, $value['table'] . '.' . $value['src']) + ->from(static::$table) + ->join($value['table']) + ->on(static::$table . '.' . static::$primaryField, '=', $value['table'] . '.' . $value['dst']) + ->leftOuterJoin($value['table']) + ->on(new And('1', new And(new Or('d1', 'd2'), 'id'))) + ->where($value['table'] . '.' . $value['dst'], '=', 'NULL'); + */ } + + $sth = self::$db->con->prepare($query->toSql()); + $sth->execute(); + $result[$member] = $sth->fetchAll(\PDO::FETCH_COLUMN); } return $result; diff --git a/Log/FileLogger.php b/Log/FileLogger.php index 592d1b369..4f63acd67 100644 --- a/Log/FileLogger.php +++ b/Log/FileLogger.php @@ -105,7 +105,7 @@ class FileLogger implements LoggerInterface if (is_dir($lpath) || strpos($lpath, '.') === false) { File::create($path = $path . '/' . date('Y-m-d') . '.log'); } else { - File::create($lpath); + File::create($path = $lpath); } $this->path = $path; diff --git a/Math/Finance/FinanceFormulas.php b/Math/Finance/FinanceFormulas.php index f00ec8c21..2d4ec0665 100644 --- a/Math/Finance/FinanceFormulas.php +++ b/Math/Finance/FinanceFormulas.php @@ -52,7 +52,7 @@ class FinanceFormulas /** * Annual Percentage Yield * - * @latex r = \left(APY + 1\right)^{\frac{1}{n}} \cdot n - 1 + * @latex r = \left(\left(APY + 1\right)^{\frac{1}{n}} - 1\right) \cdot n * * @param float $apy Annual percentage yield * @param int $n Number of times compounded @@ -64,7 +64,7 @@ class FinanceFormulas */ public static function getStateAnnualInterestRateOfAPY(float $apy, int $n) : float { - return pow($apy + 1, 1 / $n) * $n - 1; + return (pow($apy + 1, 1 / $n) - 1) * $n; } /** diff --git a/Math/Number/Prime.php b/Math/Number/Prime.php index 7076885d5..87f19de39 100644 --- a/Math/Number/Prime.php +++ b/Math/Number/Prime.php @@ -145,7 +145,7 @@ class Prime $number = next($primes); } - return $primes; + return array_values($primes); } /** diff --git a/Module/ModuleFactory.php b/Module/ModuleFactory.php index d3575fe8e..040b4913e 100644 --- a/Module/ModuleFactory.php +++ b/Module/ModuleFactory.php @@ -82,7 +82,7 @@ class ModuleFactory self::registerRequesting($obj); self::registerProvided($obj); } catch (\Exception $e) { - throw new \InvalidArgumentException('Module "' . $module . '" is not loaded.'); + self::$loaded[$module] = new NullModule($app); } } diff --git a/Module/ModuleManager.php b/Module/ModuleManager.php index 640452127..7825b79b8 100644 --- a/Module/ModuleManager.php +++ b/Module/ModuleManager.php @@ -245,16 +245,15 @@ class ModuleManager $c = count($files); for ($i = 0; $i < $c; $i++) { - $path = realpath($oldPath = self::MODULE_PATH . '/' . $files[$i] . '/info.json'); + $path = self::MODULE_PATH . '/' . $files[$i] . '/info.json'; - if (file_exists($path)) { - if (strpos($path, self::MODULE_PATH) === false) { - throw new PathException($oldPath); - } - - $json = json_decode(file_get_contents($path), true); - self::$all[$json['name']['internal']] = $json; + if (!file_exists($path)) { + continue; + // throw new PathException($path); } + + $json = json_decode(file_get_contents($path), true); + self::$all[$json['name']['internal']] = $json; } } @@ -388,8 +387,9 @@ class ModuleManager return false; } - if (!file_exists(self::MODULE_PATH . '/' . $module . '/Admin/Install.php')) { + if (!file_exists(self::MODULE_PATH . '/' . $module . '/Admin/Installer.php')) { // todo download; + return false; } try { @@ -526,7 +526,7 @@ class ModuleManager { $path = realpath($oldPath = self::MODULE_PATH . '/' . $module . '/' . 'info.json'); - if ($path === false || strpos($path, self::MODULE_PATH) === false) { + if ($path === false) { throw new PathException($oldPath); } @@ -584,38 +584,17 @@ class ModuleManager /** * Initialize module. * - * @param string|array $module Module name + * @param string|array $modules Module name * * @throws * * @since 1.0.0 * @author Dennis Eichhorn */ - public function initModule($module) + public function initModule($modules) { - try { - if (!is_array($module)) { - $module = [$module]; - } + $modules = (array) $modules; - $this->initModuleArray($module); - } catch (\Exception $e) { - throw $e; - } - } - - /** - * Initialize array of modules. - * - * @param array $modules Modules - * - * @return void - * - * @since 1.0.0 - * @author Dennis Eichhorn - */ - private function initModuleArray(array $modules) - { foreach ($modules as $module) { try { $this->initModuleController($module);