diff --git a/application/Espo/ORM/DB/BaseMapper.php b/application/Espo/ORM/DB/BaseMapper.php index b867609b93..c2ab74d9f6 100644 --- a/application/Espo/ORM/DB/BaseMapper.php +++ b/application/Espo/ORM/DB/BaseMapper.php @@ -188,7 +188,7 @@ abstract class BaseMapper implements Mapper if ($ps) { foreach ($ps as $row) { - return $row['AggregateValue']; + return $row['value']; } } @@ -207,27 +207,25 @@ abstract class BaseMapper implements Mapper { $params = $params ?? []; + $entityType = $entity->getEntityType(); + $relDefs = $entity->relations[$relationName]; if (!isset($relDefs['type'])) { throw new LogicException( - "Missing 'type' in definition for relationship {$relationName} in " . $entity->getEntityType() . " entity." + "Missing 'type' in definition for relationship {$relationName} in {entityType} entity." ); } if ($relDefs['type'] !== Entity::BELONGS_TO_PARENT) { if (!isset($relDefs['entity'])) { throw new LogicException( - "Missing 'entity' in definition for relationship {$relationName} in " . $entity->getEntityType() . " entity." + "Missing 'entity' in definition for relationship {$relationName} in {entityType} entity." ); } - $relEntityName = (!empty($relDefs['class'])) ? $relDefs['class'] : $relDefs['entity']; - $relEntity = $this->entityFactory->create($relEntityName); - - if (!$relEntity) { - return null; - } + $relEntityType = (!empty($relDefs['class'])) ? $relDefs['class'] : $relDefs['entity']; + $relEntity = $this->entityFactory->create($relEntityType); } if ($returnTotalCount) { @@ -256,17 +254,24 @@ abstract class BaseMapper implements Mapper $ps = $this->pdo->query($sql); - if ($ps) { - foreach ($ps as $row) { - if (!$returnTotalCount) { - $relEntity = $this->fromRow($relEntity, $row); - $relEntity->setAsFetched(); - return $relEntity; - } else { - return $row['AggregateValue']; - } - } + if (!$ps) { + return null; } + + if ($returnTotalCount) { + foreach ($ps as $row) { + return intval($row['value']); + } + return 0; + } + + foreach ($ps as $row) { + $relEntity = $this->fromRow($relEntity, $row); + $relEntity->setAsFetched(); + + return $relEntity; + } + return null; case Entity::HAS_MANY: @@ -302,16 +307,20 @@ abstract class BaseMapper implements Mapper } $ps = $this->pdo->query($sql); - if ($ps) { - if (!$returnTotalCount) { - $resultDataList = $ps->fetchAll(); - } else { - foreach ($ps as $row) { - return $row['AggregateValue']; - } - } + + if (!$ps) { + return null; } + if ($returnTotalCount) { + foreach ($ps as $row) { + return intval($row['value']); + } + return 0; + } + + $resultDataList = $ps->fetchAll(); + if ($relType == Entity::HAS_ONE) { if (count($resultDataList)) { $relEntity = $this->fromRow($relEntity, $resultDataList[0]); @@ -320,13 +329,13 @@ abstract class BaseMapper implements Mapper return $relEntity; } return null; - } else { - $collection = $this->createCollection($relEntity->getEntityType(), $resultDataList); - $collection->setAsFetched(); - - return $collection; } + $collection = $this->createCollection($relEntity->getEntityType(), $resultDataList); + $collection->setAsFetched(); + + return $collection; + case Entity::MANY_MANY: $additionalColumnsConditions = null; if (!empty($params['additionalColumnsConditions'])) { @@ -359,16 +368,19 @@ abstract class BaseMapper implements Mapper $ps = $this->pdo->query($sql); - if ($ps) { - if (!$returnTotalCount) { - $resultDataList = $ps->fetchAll(); - } else { - foreach ($ps as $row) { - return $row['AggregateValue']; - } - } + if (!$ps) { + return null; } + if ($returnTotalCount) { + foreach ($ps as $row) { + return intval($row['value']); + } + return null; + } + + $resultDataList = $ps->fetchAll(); + $collection = $this->createCollection($relEntity->getEntityType(), $resultDataList); $collection->setAsFetched(); @@ -377,9 +389,13 @@ abstract class BaseMapper implements Mapper case Entity::BELONGS_TO_PARENT: $foreignEntityType = $entity->get($keySet['typeKey']); $foreignEntityId = $entity->get($key); + if (!$foreignEntityType || !$foreignEntityId) { - return null; + throw new LogicException( + "Bad definition for relationship {$relationName} in {$entityType} entity." + ); } + $params['whereClause'][$foreignKey] = $foreignEntityId; $params['offset'] = 0; $params['limit'] = 1; @@ -390,20 +406,28 @@ abstract class BaseMapper implements Mapper $ps = $this->pdo->query($sql); - if ($ps) { - foreach ($ps as $row) { - if (!$returnTotalCount) { - $relEntity = $this->fromRow($relEntity, $row); - return $relEntity; - } else { - return $row['AggregateValue']; - } - } + if (!$ps) { + return null; } + + if ($returnTotalCount) { + foreach ($ps as $row) { + return intval($row['value']); + } + return 0; + } + + foreach ($ps as $row) { + $relEntity = $this->fromRow($relEntity, $row); + return $relEntity; + } + return null; } - return false; + throw new LogicException( + "Bad 'type' {$relType} in definition for relationship {$relationName} in {$entityType} entity." + ); } /** @@ -465,8 +489,7 @@ abstract class BaseMapper implements Mapper $wherePart = $this->toDb($nearKey) . " = " . $this->pdo->quote($entity->id) . " - AND " . $this->toDb($distantKey) . " = " . $this->pdo->quote($id) . " AND deleted = 0 - "; + AND " . $this->toDb($distantKey) . " = " . $this->pdo->quote($id) . " AND deleted = 0"; if (!empty($relDefs['conditions']) && is_array($relDefs['conditions'])) { foreach ($relDefs['conditions'] as $f => $v) { diff --git a/application/Espo/ORM/DB/Query/BaseQuery.php b/application/Espo/ORM/DB/Query/BaseQuery.php index 9b76012b14..f87c88c3c6 100644 --- a/application/Espo/ORM/DB/Query/BaseQuery.php +++ b/application/Espo/ORM/DB/Query/BaseQuery.php @@ -469,7 +469,7 @@ abstract class BaseQuery ); if ($params['aggregation'] === 'COUNT' && $groupByPart && $havingPart) { - $sql = "SELECT COUNT(*) AS `AggregateValue` FROM ({$sql}) AS `countAlias`"; + $sql = "SELECT COUNT(*) AS `value` FROM ({$sql}) AS `countAlias`"; } return $sql; @@ -619,8 +619,9 @@ abstract class BaseQuery return $selectPart; } - protected function getFunctionPart($function, $part, $entityType, $distinct = false, ?array $argumentPartList = null) - { + protected function getFunctionPart( + string $function, string $part, string $entityType, bool $distinct = false, ?array $argumentPartList = null + ) : string { if (!in_array($function, $this->functionList)) { throw new Error("ORM Query: Not allowed function '{$function}'."); } @@ -758,7 +759,7 @@ abstract class BaseQuery return $function . '(' . $part . ')'; } - protected function getFunctionPartTZ($entityType, ?array $argumentPartList = null) + protected function getFunctionPartTZ(string $entityType, ?array $argumentPartList = null) { if (!$argumentPartList || count($argumentPartList) < 2) { throw new Error("ORM Query: Not enough arguments for function TZ."); @@ -1452,7 +1453,7 @@ abstract class BaseQuery } $selectPart = "{$aggregation}({$distinctPart}" . $this->toDb($entity->getEntityType()) . "." . - $this->toDb($this->sanitize($aggregationBy)) . ") AS AggregateValue"; + $this->toDb($this->sanitize($aggregationBy)) . ") AS `value`"; return $selectPart; } diff --git a/tests/unit/Espo/ORM/DB/MapperTest.php b/tests/unit/Espo/ORM/DB/MapperTest.php index 3f92863e94..b1f9e56e98 100644 --- a/tests/unit/Espo/ORM/DB/MapperTest.php +++ b/tests/unit/Espo/ORM/DB/MapperTest.php @@ -318,15 +318,15 @@ class DBMapperTest extends \PHPUnit\Framework\TestCase public function testCountRelated() { $query = - "SELECT COUNT(tag.id) AS AggregateValue ". + "SELECT COUNT(tag.id) AS `value` ". "FROM `tag` ". "JOIN `post_tag` ON tag.id = post_tag.tag_id AND post_tag.post_id = '1' AND post_tag.deleted = 0 ". "WHERE tag.deleted = 0"; - $return = new MockDBResult(array( - array( - 'AggregateValue' => 1, - ), - )); + $return = new MockDBResult([ + [ + 'value' => 1, + ], + ]); $this->mockQuery($query, $return); $this->post->id = '1'; @@ -516,11 +516,11 @@ class DBMapperTest extends \PHPUnit\Framework\TestCase public function testMax() { - $query = "SELECT MAX(post.id) AS AggregateValue FROM `post` WHERE post.deleted = 0"; + $query = "SELECT MAX(post.id) AS `value` FROM `post` WHERE post.deleted = 0"; $return = new MockDBResult(array( - array ( - 'AggregateValue' => 10, - ) + [ + 'value' => 10, + ] )); $this->mockQuery($query, $return);