markNestedElementsAsInsertedInFieldset()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 11
Ratio 100 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 11
loc 11
rs 9.4285
cc 2
eloc 6
nc 2
nop 2
1
<?php
2
/**
3
 * @link    https://github.com/nnx-framework/form-comparator
4
 * @author  Malofeykin Andrey  <[email protected]>
5
 */
6
namespace Nnx\FormComparator\Comparator;
7
8
9
use Nnx\FormComparator\Comparator\Diff\UpdateCollection;
10
use Webmozart\Assert\Assert;
11
use Zend\Form\Element\Collection;
12
use Zend\Form\ElementInterface;
13
use Zend\Form\FormInterface;
14
use Zend\Form\FieldsetInterface;
15
16
/**
17
 * Class FormDiffService
18
 *
19
 * @package Nnx\FormComparator\Comparator
20
 */
21
class FormDiffService
22
{
23
24
    /**
25
     * Форма которую сравнивают
26
     *
27
     * @var FormInterface
28
     */
29
    private $sourceForm;
30
31
    /**
32
     * Форма с которой сравнивают
33
     *
34
     * @var FormInterface
35
     */
36
    private $targetForm;
37
38
    /**
39
     * Результаты сравнения
40
     *
41
     * @var AbstractDiff[]
42
     */
43
    private $diff = [];
44
45
    /**
46
     * Определение элементов которые отличаются в формах
47
     *
48
     * @param FormInterface $sourceForm
49
     * @param FormInterface $targetForm
50
     *
51
     * @return array
52
     * @throws \Nnx\FormComparator\Comparator\Exception\DomainException
53
     * @throws \Nnx\FormComparator\Comparator\CollectionDiffService\Exception\RuntimeException
54
     * @throws \Nnx\FormComparator\Comparator\Exception\RuntimeException
55
     * @throws \Nnx\FormComparator\Comparator\Exception\IncorrectElementTypeException
56
     */
57
    public function buildDiff(FormInterface $sourceForm, FormInterface $targetForm)
58
    {
59
60
//        $sourceForm->get('sign')->get('signInfo')->add([
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
61
//            'name' => 'test',
62
//            'type' => 'datetime'
63
//        ]);
64
//
65
//        $targetForm->get('sign')->get('signInfo')->add([
66
//            'name' => 'test2',
67
//            'type' => 'datetime'
68
//        ]);
69
70
        $this->sourceForm = $sourceForm;
71
        $this->targetForm = $targetForm;
72
73
74
        $this->runBuildDiffFieldset($sourceForm, $targetForm);
75
        $diff = $this->diff;
76
        $this->diff = [];
77
78
        $this->sourceForm = null;
79
        $this->targetForm = null;
80
81
82
        return $diff;
83
    }
84
85
86
    /**
87
     * Запускает сравнение двух Fieldset'ов
88
     *
89
     * @param FieldsetInterface $sourceForm
90
     * @param FieldsetInterface $targetForm
91
     *
92
     * @return AbstractDiff[]
93
     * @throws \Nnx\FormComparator\Comparator\Exception\DomainException
94
     * @throws \Nnx\FormComparator\Comparator\CollectionDiffService\Exception\RuntimeException
95
     * @throws \Nnx\FormComparator\Comparator\Exception\IncorrectElementTypeException
96
     */
97
    protected function runBuildDiffFieldset(FieldsetInterface $sourceForm, FieldsetInterface $targetForm)
98
    {
99
        $prefixPath = $sourceForm->getName();
100
        $this->buildDiffFieldset($sourceForm, $targetForm, $prefixPath);
101
        $this->addNewElementInDiff($sourceForm, $targetForm, $prefixPath);
102
    }
103
104
105
    /**
106
     * Проверка того что элементы совпадают по типам.
107
     *
108
     * Т.е. если сравниваемый элемент является коллекцией, то и элемент с которым сравнивают должен быть коллекцией.
109
     * Если сравниваемый элемент является Fieldset'ом, то и элемент с которым сравнивают должен быть Fieldset'ом
110
     *
111
     * @param ElementInterface $sourceElement
112
     * @param ElementInterface $targetElement
113
     *
114
     * @throws \Nnx\FormComparator\Comparator\Exception\IncorrectElementTypeException
115
     */
116
    protected function validateElementType(ElementInterface $sourceElement, ElementInterface $targetElement)
117
    {
118
        $baseType = ElementInterface::class;
119
        if ($sourceElement instanceof Collection) {
120
            $baseType = Collection::class;
121
        } elseif ($sourceElement instanceof FieldsetInterface) {
122
            $baseType = FieldsetInterface::class;
123
        }
124
125
        if (!$targetElement instanceof $baseType) {
126
            $targetElementName = $targetElement->getName();
127
            $targetElementClass = get_class($targetElement);
128
            $errMsg = sprintf(
129
                'Element %s not implement %s',
130
                is_string($targetElementName) ? sprintf('%s(%s)', $targetElementName, $targetElementClass) : $targetElementClass,
131
                $baseType
132
            );
133
            throw new Exception\IncorrectElementTypeException($errMsg);
134
        }
135
    }
136
137
    /**
138
     * Подготавливает список изменнные элементов для двух Fieldset'ов формы
139
     *
140
     *
141
     * @param FieldsetInterface $sourceFieldset
142
     * @param FieldsetInterface $targetFieldset
143
     * @param                   $prefixPath
144
     *
145
     * @throws \Nnx\FormComparator\Comparator\Exception\IncorrectElementTypeException
146
     * @throws \Nnx\FormComparator\Comparator\CollectionDiffService\Exception\RuntimeException
147
     * @throws \Nnx\FormComparator\Comparator\Exception\DomainException
148
     */
149
    protected function buildDiffFieldset(FieldsetInterface $sourceFieldset, FieldsetInterface $targetFieldset, $prefixPath)
150
    {
151
        Assert::string($prefixPath);
152
        Assert::notEmpty($prefixPath);
153
154 View Code Duplication
        foreach ($sourceFieldset->getIterator() as $childSourceElementOrFieldset) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
155
            /** @var ElementInterface $childSourceElementOrFieldset */
156
            Assert::isInstanceOf($childSourceElementOrFieldset, ElementInterface::class);
157
            $childSourceElementOrFieldsetName = $childSourceElementOrFieldset->getName();
158
159
            $pathToElement = $this->buildPathToElementOrFieldset($childSourceElementOrFieldsetName, $prefixPath);
160
            if ($targetFieldset->has($childSourceElementOrFieldsetName)) {
161
                $childTargetElementOrFieldset = $targetFieldset->get($childSourceElementOrFieldsetName);
162
                $this->runDiffElementStrategy($childSourceElementOrFieldset, $childTargetElementOrFieldset, $pathToElement);
163
            } else {
164
                $this->runDeleteElementStrategy($childSourceElementOrFieldset, $pathToElement);
165
            }
166
        }
167
    }
