Completed
Push — master ( de7e19...857622 )
by Ron
02:42
created

DiffStorageStore::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 1 Features 3
Metric Value
c 7
b 1
f 3
dl 0
loc 12
rs 9.4285
cc 1
eloc 11
nc 1
nop 9

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
namespace DataDiff;
3
4
use Generator;
5
use PDO;
6
use PDOStatement;
7
use Traversable;
8
9
class DiffStorageStore implements DiffStorageStoreInterface {
10
	/** @var PDO */
11
	private $pdo;
12
	/** @var PDOStatement */
13
	private $insertStmt;
14
	/** @var PDOStatement */
15
	private $selectStmt;
16
	/** @var PDOStatement */
17
	private $updateStmt;
18
	/** @var string */
19
	private $storeA;
20
	/** @var string */
21
	private $storeB;
22
	/** @var int */
23
	private $counter = 0;
24
	/** @var callable */
25
	private $duplicateKeyHandler;
26
	/** @var array */
27
	private $converter;
28
	/** @var string[] */
29
	private $keys;
30
	/** @var string[] */
31
	private $valueKeys;
32
33
	/**
34
	 * @param PDO $pdo
35
	 * @param string $keySchema
36
	 * @param string $valueSchema
37
	 * @param string[] $keys
38
	 * @param string[] $valueKeys
39
	 * @param array $converter
40
	 * @param string $storeA
41
	 * @param string $storeB
42
	 * @param callable $duplicateKeyHandler
43
	 */
44
	public function __construct(PDO $pdo, $keySchema, $valueSchema, array $keys, array $valueKeys, array $converter, $storeA, $storeB, $duplicateKeyHandler) {
45
		$this->pdo = $pdo;
46
		$this->selectStmt = $this->pdo->prepare("SELECT s_data FROM data_store WHERE s_ab='{$storeA}' AND s_key={$keySchema} AND (1=1 OR s_value={$valueSchema})");
47
		$this->insertStmt = $this->pdo->prepare("INSERT INTO data_store (s_ab, s_key, s_value, s_data, s_sort) VALUES ('{$storeA}', {$keySchema}, {$valueSchema}, :___data, :___sort)");
48
		$this->updateStmt = $this->pdo->prepare("UPDATE data_store SET s_value={$valueSchema}, s_data=:___data WHERE s_ab='{$storeA}' AND s_key={$keySchema}");
49
		$this->storeA = $storeA;
50
		$this->storeB = $storeB;
51
		$this->keys = $keys;
52
		$this->valueKeys = $valueKeys;
53
		$this->converter = $converter;
54
		$this->duplicateKeyHandler = $duplicateKeyHandler;
55
	}
56
57
	/**
58
	 * @param array $data
59
	 * @param array $translation
60
	 * @param callable $duplicateKeyHandler
61
	 */
62
	public function addRow(array $data, array $translation = null, $duplicateKeyHandler = null) {
63
		$data = $this->translate($data, $translation);
64
		if($duplicateKeyHandler === null) {
65
			$duplicateKeyHandler = $this->duplicateKeyHandler;
66
		}
67
		$buildMetaData = function (array $data, array $keys) {
68
			$metaData = $data;
69
			$metaData = array_diff_key($metaData, array_diff_key($metaData, $keys));
70
			$metaData['___data'] = json_encode($data);
71
			$metaData['___sort'] = $this->counter;
72
			return $metaData;
73
		};
74
		try {
75
			$metaData = $buildMetaData($data, $this->converter);
76
			$this->insertStmt->execute($metaData);
77
		} catch (\PDOException $e) {
78
			if(strpos($e->getMessage(), 'UNIQUE constraint failed') !== false) {
79
				$metaData = $buildMetaData($data, $this->converter);
80
				unset($metaData['___data']);
81
				unset($metaData['___sort']);
82
				$this->selectStmt->execute($metaData);
83
				$oldData = $this->selectStmt->fetch(PDO::FETCH_COLUMN, 0);
84
				if($oldData === null) {
85
					$oldData = [];
86
				} else {
87
					$oldData = json_decode($oldData, true);
88
				}
89
				$data = $duplicateKeyHandler($data, $oldData);
90
				$metaData = $buildMetaData($data, $this->converter);
91
				unset($metaData['___sort']);
92
				$this->updateStmt->execute($metaData);
93
			} else {
94
				throw $e;
95
			}
96
		}
97
	}
98
99
	/**
100
	 * @param Traversable|array $rows
101
	 * @param array $translation
102
	 * @param callable $duplicateKeyHandler
103
	 * @return $this
104
	 */
105
	public function addRows($rows, array $translation = null, $duplicateKeyHandler = null) {
106
		foreach($rows as $row) {
107
			$this->addRow($row, $translation, $duplicateKeyHandler);
108
		}
109
		return $this;
110
	}
111
112
	/**
113
	 * Returns true whenever there is any changed, added or removed data available
114
	 */
115
	public function hasAnyChanges() {
116
		/** @noinspection PhpUnusedLocalVariableInspection */
117
		foreach($this->getNewOrChanged() as $_) {
118
			return true;
119
		}
120
		/** @noinspection PhpUnusedLocalVariableInspection */
121
		foreach($this->getMissing() as $_) {
122
			return true;
123
		}
124
		return false;
125
	}
126
127
	/**
128
	 * Get all rows, that are present in this store, but not in the other
129
	 *
130
	 * @return Generator|DiffStorageStoreRow[]
131
	 */
132
	public function getNew() {
133
		return $this->query('
134
			SELECT
135
				s1.s_key AS k,
136
				s1.s_data AS d,
137
				s2.s_data AS f
138
			FROM
139
				data_store AS s1
140
			LEFT JOIN
141
				data_store AS s2 ON s2.s_ab = :sB AND s1.s_key = s2.s_key
142
			WHERE
143
				s1.s_ab = :sA
144
				AND
145
				s2.s_ab IS NULL
146
			ORDER BY
147
				s1.s_sort
148
		');
149
	}
150
151
	/**
152
	 * Get all rows, that have a different value hash in the other store
153
	 *
154
	 * @return Generator|DiffStorageStoreRow[]
155
	 */
156
	public function getChanged() {
157
		return $this->query('
158
			SELECT
159
				s1.s_key AS k,
160
				s1.s_data AS d,
161
				s2.s_data AS f
162
			FROM
163
				data_store AS s1
164
			INNER JOIN
165
				data_store AS s2 ON s2.s_ab = :sB AND s1.s_key = s2.s_key
166
			WHERE
167
				s1.s_ab = :sA
168
				AND
169
				s1.s_value != s2.s_value
170
			ORDER BY
171
				s1.s_sort
172
		');
173
	}
174
175
	/**
176
	 * @return Generator|DiffStorageStoreRow[]
177
	 */
178
	public function getNewOrChanged() {
179
		return $this->query('
180
			SELECT
181
				s1.s_key AS k,
182
				s1.s_data AS d,
183
				s2.s_data AS f
184
			FROM
185
				data_store AS s1
186
			LEFT JOIN
187
				data_store AS s2 ON s2.s_ab = :sB AND s1.s_key = s2.s_key
188
			WHERE
189
				s1.s_ab = :sA
190
				AND
191
				((s2.s_ab IS NULL) OR (s1.s_value != s2.s_value))
192
			ORDER BY
193
				s1.s_sort
194
		');
195
	}
196
197
	/**
198
	 * Get all rows, that are present in the other store, but not in this
199
	 *
200
	 * @return Generator|DiffStorageStoreRow[]
201
	 */
202
	public function getMissing() {
203
		return $this->query('
204
			SELECT
205
				s1.s_key AS k,
206
				s2.s_data AS d,
207
				s1.s_data AS f
208
			FROM
209
				data_store AS s1
210
			LEFT JOIN
211
				data_store AS s2 ON s2.s_ab = :sA AND s2.s_key = s1.s_key
212
			WHERE
213
				s1.s_ab = :sB
214
				AND
215
				s2.s_ab IS NULL
216
			ORDER BY
217
				s1.s_sort
218
		');
219
	}
220
221
	/**
222
	 * @return $this
223
	 */
224
	public function clearAll() {
225
		$stmt = $this->pdo->query('DELETE FROM data_store WHERE s_ab=:s');
226
		$stmt->execute(['s' => $this->storeA]);
227
		$stmt->closeCursor();
228
	}
229
230
	/**
231
	 * @param string $query
232
	 * @return Generator|DiffStorageStoreRow[]
233
	 */
234
	private function query($query) {
235
		$stmt = $this->pdo->query($query);
236
		$stmt->execute(['sA' => $this->storeA, 'sB' => $this->storeB]);
237
		while($row = $stmt->fetch(PDO::FETCH_NUM)) {
238
			$d = json_decode($row[1], true);
239
			$f = json_decode($row[2], true);
240
			yield $this->instantiateRow($d, $f);
241
		}
242
		$stmt->closeCursor();
243
	}
244
245
	/**
246
	 * @return Traversable|array[]
247
	 */
248
	public function getIterator() {
249
		$query = '
250
			SELECT
251
				s1.s_data AS d
252
			FROM
253
				data_store AS s1
254
			WHERE
255
				s1.s_ab = :s
256
			ORDER BY
257
				s1.s_sort
258
		';
259
		$stmt = $this->pdo->query($query);
260
		$stmt->execute(['s' => $this->storeA]);
261
		while($row = $stmt->fetch(PDO::FETCH_NUM)) {
262
			$row = json_decode($row[0], true);
263
			$row = $this->instantiateRow($row, []);
264
			yield $row->getData();
265
		}
266
		$stmt->closeCursor();
267
	}
268
269
	/**
270
	 * @param array $data
271
	 * @param array $translation
272
	 * @return array
273
	 */
274
	private function translate(array $data, array $translation = null) {
275
		if($translation !== null) {
276
			$result = [];
277
			foreach($data as $key => $value) {
278
				if(array_key_exists($key, $translation)) {
279
					$key = $translation[$key];
280
				}
281
				$result[$key] = $value;
282
			}
283
			return $result;
284
		}
285
		return $data;
286
	}
287
288
	/**
289
	 * @return int
290
	 */
291
	public function count() {
292
		$query = '
293
			SELECT
294
				COUNT(*)
295
			FROM
296
				data_store AS s1
297
			WHERE
298
				s1.s_ab = :s
299
		';
300
		$stmt = $this->pdo->query($query);
301
		$stmt->execute(['s' => $this->storeA]);
302
		$count = $stmt->fetch(PDO::FETCH_COLUMN, 0);
303
		return $count;
304
	}
305
306
	/**
307
	 * @param array $localData
308
	 * @param array $foreignData
309
	 * @return DiffStorageStoreRow
310
	 */
311
	private function instantiateRow(array $localData = null, array $foreignData = null) {
312
		return new DiffStorageStoreRow($localData, $foreignData, $this->keys, $this->valueKeys, $this->converter);
313
	}
314
}
315