Fluent::selectEntityProperties()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 6.0585

Importance

Changes 0
Metric Value
cc 6
eloc 17
nc 6
nop 0
dl 0
loc 29
ccs 15
cts 17
cp 0.8824
crap 6.0585
rs 9.0777
c 0
b 0
f 0
1
<?php
2
namespace SpareParts\Pillar\Assistant\Dibi;
3
4
use SpareParts\Enum\Converter\MapConverter;
5
use SpareParts\Pillar\Assistant\Dibi\Sorting\ISorting;
6
use SpareParts\Pillar\Assistant\Dibi\Sorting\SortingDirectionEnum;
7
use SpareParts\Pillar\Mapper\Dibi\ColumnInfo;
8
use SpareParts\Pillar\Mapper\Dibi\IEntityMapping;
9
use SpareParts\Pillar\Mapper\Dibi\TableInfo;
10
11
class Fluent extends \Dibi\Fluent
12
{
13
	/**
14
	 * @var IEntityMapping
15
	 */
16
	protected $entityMapping;
17
18
	/**
19
	 * @var IEntityFactory
20
	 */
21
	protected $entityFactory;
22
23 4
	public function __construct(\Dibi\Connection $connection, IEntityMapping $entityMapping, IEntityFactory $entityFactory = null)
24
	{
25 4
		parent::__construct($connection);
26 4
		if ($entityFactory) {
27 1
			$this->setupResult('setRowFactory', function (array $data) use ($entityFactory, $entityMapping) {
28 1
				return $entityFactory->createEntity($entityMapping->getEntityClassName(), $data);
29 1
			});
30
		}
31
32 4
		$this->entityMapping = $entityMapping;
33 4
		$this->entityFactory = $entityFactory;
34 4
	}
35
36
	/**
37
	 * @return $this
38
	 */
39 1
	public function selectEntityProperties()
40
	{
41 1
		$propertyList = [];
42 1
		foreach ($this->entityMapping->getTables() as $table) {
43 1
			foreach ($this->entityMapping->getColumnsForTable($table->getIdentifier()) as $column) {
44
				// each property may be mapped to multiple columns, we are using mapping to the FIRST ACTIVE table and ignoring the rest
45 1
				if (isset($propertyList[$column->getPropertyName()])) {
46 1
					continue;
47
				}
48
				// do not select columns marked as "deprecated"
49 1
				if ($column->isDeprecated()) {
50
					continue;
51
				}
52
53
54 1
				$propertyList[$column->getPropertyName()] = true;
55
56 1
				if ($column->getCustomSelectSql()) {
57
					$this->select($column->getCustomSelectSql())->as($column->getPropertyName());
58
				} else {
59 1
					$this->select('%n', sprintf(
60 1
						'%s.%s',
61 1
						$column->getTableInfo()->getIdentifier(),
62 1
						$column->getColumnName()
63 1
					))->as($column->getPropertyName());
64
				}
65
			}
66
		}
67 1
		return $this;
68
	}
69
70
	/**
71
	 * @param string[] $propertyList If present, Pillar will try to include only those tables that are needed to select these properties. This might not work 100% of the times, mostly if there is a `silent mid-table` in the joins. In this case use @see $additionalTableList
72
	 * @param string[] $additionalTableList If present, those tables will be used instead of (default) all tables.
73
	 *
74
	 * @return $this
75
	 */
76 4
	public function fromEntityDataSources(array $propertyList = null, array $additionalTableList = null)
77
	{
78 4
		$tables = $this->entityMapping->getTables();
79
80 4
		$primaryTable = array_shift($tables);
81
82 4
		$propertyTables = [];
83 4
		if ($propertyList !== null) {
84 2
			$propertyTables = $this->preparePropertyTables($propertyList);
85
		}
86
87 4
		$additionalTables = [];
88 4
		if ($additionalTableList !== null) {
89
			$additionalTables = array_filter($tables, function (TableInfo $tableInfo) use ($additionalTableList) {
90
				return in_array($tableInfo->getIdentifier(), $additionalTableList);
91
			});
92
		}
93
94 4
		$innerJoinTables = array_filter($tables, function (TableInfo $tableInfo) {
95 4
			return (strtolower(substr($tableInfo->getSqlJoinCode(), 0, 5)) === 'inner');
96 4
		});
97
98 4
		if ($propertyList || $additionalTableList) {
99
			// in case I wish to restrict the result
100 2
			$tables = array_unique(array_merge($innerJoinTables, $propertyTables, $additionalTables));
101
		}
102
103
104 4
		$this->from(sprintf(
105 4
			'`%s` AS `%s`',
106 4
			$primaryTable->getName(),
107 4
			$primaryTable->getIdentifier()
108
		));
109
110 4
		foreach ($tables as $table) {
111 3
			$this->__call('', [$table->getSqlJoinCode()]);
112
		}
113 4
		return $this;
114
	}
115
116
	/**
117
	 * @param string[] $propertyList
118
	 * @return TableInfo[]
119
	 */
120
	private function preparePropertyTables(array $propertyList)
121
	{
122
		// tables that should not be important to select correct row (ie. left joins)
123
		/** @var TableInfo[] $optionalTables */
124 2
		$optionalTables = array_filter($this->entityMapping->getTables(), function (TableInfo $tableInfo) {
125 2
			return (strtolower(substr($tableInfo->getSqlJoinCode(), 0, 4)) === 'left');
126 2
		});
127
128 2
		$propertyTables = [];
129
		// find out which of those tables are important for the properties
130 2
		foreach ($optionalTables as $tableInfo) {
131 2
			$propertyInfoList = $this->entityMapping->getColumnsForTable($tableInfo->getIdentifier());
132
133 2
			$tablePropertyNames = array_map(function (ColumnInfo $columnInfo) {
134 2
				return $columnInfo->getPropertyName();
135 2
			}, $propertyInfoList);
136
137 2
			if (count(array_intersect($tablePropertyNames, $propertyList))) {
138 2
				$propertyTables[] = $tableInfo;
139
			}
140
		}
141 2
		return $propertyTables;
142
	}
143
144
	/**
145
	 * @param ISorting[] $sortingList
146
	 * @return $this
147
	 * @throws UnknownPropertyException
148
	 * @throws \SpareParts\Enum\Converter\UnableToConvertException
149
	 */
150
	public function applySorting(array $sortingList)
151
	{
152
		if (count($sortingList) === 0) {
153
			// don't try to apply empty $sortingList
154
			return $this;
155
		}
156
157
		/** @var ColumnInfo[] $sortableProperties */
158
		$sortableProperties = [];
159
		foreach ($this->entityMapping->getTables() as $tableInfo) {
160
			foreach ($this->entityMapping->getColumnsForTable($tableInfo->getIdentifier()) as $columnInfo) {
161
				if (isset($sortableProperties[$columnInfo->getPropertyName()])) {
162
					continue;
163
				}
164
				$sortableProperties[$columnInfo->getPropertyName()] = $columnInfo;
165
			}
166
		}
167
		$directionMap = new MapConverter([
168
			'ASC' => SortingDirectionEnum::ASCENDING(),
169
			'DESC' => SortingDirectionEnum::DESCENDING(),
170
		]);
171
172
		foreach ($sortingList as $sorting) {
173
			if (!isset($sortableProperties[$sorting->getProperty()])) {
174
				throw new UnknownPropertyException(sprintf('Unable to map property: `%s` to entity: `%s`, please check whether the provided property name is correct.', $sorting->getProperty(), $this->entityMapping->getEntityClassName()));
175
			}
176
			$columnInfo = $sortableProperties[$sorting->getProperty()];
177
			$this->orderBy(
178
				'%n', sprintf(
179
					'%s.%s',
180
					$columnInfo->getTableInfo()->getIdentifier(),
181
					$columnInfo->getColumnName()
182
				),
183
				$directionMap->fromEnum($sorting->getDirection())
184
			);
185
		}
186
		return $this;
187
	}
188
}
189