168
169
    /**
170
     * @param ElementInterface $insertedElement
171
     * @param                  $prefixPath
172
     *
173
     * @throws \Nnx\FormComparator\Comparator\CollectionDiffService\Exception\RuntimeException
174
     * @throws \Nnx\FormComparator\Comparator\Exception\DomainException
175
     */
176 View Code Duplication
    protected function createInsertedElementDiff(ElementInterface $insertedElement, $prefixPath)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
177
    {
178
        $builder = $this->diffBuilderFactory(DiffElementBuilder::INSERT_ELEMENT_MODE);
179
        $builder->setPathToElement($prefixPath)
180
            ->setTargetElement($insertedElement)
181
            ->setSourceLabel($insertedElement->getLabel());
182
183
        $this->diff[] = $builder->build();
184
    }
185
186
187
    /**
188
     * Строит объект описывающий различия для двух изменныех элементов форм
189
     *
190
     * @param ElementInterface $sourceElement
191
     * @param ElementInterface $targetElement
192
     * @param                  $prefixPath
193
     *
194
     * @throws \Nnx\FormComparator\Comparator\CollectionDiffService\Exception\RuntimeException
195
     * @throws \Nnx\FormComparator\Comparator\Exception\DomainException
196
     */
197
    protected function buildDiffElementValue(ElementInterface $sourceElement, ElementInterface $targetElement, $prefixPath)
