1
|
|
|
<?php
|
2
|
|
|
/**
|
3
|
|
|
* @author Alexey Tatarinov <[email protected]>
|
4
|
|
|
* @link https://github.com/shogodev/argilla/
|
5
|
|
|
* @copyright Copyright © 2003-2014 Shogo
|
6
|
|
|
* @license http://argilla.ru/LICENSE
|
7
|
|
|
* @package frontend.components.collection
|
8
|
|
|
* @property string $key
|
9
|
|
|
* @property string $type
|
10
|
|
|
* @property string $primaryKey
|
11
|
|
|
*/
|
12
|
|
|
class FCollectionElement extends CComponent implements JsonSerializable
|
13
|
|
|
{
|
14
|
|
|
const CUSTOM = 'customElement';
|
15
|
|
|
|
16
|
|
|
const COLLECTION = 'innerCollection';
|
17
|
|
|
|
18
|
|
|
public $amount;
|
19
|
|
|
|
20
|
|
|
public $items;
|
21
|
|
|
|
22
|
|
|
public $index;
|
23
|
|
|
|
24
|
|
|
/**
|
25
|
|
|
* @var FCollection
|
26
|
|
|
*/
|
27
|
|
|
public $collectionParent;
|
28
|
|
|
|
29
|
|
|
private $key;
|
30
|
|
|
|
31
|
|
|
private $primaryKey;
|
32
|
|
|
|
33
|
|
|
private $type;
|
34
|
|
|
|
35
|
|
|
private $object;
|
36
|
|
|
|
37
|
|
|
private $compareByItemKeys;
|
38
|
|
|
|
39
|
20 |
|
public function __construct($data, $compareByItemKeys = array(), $rootCollection = null)
|
40
|
|
|
{
|
41
|
20 |
|
$this->type = $data['type'];
|
42
|
20 |
|
$this->primaryKey = $data['id'];
|
43
|
|
|
|
44
|
20 |
|
if( isset($data['key']) )
|
45
|
20 |
|
{
|
46
|
14 |
|
$this->key = $data['key'];
|
47
|
14 |
|
}
|
48
|
|
|
|
49
|
20 |
|
$this->amount = isset($data['amount']) ? intval($data['amount']) : 1;
|
50
|
|
|
|
51
|
20 |
|
if( isset($data['items']) )
|
52
|
20 |
|
$this->items = $this->buildItems($data['items'], $rootCollection);
|
53
|
|
|
|
54
|
20 |
|
$this->compareByItemKeys = $compareByItemKeys;
|
55
|
20 |
|
}
|
56
|
|
|
|
57
|
18 |
|
public function validate()
|
58
|
|
|
{
|
59
|
18 |
|
if( in_array($this->type, array(self::COLLECTION, self::CUSTOM)) )
|
60
|
18 |
|
return true;
|
61
|
|
|
|
62
|
18 |
|
if( $this->createObject() )
|
63
|
17 |
|
return true;
|
64
|
|
|
|
65
|
|
|
return false;
|
66
|
|
|
}
|
67
|
|
|
|
68
|
|
|
/**
|
69
|
|
|
* @param FCollectionElement $element
|
70
|
|
|
*
|
71
|
|
|
* @return bool
|
72
|
|
|
*/
|
73
|
17 |
|
public function merge($element)
|
74
|
|
|
{
|
75
|
17 |
|
if( $element->index === $this->index )
|
76
|
17 |
|
return false;
|
77
|
|
|
|
78
|
12 |
|
if( $this->compare($element) && $this->compareItems($this->items, $element->items, $this->compareByItemKeys) )
|
79
|
12 |
|
{
|
80
|
4 |
|
$this->amount += $element->amount;
|
81
|
4 |
|
$this->invalidate();
|
82
|
|
|
|
83
|
4 |
|
return true;
|
84
|
|
|
}
|
85
|
|
|
|
86
|
12 |
|
return false;
|
87
|
|
|
}
|
88
|
|
|
|
89
|
|
|
/**
|
90
|
|
|
* @param FCollectionElement $element
|
91
|
|
|
*
|
92
|
|
|
* @return bool
|
93
|
|
|
*/
|
94
|
15 |
|
public function compare($element)
|
95
|
|
|
{
|
96
|
15 |
|
if( $this->primaryKey == $element->primaryKey && $this->type == $element->type )
|
97
|
15 |
|
{
|
98
|
8 |
|
return true;
|
99
|
|
|
}
|
100
|
|
|
|
101
|
11 |
|
return false;
|
102
|
|
|
}
|
103
|
|
|
|
104
|
4 |
|
public function invalidate()
|
105
|
|
|
{
|
106
|
4 |
|
$this->object = null;
|
107
|
4 |
|
}
|
108
|
|
|
|
109
|
14 |
|
public function getObject()
|
110
|
|
|
{
|
111
|
14 |
|
if( is_null($this->object) )
|
112
|
14 |
|
{
|
113
|
14 |
|
if( $this->type == self::CUSTOM )
|
114
|
14 |
|
{
|
115
|
11 |
|
$this->object = $this->primaryKey;
|
116
|
11 |
|
}
|
117
|
13 |
|
else if( $this->type == self::COLLECTION )
|
118
|
13 |
|
{
|
119
|
12 |
|
$this->object = $this->items;
|
120
|
12 |
|
}
|
121
|
|
|
else
|
122
|
|
|
{
|
123
|
9 |
|
$this->object = $this->createObject();
|
124
|
|
|
}
|
125
|
14 |
|
}
|
126
|
|
|
|
127
|
14 |
|
return $this->object;
|
128
|
|
|
}
|
129
|
|
|
|
130
|
16 |
|
public function jsonSerialize()
|
131
|
|
|
{
|
132
|
16 |
|
if( in_array($this->type, array(self::CUSTOM, self::COLLECTION)) )
|
133
|
16 |
|
{
|
134
|
13 |
|
$data = $this->getObject();
|
135
|
13 |
|
}
|
136
|
|
|
else
|
137
|
|
|
{
|
138
|
|
|
$data = array(
|
139
|
16 |
|
'id' => $this->primaryKey,
|
140
|
16 |
|
'type' => $this->type,
|
141
|
16 |
|
'amount' => $this->amount,
|
142
|
16 |
|
);
|
143
|
|
|
|
144
|
16 |
|
if( !is_null($this->items) )
|
145
|
16 |
|
$data['items'] = $this->items;
|
146
|
|
|
}
|
147
|
|
|
|
148
|
16 |
|
return $data;
|
149
|
|
|
}
|
150
|
|
|
|
151
|
14 |
|
public function buildItems($items, $rootCollection)
|
152
|
|
|
{
|
153
|
14 |
|
$preparedItems = $this->getPreparedItems($items, $rootCollection);
|
154
|
|
|
|
155
|
14 |
|
$collectionItems = new FCollection('items', $this->compareByItemKeys, false, $rootCollection);
|
156
|
|
|
|
157
|
14 |
|
foreach($preparedItems as $item)
|
158
|
|
|
{
|
159
|
14 |
|
$collectionItems->addInner($item);
|
160
|
14 |
|
}
|
161
|
|
|
|
162
|
14 |
|
return $collectionItems;
|
163
|
|
|
}
|
164
|
|
|
|
165
|
|
|
public function toArray()
|
166
|
|
|
{
|
167
|
|
|
return json_decode(json_encode($this->jsonSerialize()), true);
|
168
|
|
|
}
|
169
|
|
|
|
170
|
|
|
protected function getType()
|
171
|
|
|
{
|
172
|
|
|
return $this->type;
|
173
|
|
|
}
|
174
|
|
|
|
175
|
17 |
|
protected function getKey()
|
176
|
|
|
{
|
177
|
17 |
|
return $this->key;
|
178
|
|
|
}
|
179
|
|
|
|
180
|
|
|
protected function getPrimaryKey()
|
181
|
|
|
{
|
182
|
|
|
return $this->primaryKey;
|
183
|
|
|
}
|
184
|
|
|
|
185
|
14 |
|
private function getPreparedItems($items, $rootCollection)
|
186
|
|
|
{
|
187
|
14 |
|
$preparedItems = array();
|
188
|
|
|
|
189
|
14 |
|
foreach($items as $key => $item)
|
190
|
|
|
{
|
191
|
14 |
|
if( $this->isObject($item) )
|
192
|
14 |
|
{
|
193
|
13 |
|
$preparedItems[$key] = $item;
|
194
|
|
|
|
195
|
13 |
|
if( !is_numeric($key) )
|
196
|
13 |
|
$preparedItems[$key]['key'] = $key;
|
197
|
|
|
|
198
|
13 |
|
if( isset($item['items']) )
|
199
|
13 |
|
$item['items'] = $this->buildItems($item['items'], $rootCollection);
|
200
|
13 |
|
}
|
201
|
14 |
|
else if( is_array($item) && $this->isObject(reset($item)) )
|
202
|
14 |
|
{
|
203
|
13 |
|
$preparedItems[$key] = array(
|
204
|
13 |
|
'id' => $key,
|
205
|
13 |
|
'key' => $key,
|
206
|
13 |
|
'type' => self::COLLECTION,
|
207
|
|
|
'items' => $item
|
208
|
13 |
|
);
|
209
|
13 |
|
}
|
210
|
|
|
else
|
211
|
|
|
{
|
212
|
11 |
|
$preparedItems[$key] = array(
|
213
|
11 |
|
'id' => $item,
|
214
|
11 |
|
'key' => $key,
|
215
|
|
|
'type' => self::CUSTOM
|
216
|
11 |
|
);
|
217
|
|
|
}
|
218
|
14 |
|
}
|
219
|
|
|
|
220
|
14 |
|
return $preparedItems;
|
221
|
|
|
}
|
222
|
|
|
|
223
|
18 |
|
private function createObject()
|
224
|
|
|
{
|
225
|
18 |
|
$className = Utils::toCamelCase($this->type);
|
226
|
|
|
|
227
|
|
|
/**
|
228
|
|
|
* @var FActiveRecord|FCollectionElementBehavior $model
|
229
|
|
|
*/
|
230
|
18 |
|
$modelClass = $className::model();
|
231
|
|
|
|
232
|
18 |
|
if( !($modelClass instanceof CModel) )
|
|
|
|
|
233
|
18 |
|
throw new CHttpException('500', 'Ошибка! Не удалось создать модель.');
|
234
|
|
|
|
235
|
18 |
|
if( !$modelClass->asa('collectionElement') )
|
236
|
18 |
|
throw new CHttpException('500', 'Ошибка! Не найдено поведение.');
|
237
|
|
|
|
238
|
17 |
|
$model = $modelClass->findByPk($this->primaryKey);
|
239
|
|
|
|
240
|
17 |
|
if( !$model )
|
241
|
17 |
|
return null;
|
242
|
|
|
|
243
|
17 |
|
$model->setCollectionElement($this);
|
244
|
17 |
|
$model->afterCreateCollection();
|
245
|
|
|
|
246
|
17 |
|
return $model;
|
247
|
|
|
}
|
248
|
|
|
|
249
|
14 |
|
private function isObject($data)
|
250
|
|
|
{
|
251
|
14 |
|
if( !is_array($data) )
|
252
|
14 |
|
return false;
|
253
|
|
|
|
254
|
13 |
|
if( !isset($data['id']) || !isset($data['type']) )
|
255
|
13 |
|
return false;
|
256
|
|
|
|
257
|
13 |
|
return true;
|
258
|
|
|
}
|
259
|
|
|
|
260
|
|
|
/**
|
261
|
|
|
* @param $collectionItems1
|
262
|
|
|
* @param $collectionItems2
|
263
|
|
|
* @param array $compareByItemKeys
|
264
|
|
|
*
|
265
|
|
|
* @return bool
|
266
|
|
|
*/
|
267
|
4 |
|
private function compareItems($collectionItems1, $collectionItems2, $compareByItemKeys)
|
268
|
|
|
{
|
269
|
4 |
|
if( empty($compareByItemKeys) )
|
270
|
4 |
|
return true;
|
271
|
|
|
|
272
|
4 |
|
if( empty($collectionItems1) && empty($collectionItems2) )
|
273
|
4 |
|
return true;
|
274
|
|
|
|
275
|
4 |
|
foreach($compareByItemKeys as $key)
|
276
|
|
|
{
|
277
|
4 |
|
if( !$this->compareItemsArray($collectionItems1, $collectionItems2, $key) )
|
278
|
4 |
|
return false;
|
279
|
4 |
|
}
|
280
|
|
|
|
281
|
4 |
|
return true;
|
282
|
|
|
}
|
283
|
|
|
|
284
|
4 |
|
private function compareItemsArray($items1, $items2, $itemName)
|
285
|
|
|
{
|
286
|
4 |
|
if( empty($items1[$itemName]) && empty($items2[$itemName]) )
|
287
|
4 |
|
return true;
|
288
|
|
|
|
289
|
4 |
|
if( !isset($items1[$itemName]) && !isset($items2[$itemName]) )
|
290
|
4 |
|
return true;
|
291
|
|
|
|
292
|
4 |
|
if( isset($items1[$itemName]) && !isset($items2[$itemName]) )
|
293
|
4 |
|
return false;
|
294
|
|
|
|
295
|
4 |
|
if( !isset($items1[$itemName]) && isset($items2[$itemName]) )
|
296
|
4 |
|
return false;
|
297
|
|
|
|
298
|
4 |
|
$dataForCompare1 = $this->preparationDataForCompare($items1[$itemName]);
|
299
|
4 |
|
$dataForCompare2 = $this->preparationDataForCompare($items2[$itemName]);
|
300
|
|
|
|
301
|
4 |
|
return $dataForCompare1 == $dataForCompare2;
|
302
|
|
|
}
|
303
|
|
|
|
304
|
4 |
|
private function preparationDataForCompare($data)
|
305
|
|
|
{
|
306
|
4 |
|
if( $data instanceof FCollection )
|
307
|
4 |
|
$data = $data;
|
|
|
|
|
308
|
|
|
|
309
|
4 |
|
if( is_object($data) )
|
310
|
4 |
|
$data = $data->toArray();
|
311
|
|
|
|
312
|
4 |
|
if( !is_array($data) )
|
313
|
4 |
|
return $data;
|
314
|
|
|
|
315
|
2 |
|
$ids = array();
|
316
|
2 |
|
foreach($data as $key => $item)
|
317
|
|
|
{
|
318
|
2 |
|
if( $this->isObject($item) )
|
319
|
2 |
|
{
|
320
|
2 |
|
$uniqueKey = $item['id'].'_'.$item['amount'];
|
321
|
2 |
|
$ids[$uniqueKey] = $uniqueKey;
|
322
|
2 |
|
}
|
323
|
|
|
else
|
324
|
|
|
{
|
325
|
1 |
|
if( is_array($item) )
|
326
|
1 |
|
asort($item);
|
327
|
|
|
|
328
|
1 |
|
$ids[$key] = $item;
|
329
|
|
|
}
|
330
|
2 |
|
}
|
331
|
|
|
|
332
|
2 |
|
ksort($ids);
|
333
|
|
|
|
334
|
2 |
|
return $ids;
|
335
|
|
|
}
|
336
|
|
|
} |
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.