EntitySnapshot::getGenerator()   F
last analyzed

Complexity

Conditions 15
Paths 380

Size

Total Lines 70
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 240

Importance

Changes 0
Metric Value
eloc 36
dl 0
loc 70
c 0
b 0
f 0
ccs 0
cts 51
cp 0
rs 2.8333
cc 15
nc 380
nop 0
crap 240

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
/**
3
 * YAWIK
4
 *
5
 * @filesource
6
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
7
 * @license   MIT
8
 * @author    [email protected]
9
 */
10
11
namespace Core\Controller\Plugin;
12
13
use Core\Entity\SnapshotGeneratorProviderInterface;
14
use Core\Service\SnapshotGenerator;
15
use Laminas\Mvc\Controller\Plugin\PluginInterface;
16
use Laminas\Stdlib\DispatchableInterface as Dispatchable;
17
use Laminas\Stdlib\ArrayUtils;
18
use Core\Entity\Hydrator\EntityHydrator;
19
use Core\Repository\RepositoryInterface;
20
21
/**
22
 * Class EntitySnapshot
23
 * @package Core\Controller\Plugin
24
 */
25
class EntitySnapshot implements PluginInterface
26
{
27
    /**
28
     * @var
29
     */
30
    protected $serviceLocator;
31
32
    /**
33
     * @var RepositoryInterface
34
     */
35
    protected $repositories;
36
37
    /**
38
     * @var array
39
     */
40
    protected $options;
41
42
    /**
43
     * @var
44
     */
45
    protected $entity;
46
47
    /**
48
     * @var SnapshotGenerator
49
     */
50
    protected $generator;
51
52
    /**
53
     * @param $serviceLocator
54
     * @return $this
55
     */
56
    public function setServiceLocator($serviceLocator)
57
    {
58
        $this->serviceLocator = $serviceLocator;
59
        return $this;
60
    }
61
62
    /**
63
     * @return mixed
64
     */
65
    public function getServiceLocator()
66
    {
67
        return $this->serviceLocator;
68
    }
69
70
    /**
71
     * @param $repositories
72
     * @return $this
73
     */
74
    public function setRepositories($repositories)
75
    {
76
        $this->repositories = $repositories;
77
        return $this;
78
    }
79
80
    /**
81
     * @return mixed
82
     */
83
    public function getRepositories()
84
    {
85
        return $this->repositories;
86
    }
87
88
    /**
89
     * @param $options
90
     * @return $this
91
     */
92
    public function setOptions($options)
93
    {
94
        $this->options = $options;
95
        return $this;
96
    }
97
98
    /**
99
     * @return mixed
100
     */
101
    public function getOptions()
102
    {
103
        return $this->options;
104
    }
105
106
    /**
107
     * @param null $entity
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $entity is correct as it would always require null to be passed?
Loading history...
108
     * @param array $options
109
     * @return $this
110
     */
111
    public function __invoke($entity = null, $options = array())
112
    {
113
        if (is_array($entity)) {
0 ignored issues
show
introduced by
The condition is_array($entity) is always false.
Loading history...
114
            $options = $entity;
115
            $entity = null;
116
        }
117
        $this->entity = $entity;
118
        $this->options = $options;
119
        $this->generator = null;
120
        if (!isset($entity)) {
121
            return $this;
122
        }
123
        $this->snapshot();
124
        return $this;
125
    }
126
127
    /**
128
     * @param null $entity
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $entity is correct as it would always require null to be passed?
Loading history...
129
     * @return $this
130
     */
131
    public function snapshot($entity = null)
132
    {
133
        if (isset($entity)) {
134
            $this->entity = $entity;
135
        }
136
        if (!isset($this->entity)) {
137
            // or throw an exception ? since we expect to get a snapshot
138
            return $this;
139
        }
140
141
        if ($this->entity instanceof SnapshotGeneratorProviderInterface) {
142
            $generator = $this->getGenerator();
143
            $data = $generator->getSnapshot();
0 ignored issues
show
Bug introduced by
The method getSnapshot() does not exist on Core\Service\SnapshotGenerator. Did you maybe mean getSnapshotAttributes()? ( Ignorable by Annotation )

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

143
            /** @scrutinizer ignore-call */ 
144
            $data = $generator->getSnapshot();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
144
145
            // snapshot-class
146
            $target = $this->getTarget();
147
148
            if (isset($target)) {
149
                $target->__invoke($data);
150
                $className = get_class($this->entity);
151
                // @TODO, have to be abstract
152
                $snapShotMetaClassName = '\\' . $className . 'SnapshotMeta';
153
                $meta = new $snapShotMetaClassName;
154
                $meta->setEntity($target);
155
                $meta->setSourceId($this->entity->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Core\Entity\SnapshotGeneratorProviderInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
156
                $this->getRepositories()->store($meta);
0 ignored issues
show
Bug introduced by
The method store() does not exist on Core\Repository\RepositoryInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Core\Repository\RepositoryInterface. ( Ignorable by Annotation )

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

156
                $this->getRepositories()->/** @scrutinizer ignore-call */ store($meta);
Loading history...
157
            }
158
        }
159
        return $this;
160
    }
161
162
    /**
163
     * shows the differences between the last snapshot and the given entity
164
     * return Null = there is no snapshot
165
     * return array() = there is a snapshot but no difference
166
     *
167
     * @param $entity
168
     * @return array|null
169
     */
170
    public function diff($entity)
171
    {
172
        if ($entity instanceof SnapshotGeneratorProviderInterface) {
173
            $this->entity = $entity;
174
            $generator = $this->getGenerator();
175
            $targetClass = $this->getTarget(false);
176
            $dataHead = $generator->getSnapshot();
177
            if (empty($dataHead) || empty($targetClass)) {
178
                return null;
179
            }
180
            $repositorySnapshotMeta = $this->getRepositories()->getRepository($targetClass . "Meta");
0 ignored issues
show
Bug introduced by
The method getRepository() does not exist on Core\Repository\RepositoryInterface. ( Ignorable by Annotation )

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

180
            $repositorySnapshotMeta = $this->getRepositories()->/** @scrutinizer ignore-call */ getRepository($targetClass . "Meta");

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
181
            $snapshot = $repositorySnapshotMeta->findSnapshot($this->entity);
182
            // an snapshot has to be so simple that there is no need for a special hydrator
183
            $hydrator = new EntityHydrator();
184
            $dataLast = $hydrator->extract($snapshot);
185
            if (empty($dataLast)) {
186
                // there is no Snapshot, but returning an empty array would make a wrong conclusion,
187
                // that there is a snapshot, and it has no differences.
188
                // actually, if there is a snapshot, it always differ (dateCreated)
189
                return null;
190
            }
191
            return $this->array_compare($dataLast, $dataHead);
192
        } else {
193
            // entity is not an implementation of SnapshotGeneratorProviderInterface
194
        }
195
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Core\Controller\Plugin\EntitySnapshot which is incompatible with the documented return type array|null.
Loading history...
196
    }
197
198
    /**
199
     * the Target is the snapshotMeta-Class
200
     *
201
     * @param bool $generateInstance    most of the time we need an instance of the snapshot
202
     *                                  but we need sometimes just the repository of the snapshotMeta,
203
     *                                  and we just want the className.
204
     *                                  If we make this parameter to False we just get the className
205
     * @return null|string
206
     */
207
    protected function getTarget($generateInstance = true)
208
    {
209
        $serviceLocator = $this->getServicelocator();
210
        // set the actual options
211
        $this->getGenerator();
212
        $target = null;
213
        if (array_key_exists('target', $this->options)) {
214
            $target = $this->options['target'];
215
            if (is_string($target)) {
216
                if ($serviceLocator->has($target)) {
217
                    $target = $serviceLocator->get($target);
218
                    if ($generateInstance) {
219
                        $target = get_class($target);
220
                    }
221
                } else {
222
                    if ($generateInstance) {
223
                        $target = new $target;
224
                    }
225
                }
226
            }
227
        }
228
        return $target;
229
    }
230
231
    /**
232
     * the generator transforms an entity into an array
233
     *
234
     * what a generator ought to do more than an hydrator is to unriddle all related data,
235
     * which can imply that from other entities there also a snapshot can be created
236
     * @return SnapshotGenerator|mixed|null
237
     */
238
    protected function getGenerator()
239
    {
240
        if (isset($this->generator)) {
241
            return $this->generator;
242
        }
243
244
        if ($this->entity instanceof SnapshotGeneratorProviderInterface) {
245
            $serviceLocator = $this->getServicelocator();
246
247
            // the snapshotgenerator is a service defined by the name of the entity
248
            // this is the highest means, all subsequent means just add what is not set
249
            $className = get_class($this->entity);
250
            if ($serviceLocator->has('snapshotgenerator' . $className)) {
251
                $generator = $this->serviceLocator->get('snapshotgenerator' . $className);
252
                if (is_array($generator)) {
253
                    $this->options = ArrayUtils::merge($generator, $this->options);
254
                    $generator = null;
255
                }
256
            }
257
258
            // the snapshotgenerator is provided by the entity
259
            // this can either be a generator-entity of a array with options
260
            if (!isset($generator)) {
261
                $generator = $this->entity->getSnapshotGenerator();
262
                if (is_array($generator)) {
263
                    $this->options = ArrayUtils::merge($generator, $this->options);
264
                    if (array_key_exists('generator', $generator)) {
265
                        $generator = $this->options['generator'];
266
                        unset($this->options['generator']);
267
                    } else {
268
                        $generator = null;
269
                    }
270
                }
271
                if (is_string($generator)) {
272
                    $generator = $serviceLocator->get($generator);
273
                }
274
            }
275
276
            // the last possibility to get a generator
277
            if (!isset($generator)) {
278
                // defaultGenerator
279
                $generator = new SnapshotGenerator();
280
            }
281
282
            // *** filling the options
283
            // hydrator
284
            // can be a class, but if it's a string, consider it to be an hydrator in the hydratormanager
285
            if (array_key_exists('hydrator', $this->options)) {
286
                $hydrator = $this->options['hydrator'];
287
                if (is_string($hydrator) && !empty($hydrator)) {
288
                    $hydrator = $serviceLocator->get('HydratorManager')->get($hydrator);
289
                }
290
                $generator->setHydrator($hydrator);
291
            }
292
293
            // exclude
294
            // add the elements, that should not be transferred
295
            if (array_key_exists('exclude', $this->options)) {
296
                // it is very likely that the hydrator is set by the snapshot-class,
297
                // so we have to asume, that may know the hydrator
298
                $hydrator = $generator->getHydrator();
299
                $exclude = $this->options['exclude'];
300
                if (is_array($exclude)) {
301
                    $hydrator->setExcludeMethods($exclude);
302
                }
303
            }
304
            $generator->setSource($this->entity);
305
            $this->generator = $generator;
306
        }
307
        return $this->generator;
308
    }
309
310
    /**
311
     * makes a recursiv difference between array1 and array2
312
     * found commands like  'array_diff_assoc' wanting
313
     *
314
     * the result looks like
315
     * key => array( old, new)
316
     * in subarrays it looks like
317
     * key.subkey = array( old, new)
318
     *
319
     * @param $array1
320
     * @param $array2
321
     * @param int $maxDepth
322
     * @return array
323
     */
324
    protected function array_compare($array1, $array2, $maxDepth = 2)
325
    {
326
        $result = array();
327
        $arraykeys = array_unique(array_merge(array_keys($array1), array_keys($array2)));
328
        foreach ($arraykeys as $key) {
329
            if (!empty($key) && is_string($key) && $key[0] != "\0" && substr($key, 0, 8) != 'Doctrine') {
330
                if (array_key_exists($key, $array1) && !array_key_exists($key, $array2)) {
331
                    $result[$key] = array($array1[$key], '');
332
                }
333
                if (!array_key_exists($key, $array1) && array_key_exists($key, $array2)) {
334
                    $result[$key] = array('', $array2[$key]);
335
                }
336
                if (array_key_exists($key, $array1) && array_key_exists($key, $array2)) {
337
                    $subResult = null;
338
                    if (is_array($array1[$key]) && is_array($array2[$key])) {
339
                        if (0 < $maxDepth) {
340
                            $subResult = $this->array_compare($array1[$key], $array2[$key], $maxDepth - 1);
341
                        }
342
                    } elseif (is_object($array1[$key]) && is_object($array2[$key])) {
343
                        if (0 < $maxDepth) {
344
                            $hydrator = new EntityHydrator();
345
                            $a1 = $hydrator->extract($array1[$key]);
346
                            $a2 = $hydrator->extract($array2[$key]);
347
                            $subResult = $this->array_compare($a1, $a2, $maxDepth - 1);
348
                        }
349
                    } else {
350
                        if ($array1[$key] != $array2[$key]) {
351
                            $result[$key] = array( $array1[$key], $array2[$key]);
352
                        }
353
                    }
354
                    if (!empty($subResult)) {
355
                        foreach ($subResult as $subKey => $subValue) {
356
                            if (!empty($subKey) && is_string($subKey)) {
357
                                $result[$key . '.' . $subKey] = $subValue;
358
                            }
359
                        }
360
                    }
361
                }
362
            }
363
        }
364
        return $result;
365
    }
366
367
368
    /**
369
     * @param Dispatchable $controller
370
     */
371
    public function setController(Dispatchable $controller)
372
    {
373
    }
374
375
    /**
376
     * @return null|void|Dispatchable
377
     */
378
    public function getController()
379
    {
380
    }
381
}
382