198
    {
199
200
        $sourceElementValue = $sourceElement->getValue();
201
        $targetElementValue = $targetElement->getValue();
202
203
        if ($sourceElementValue !== $targetElementValue) {
204
            $pathToElement = $this->buildPathToElementOrFieldset($sourceElement->getName(), $prefixPath);
205
206
            $builder = $this->diffBuilderFactory(DiffElementBuilder::UPDATE_ELEMENT_MODE);
207
208
            $builder->setPathToElement($pathToElement)
209
                ->setSourceLabel($sourceElement->getLabel())
210
                ->setSourceValue($sourceElementValue)
211
                ->setTargetValue($targetElementValue)
212
                ->setSourceElement($sourceElement)
213
                ->setTargetElement($targetElement);
214
215
            $this->diff[] = $builder->build();
216
        }
217
    }
218
219
220
    /**
221
     * @param FieldsetInterface $sourceFieldset
222
     * @param FieldsetInterface $targetFieldset
223
     * @param                   $prefixPath
224
     *
225
     * @throws \Nnx\FormComparator\Comparator\Exception\IncorrectElementTypeException
226
     * @throws \Nnx\FormComparator\Comparator\CollectionDiffService\Exception\RuntimeException
227
     * @throws \Nnx\FormComparator\Comparator\Exception\DomainException
228
     */
229
    protected function addNewElementInDiff(FieldsetInterface $sourceFieldset, FieldsetInterface $targetFieldset, $prefixPath)
230
    {
231 View Code Duplication
        foreach ($targetFieldset->getIterator() as $childTargetElementOrFieldset) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
232
            /** @var ElementInterface $childTargetElementOrFieldset */
233
            Assert::isInstanceOf($childTargetElementOrFieldset, ElementInterface::class);
234
235
            $childTargetFieldsetName = $childTargetElementOrFieldset->getName();
236
            $pathToElementOrFieldset = $this->buildPathToElementOrFieldset($childTargetFieldsetName, $prefixPath);
237
238
            if ($sourceFieldset->has($childTargetFieldsetName)) {
239
                /** @var FieldsetInterface $childSourceFieldset */
240
                $childSourceFieldset = $sourceFieldset->get($childTargetFieldsetName);
241
242
                if ($this->isRunAddNewElementInDiff($childSourceFieldset, $childTargetElementOrFieldset)) {
243
                    /** @var FieldsetInterface $childTargetElementOrFieldset */
244
                    $this->addNewElementInDiff($childSourceFieldset, $childTargetElementOrFieldset, $pathToElementOrFieldset);
245
                }
246
247
            } else {
248
                $this->runInsertedElementStrategy($childTargetElementOrFieldset, $pathToElementOrFieldset);
249
            }
250
        }
251
    }
252
253
    /**
254
     * Проверяет нужно ли продолжать добавление новых элементов
255
     *
256
     * @param ElementInterface $sourceElement
257
     * @param ElementInterface $targetElement
258
     *
259
     * @return bool
260
     * @throws \Nnx\FormComparator\Comparator\Exception\IncorrectElementTypeException
261
     */
262
    protected function isRunAddNewElementInDiff(ElementInterface $sourceElement, ElementInterface $targetElement)
263
    {
264
        $this->validateElementType($sourceElement, $targetElement);
265
266
        if ($sourceElement instanceof Collection && $targetElement instanceof Collection) {
267
            return false;
268
        } elseif ($sourceElement instanceof FieldsetInterface && $targetElement instanceof FieldsetInterface) {
269
            return true;
270
        }
271
272
        return false;
273
    }
274
275
    /**
276
     * Помечает вложенные элемента Fieldset'a как удаленные
277
     *
278
     * @param FieldsetInterface $deletedFieldset
279
     * @param                   $prefixPath
280
     *
281
     * @throws \Nnx\FormComparator\Comparator\CollectionDiffService\Exception\RuntimeException
282
     * @throws \Nnx\FormComparator\Comparator\Exception\DomainException
283
     */
