CollectionSubscriber   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 98
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 7
dl 0
loc 98
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A getSubscribedEvents() 0 7 1
C preFlush() 0 42 7
B prePersist() 0 32 5
1
<?php
2
3
namespace Psi\Bridge\ContentType\Doctrine\PhpcrOdm\Subscriber;
4
5
use Doctrine\Common\EventSubscriber;
6
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
7
use Doctrine\Common\Persistence\Event\ManagerEventArgs;
8
use Doctrine\Common\Util\ClassUtils;
9
use Doctrine\ODM\PHPCR\Event;
10
use Doctrine\ODM\PHPCR\Mapping\ClassMetadata;
11
use Metadata\MetadataFactory;
12
use Metadata\MetadataFactoryInterface;
13
use PHPCR\Util\PathHelper;
14
use Psi\Bridge\ContentType\Doctrine\PhpcrOdm\PropertyEncoder;
15
16
/**
17
 * This class provides exception messages for cases when the collection
18
 * identifier updater has not been invoked.
19
 *
20
 * It is purely for debugging purposes and may safely be disabled.
21
 */
22
class CollectionSubscriber implements EventSubscriber
23
{
24
    private $metadataFactory;
25
    private $encoder;
26
    private $stack = [];
27
28
    public function __construct(
29
        MetadataFactoryInterface $metadataFactory,
30
        PropertyEncoder $encoder
31
    ) {
32
        $this->metadataFactory = $metadataFactory;
33
        $this->encoder = $encoder;
34
    }
35
36
    public function getSubscribedEvents()
37
    {
38
        return [
39
            Event::prePersist,
40
            Event::preFlush,
41
        ];
42
    }
43
44
    public function preFlush(ManagerEventArgs $args)
45
    {
46
        if (empty($this->stack)) {
47
            return;
48
        }
49
50
        $metadataFactory = $args->getObjectManager()->getMetadataFactory();
51
52
        foreach ($this->stack as $data) {
53
            $children = $data['children'];
54
            $ctMetadata = $data['ct_metadata'];
55
            $childrenField = $data['field'];
56
57
            $index = 0;
58
59
            foreach ($children as $child) {
60
                $childMetadata = $metadataFactory->getMetadataFor(ClassUtils::getRealClass(get_class($child)));
61
                $expectedId = $this->encoder->encode($childrenField, $index++);
62
                $identifier = $childMetadata->getIdentifierValue($child);
63
                $idGenerator = $childMetadata->idGenerator;
64
65
                if ($idGenerator !== ClassMetadata::GENERATOR_TYPE_ASSIGNED) {
66
                    throw new \InvalidArgumentException(sprintf(
67
                        'Currently, all documents which belong to a mapped collection must use the ' .
68
                        'assigned ID generator strategy, "%s" is using "%s".',
69
                        $childMetadata->getName(), $idGenerator
70
                    ));
71
                }
72
73
                if (!$identifier || PathHelper::getNodeName($identifier) !== $expectedId) {
74
                    throw new \InvalidArgumentException(sprintf(
75
                        'Child mapped to content type "%s" on field "%s" has an unexpected ID "%s". ' .
76
                        'It is currently necessary to envoke the CollectionIdentifierUpdater on all ' .
77
                        'documents (at least those which have collections) before they are persisted.',
78
                        $ctMetadata->getType(),
79
                        $childrenField,
80
                        $identifier
81
                    ));
82
                }
83
            }
84
        }
85
    }
86
87
    public function prePersist(LifecycleEventArgs $args)
88
    {
89
        $document = $args->getObject();
90
        $metadataFactory = $args->getObjectManager()->getMetadataFactory();
91
        $odmMetadata = $metadataFactory->getMetadataFor(ClassUtils::getRealClass(get_class($document)));
92
93
        if (null === $ctMetadata = $this->metadataFactory->getMetadataForClass($odmMetadata->getName())) {
94
            return;
95
        }
96
97
        foreach ($odmMetadata->childrenMappings as $childrenField) {
98
99
            // if the children field is not managed by the CT component,
100
            // continue
101
            if (!isset($ctMetadata->propertyMetadata[$childrenField])) {
102
                continue;
103
            }
104
105
            $childCtMetadata = $ctMetadata->propertyMetadata[$childrenField];
106
            $children = $odmMetadata->getFieldValue($document, $childrenField);
107
108
            if (!$children) {
109
                continue;
110
            }
111
112
            $this->stack[] = [
113
                'children' => $children,
114
                'ct_metadata' => $childCtMetadata,
115
                'field' => $childrenField,
116
            ];
117
        }
118
    }
119
}
120