AnnotationMapper::getEntityMapping()   D
last analyzed

Complexity

Conditions 18
Paths 17

Size

Total Lines 82
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 43
CRAP Score 18.1996

Importance

Changes 0
Metric Value
cc 18
eloc 46
nc 17
nop 1
dl 0
loc 82
ccs 43
cts 47
cp 0.9149
crap 18.1996
rs 4.8666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace SpareParts\Pillar\Mapper\Dibi;
3
4
use Doctrine\Common\Annotations\Reader;
5
use SpareParts\Pillar\Entity\IEntity;
6
use SpareParts\Pillar\Mapper\Annotation\Column;
7
use SpareParts\Pillar\Mapper\Annotation\IPillarAnnotation;
8
use SpareParts\Pillar\Mapper\Annotation\Table;
9
use SpareParts\Pillar\Mapper\Annotation\VirtualEntity;
10
use SpareParts\Pillar\Mapper\EntityMappingException;
11
use SpareParts\Pillar\Mapper\IMapper;
12
13
class AnnotationMapper implements IMapper
14
{
15
	/**
16
	 * @var Reader
17
	 */
18
	private $annotationReader;
19
20
	/**
21
	 * @var IEntityMapping[]
22
	 */
23
	private $dibiMappingCache = [];
24
25
	/**
26
	 * Mapper constructor.
27
	 *
28
	 * @param Reader $annotationReader
29
	 */
30 6
	public function __construct(Reader $annotationReader)
31
	{
32 6
		$this->annotationReader = $annotationReader;
33 6
	}
34
35
	/**
36
	 * @param string|IEntity $classnameOrInstance
37
	 * @return IEntityMapping
38
	 * @throws EntityMappingException
39
	 */
40 6
	public function getEntityMapping($classnameOrInstance)
41
	{
42 6
		$className = $classnameOrInstance;
43 6
		if (is_object($classnameOrInstance)) {
44 2
			if (!($classnameOrInstance instanceof IEntity)) {
0 ignored issues
show
introduced by
$classnameOrInstance is always a sub-type of SpareParts\Pillar\Entity\IEntity.
Loading history...
45
				throw new EntityMappingException(sprintf('Expected class implementing IEntity interface, got %s instead', get_class($classnameOrInstance)));
46
			}
47 2
			$className = get_class($classnameOrInstance);
48
		}
49
50 6
		if (!isset($this->dibiMappingCache[$className])) {
51 6
			$class = new \ReflectionClass($className);
52 6
			$tableInfoList = [];
53 6
			$isVirtualEntity = false;
54
55 6
			foreach ($this->annotationReader->getClassAnnotations($class) as $classAnnotation) {
56 6
				if (!($classAnnotation instanceof IPillarAnnotation)) {
57
					continue;
58
				}
59
60 6
				if ($classAnnotation instanceof Table) {
61 6
					$identifier = $classAnnotation->getIdentifier() ?: $classAnnotation->getName();
62 6
					$tableInfoList[$identifier] = new TableInfo(
63 6
						$classAnnotation->getName(),
64 6
						$identifier,
65 6
						$classAnnotation->getCode()
66
					);
67
				}
68
69 6
				if ($classAnnotation instanceof VirtualEntity) {
70 6
					$isVirtualEntity = true;
71
				}
72
			}
73
74 6
			$columnInfoList = [];
75 6
			foreach ($class->getProperties() as $property) {
76 6
				$enabledForSelect = true;
77
				// null means this property is not mapped to ANY table = probably not mapped column - ignore it.
78
				// true means this property is mapped to a table, and that table was not used - exception
79
				// false means this property is mapped to a table and at least one of those tables is used - ok
80 6
				$danglingProperty = null;
81 6
				foreach ($this->annotationReader->getPropertyAnnotations($property) as $propertyAnnotation) {
82 6
					if (!($propertyAnnotation instanceof Column)) {
83
						continue;
84
					}
85 6
					if (is_null($danglingProperty)) {
86 6
						$danglingProperty = true;
87
					}
88
89 6
					if (!isset($tableInfoList[$propertyAnnotation->getTable()])) {
90
						// this is possibly not a mistake - property may have multiple Column annotations, and not be using all at once in the current entity
91
						continue;
92
//						throw new EntityMappingException(sprintf('Entity :`%s` property: `%s` is mapped to table identified as: `%s`, but no such table identifier is present.', $className, $property->getName(), $propertyAnnotation->getTable()));
93
					}
94 6
					$danglingProperty = false;
95
96 6
					$columnInfoList[] = new ColumnInfo(
97 6
						$propertyAnnotation->getName() ?: $property->getName(),
98 6
						$property->getName(),
99 6
						$tableInfoList[$propertyAnnotation->getTable()],
100 6
						$propertyAnnotation->isPrimary(),
101 6
						$propertyAnnotation->isDeprecated(),
102 6
						$enabledForSelect,
103 6
						$propertyAnnotation->getCustomSelect()
0 ignored issues
show
Bug introduced by
It seems like $propertyAnnotation->getCustomSelect() can also be of type string; however, parameter $customSelectSql of SpareParts\Pillar\Mapper...lumnInfo::__construct() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

103
						/** @scrutinizer ignore-type */ $propertyAnnotation->getCustomSelect()
Loading history...
104
					);
105
					// only first @column annotation should be used for selecting
106
					// all following @column are there for saving/updating
107 6
					$enabledForSelect = false;
108
				}
109
110
				// dangling property = property which will never be selected, throw an exception
111
				// ignore dangling properties for abstract and virtual entities
112 6
				if ($danglingProperty === true && !$class->isAbstract() && !$isVirtualEntity) {
113 6
					throw new EntityMappingException(sprintf('Entity: `%s` has property `%s` mapped to tables, but none of those tables are used in the entity. Maybe you forgot to use the table in the select?', $className, $property->getName()));
0 ignored issues
show
Bug introduced by
It seems like $className can also be of type SpareParts\Pillar\Entity\IEntity; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

113
					throw new EntityMappingException(sprintf('Entity: `%s` has property `%s` mapped to tables, but none of those tables are used in the entity. Maybe you forgot to use the table in the select?', /** @scrutinizer ignore-type */ $className, $property->getName()));
Loading history...
114
				}
115
			}
116
117 6
			$this->dibiMappingCache[$className] = new EntityMapping(
118 6
				$className, $tableInfoList, $columnInfoList, $isVirtualEntity
0 ignored issues
show
Bug introduced by
It seems like $className can also be of type SpareParts\Pillar\Entity\IEntity; however, parameter $entityClassName of SpareParts\Pillar\Mapper...yMapping::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

118
				/** @scrutinizer ignore-type */ $className, $tableInfoList, $columnInfoList, $isVirtualEntity
Loading history...
119
			);
120
		}
121 6
		return $this->dibiMappingCache[$className];
122
	}
123
}
124