284 View Code Duplication
    protected function markNestedElementsAsDeletedInFieldset(FieldsetInterface $deletedFieldset, $prefixPath)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
285
    {
286
        foreach ($deletedFieldset->getIterator() as $childElementOrFieldset) {
287
            /** @var ElementInterface $childElementOrFieldset */
288
            Assert::isInstanceOf($childElementOrFieldset, ElementInterface::class);
289
290
            $childElementOrFieldsetName = $childElementOrFieldset->getName();
291
            $pathToDeletedElement = $this->buildPathToElementOrFieldset($childElementOrFieldsetName, $prefixPath);
292
293
            $this->runDeleteElementStrategy($childElementOrFieldset, $pathToDeletedElement);
294
295
        }
296
    }
297
298
    /**
299
     * Помечает вложенные элемента Fieldset'a как добавленные
300
     *
301
     * @param FieldsetInterface $insertedFieldset
302
     * @param                   $prefixPath
303
     *
304
     * @throws \Nnx\FormComparator\Comparator\CollectionDiffService\Exception\RuntimeException
305
     * @throws \Nnx\FormComparator\Comparator\Exception\DomainException
306
     */
307 View Code Duplication
    protected function markNestedElementsAsInsertedInFieldset(FieldsetInterface $insertedFieldset, $prefixPath)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
308
    {
309
        foreach ($insertedFieldset->getIterator() as $childElementOrFieldset) {
310
            /** @var ElementInterface $childElementOrFieldset */
311
            Assert::isInstanceOf($childElementOrFieldset, ElementInterface::class);
312
313
            $childElementOrFieldsetName = $childElementOrFieldset->getName();
314
            $pathToInsertedElement = $this->buildPathToElementOrFieldset($childElementOrFieldsetName, $prefixPath);
315
            $this->runInsertedElementStrategy($childElementOrFieldset, $pathToInsertedElement);
316
        }
317
    }
318
319
320
    /**
321
     * Создает diff - указывающий на то что элемент был удален
322
     *
323
     * @param ElementInterface $deletedElement
324
     * @param                  $pathToDeletedElement
325
     *
326
     * @throws \Nnx\FormComparator\Comparator\CollectionDiffService\Exception\RuntimeException
327
     * @throws \Nnx\FormComparator\Comparator\Exception\DomainException
328
     */
329 View Code Duplication
    public function createDeleteElementDiff(ElementInterface $deletedElement, $pathToDeletedElement)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
330
    {
331
        $builder = $this->diffBuilderFactory(DiffElementBuilder::DELETE_ELEMENT_MODE);
332
        $builder->setPathToElement($pathToDeletedElement)
333
            ->setSourceElement($deletedElement)
334
            ->setSourceLabel($deletedElement->getLabel());
335
336
        $this->diff[] = $builder->build();
337
    }
338
339
340
    /**
341
     * Путь до элемента
342
     *
343
     * @param             $elementName
344
     * @param null|string $prefixPath
345
     *
346
     * @return string
347
     */
348
    protected function buildPathToElementOrFieldset($elementName, $prefixPath = null)
349
    {
350
        $pathToElement = $elementName;
351
        if (null !== $prefixPath) {
352
            $pathToElement = sprintf('%s.%s', $prefixPath, $elementName);
353
        }
354
355
        return $pathToElement;
356
    }
357
358
    /**
359
     * Создает билдер, используемый для того что бы построить объект в котором описываются различия между элементами
360
     * формы
361
     *
362
     * @param $mode
363
     *
364
     * @return DiffElementBuilder
365
     */
366
    protected function diffBuilderFactory($mode)
367
    {
368
        $builder = new DiffElementBuilder($mode);
369
        $builder->setSourceForm($this->sourceForm)
370
            ->setTargetForm($this->targetForm);
371
372
        return $builder;
373
    }
