From 9745efa7d5841b75e568545df05ca40c7356d043 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 25 Nov 2022 19:25:41 +0100 Subject: [PATCH] creating join --- .../Database/Mapper/DataMapperAbstract.php | 88 +++++++++++++++++++ DataStorage/Database/Mapper/ReadMapper.php | 40 +++++++-- 2 files changed, 119 insertions(+), 9 deletions(-) diff --git a/DataStorage/Database/Mapper/DataMapperAbstract.php b/DataStorage/Database/Mapper/DataMapperAbstract.php index 6f0ba0a2d..382796471 100755 --- a/DataStorage/Database/Mapper/DataMapperAbstract.php +++ b/DataStorage/Database/Mapper/DataMapperAbstract.php @@ -95,6 +95,14 @@ abstract class DataMapperAbstract */ protected array $where = []; + /** + * Join conditions + * + * @var array + * @since 1.0.0 + */ + protected array $join = []; + /** * Base query which is merged with the query in the mapper * @@ -307,6 +315,86 @@ abstract class DataMapperAbstract return $this; } + /** + * Define the joining data + * + * @param string $member Property name to filter by + * @param string $mapper Mapper + * @param mixed $value Filter value + * @param string $logic Comparison logic (e.g. =, in, ...) + * @param string $type Join type (e.g. left, right, inner) + * + * @return static + * + * @since 1.0.0 + */ + public function join(string $member, string $mapper, mixed $value, string $logic = '=', string $type = 'left') : self + { + $split = \explode('/', $member); + $memberSplit = \array_shift($split); + + $this->join[$memberSplit][] = [ + 'child' => \implode('/', $split), + 'mapper' => $mapper, + 'value' => $value, + 'logic' => $logic, + 'type' => $type, + ]; + + return $this; + } + + /** + * Define the joining data + * + * @param string $member Property name to filter by + * @param string $mapper Mapper + * @param mixed $value Filter value + * @param string $logic Comparison logic (e.g. =, in, ...) + * + * @return static + * + * @since 1.0.0 + */ + public function leftJoin(string $member, string $mapper, mixed $value, string $logic = '=') : self + { + return $this->join($member, $mapper, $value, $logic, 'left'); + } + + /** + * Define the joining data + * + * @param string $member Property name to filter by + * @param string $mapper Mapper + * @param mixed $value Filter value + * @param string $logic Comparison logic (e.g. =, in, ...) + * + * @return static + * + * @since 1.0.0 + */ + public function rightJoin(string $member, string $mapper, mixed $value, string $logic = '=') : self + { + return $this->join($member, $mapper, $value, $logic, 'right'); + } + + /** + * Define the joining data + * + * @param string $member Property name to filter by + * @param string $mapper Mapper + * @param mixed $value Filter value + * @param string $logic Comparison logic (e.g. =, in, ...) + * + * @return static + * + * @since 1.0.0 + */ + public function innerJoin(string $member, string $mapper, mixed $value, string $logic = '=') : self + { + return $this->join($member, $mapper, $value, $logic, 'inner'); + } + /** * Populate a mapper (e.g. child mapper, relation mapper) based on the current mapper information. * diff --git a/DataStorage/Database/Mapper/ReadMapper.php b/DataStorage/Database/Mapper/ReadMapper.php index a421cad37..d1731e36a 100755 --- a/DataStorage/Database/Mapper/ReadMapper.php +++ b/DataStorage/Database/Mapper/ReadMapper.php @@ -173,7 +173,6 @@ final class ReadMapper extends DataMapperAbstract { $primaryKeys = []; $memberOfPrimaryField = $this->mapper::COLUMNS[$this->mapper::PRIMARYFIELD]['internal']; - $emptyWhere = empty($this->where); if (isset($this->where[$memberOfPrimaryField])) { $keys = $this->where[$memberOfPrimaryField][0]['value']; @@ -184,16 +183,14 @@ final class ReadMapper extends DataMapperAbstract $obj = []; // Get remaining objects (not available in memory cache) or remaining where clauses. - if (!empty($primaryKeys) || (!empty($this->where) || $emptyWhere)) { - $dbData = $this->executeGetRaw($query); + $dbData = $this->executeGetRaw($query); - foreach ($dbData as $row) { - $value = $row[$this->mapper::PRIMARYFIELD . '_d' . $this->depth]; - $obj[$value] = $this->mapper::createBaseModel(); + foreach ($dbData as $row) { + $value = $row[$this->mapper::PRIMARYFIELD . '_d' . $this->depth]; + $obj[$value] = $this->mapper::createBaseModel(); - $obj[$value] = $this->populateAbstract($row, $obj[$value]); - $this->loadHasManyRelations($obj[$value]); - } + $obj[$value] = $this->populateAbstract($row, $obj[$value]); + $this->loadHasManyRelations($obj[$value]); } $countResulsts = \count($obj); @@ -322,6 +319,31 @@ final class ReadMapper extends DataMapperAbstract $query->fromAs($this->mapper::TABLE, $this->mapper::TABLE . '_d' . $this->depth); } + // Join tables manually without using "with()" (NOT has many/owns one etc.) + // This is necessary for special cases, e.g. when joining in the other direction + // Example: Show all profiles who have written a news article. + // "with()" only allows to go from articles to accounts but we want to go the other way + foreach ($this->join as $member => $values) { + if (($col = $this->mapper::getColumnByMember($member)) !== null) { + /* variable in model */ + foreach ($values as $join) { + // @todo: the has many, etc. if checks only work if it is a relation on the first level, if we have a deeper where condition nesting this fails + if ($join['child'] !== '') { + continue; + } + + $query->join($join['mapper']::TABLE, $join['type'], $join['mapper']::TABLE . '_d' . ($this->depth + 1)) + ->on( + $this->mapper::TABLE . '_d' . $this->depth . '.' . $col, + '=', + $join['mapper']::TABLE . '_d' . ($this->depth + 1) . '.' . $join['mapper']::getColumnByMember($join['value']), + 'and', + $join['mapper']::TABLE . '_d' . ($this->depth + 1) + ); + } + } + } + // where foreach ($this->where as $member => $values) { // handle where query