374
375
    /**
376
     * Определяет стратегию сравнения в зависимости от типа элемента
377
     *
378
     * @param ElementInterface $sourceElement
379
     * @param ElementInterface $targetElement
380
     * @param                  $prefixPath
381
     *
382
     * @throws \Nnx\FormComparator\Comparator\Exception\IncorrectElementTypeException
383
     * @throws \Nnx\FormComparator\Comparator\CollectionDiffService\Exception\RuntimeException
384
     * @throws \Nnx\FormComparator\Comparator\Exception\DomainException
385
     */
386
    protected function runDiffElementStrategy(ElementInterface $sourceElement, ElementInterface $targetElement, $prefixPath)
387
    {
388
        $this->validateElementType($sourceElement, $targetElement);
389
390
        if ($sourceElement instanceof Collection && $targetElement instanceof Collection) {
391
392
            $builder = $this->diffBuilderFactory(DiffElementBuilder::UPDATE_COLLECTION_MODE);
393
            $builder->setSourceElement($sourceElement)
394
                ->setTargetElement($targetElement)
395
                ->setSourceLabel($sourceElement->getLabel())
396
                ->setPathToElement($prefixPath);
397
398
            $diff = $builder->build();
399
            if ($diff instanceof UpdateCollection && count($diff->getDiff()) !== 0) {
400
                $this->diff[] = $builder->build();
401
            }
402
        } elseif ($sourceElement instanceof FieldsetInterface && $targetElement instanceof FieldsetInterface) {
403
            $this->buildDiffFieldset($sourceElement, $targetElement, $prefixPath);
404
        } else {
405
            $this->buildDiffElementValue($sourceElement, $targetElement, $prefixPath);
406
        }
407
    }
408
409
    /**
410
     * Определяет стратегию для создания объекта описывающего изменения связанные с удалением элемента из формы, в
411
     * зависимости от типа элемента
412
     *
413
     * @param ElementInterface $deletedElement
414
     * @param                  $prefixPath
415
     *
416
     * @throws \Nnx\FormComparator\Comparator\CollectionDiffService\Exception\RuntimeException
417
     * @throws \Nnx\FormComparator\Comparator\Exception\DomainException
418
     */
419 View Code Duplication
    protected function runDeleteElementStrategy(ElementInterface $deletedElement, $prefixPath)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
420
    {
421
        if ($deletedElement instanceof Collection) {
422
            $builder = $this->diffBuilderFactory(DiffElementBuilder::DELETE_COLLECTION_MODE);
423
            $builder->setSourceElement($deletedElement)
424
                ->setSourceLabel($deletedElement->getLabel())
425
                ->setPathToElement($prefixPath);
426
            $this->diff[] = $builder->build();
427
        } elseif ($deletedElement instanceof FieldsetInterface) {
428
            $this->markNestedElementsAsDeletedInFieldset($deletedElement, $prefixPath);
429
        } else {
430
            $this->createDeleteElementDiff($deletedElement, $prefixPath);
431
        }
432
    }
433
434
435
    /**
436
     * В зависимости от типа элемента определяет стратегию для создания объекта описывающего изменения связанные
437
     * с добавлением элемента в форму
438
     *
439
     * @param ElementInterface $insertedElement
440
     * @param                  $prefixPath
441
     *
442
     * @throws \Nnx\FormComparator\Comparator\CollectionDiffService\Exception\RuntimeException
443
     * @throws \Nnx\FormComparator\Comparator\Exception\DomainException
444
     */
445 View Code Duplication
    protected function runInsertedElementStrategy(ElementInterface $insertedElement, $prefixPath)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
446
    {
447
        if ($insertedElement instanceof Collection) {
448
            $builder = $this->diffBuilderFactory(DiffElementBuilder::INSERT_COLLECTION_MODE);
449
            $builder->setSourceElement($insertedElement)
450
                ->setSourceLabel($insertedElement->getLabel())
451
                ->setPathToElement($prefixPath);
452
            $this->diff[] = $builder->build();
453
        } elseif ($insertedElement instanceof FieldsetInterface) {
454
            $this->markNestedElementsAsInsertedInFieldset($insertedElement, $prefixPath);
455
        } else {
456
            $this->createInsertedElementDiff($insertedElement, $prefixPath);
457
        }
458
    }
459
}
460