Passed
Push — master ( d78780...0417e4 )
by Aleksei
02:26
created

RelationMap::sameReference()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 3
c 0
b 0
f 0
nc 3
nop 2
dl 0
loc 7
rs 10
1
<?php
2
3
/**
4
 * Cycle DataMapper ORM
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Cycle\ORM;
13
14
use Cycle\ORM\Command\Branch\ContextSequence;
15
use Cycle\ORM\Command\CommandInterface;
16
use Cycle\ORM\Command\ContextCarrierInterface as CC;
17
use Cycle\ORM\Heap\Node;
18
use Cycle\ORM\Relation\ChangesCheckerInterface;
19
use Cycle\ORM\Relation\DependencyInterface;
20
use Cycle\ORM\Relation\RelationInterface;
21
22
/**
23
 * Manages the position of node in the relation graph and provide access to neighbours.
24
 */
25
final class RelationMap
26
{
27
    /** @var ORMInterface @internal */
28
    private $orm;
29
30
    /** @var RelationInterface[] */
31
    private $relations = [];
32
33
    /** @var DependencyInterface[] */
34
    private $dependencies = [];
35
36
    /**
37
     * @param ORMInterface $orm
38
     * @param array $relations
39
     */
40
    public function __construct(ORMInterface $orm, array $relations)
41
    {
42
        $this->orm = $orm;
43
        $this->relations = $relations;
44
45
        foreach ($this->relations as $name => $relation) {
46
            if ($relation instanceof DependencyInterface) {
47
                $this->dependencies[$name] = $relation;
48
            }
49
        }
50
    }
51
52
    /**
53
     * Init relation data in entity data and entity state.
54
     *
55
     * @param Node  $node
56
     * @param array $data
57
     *
58
     * @return array
59
     */
60
    public function init(Node $node, array $data): array
61
    {
62
        foreach ($this->relations as $name => $relation) {
63
            if (!array_key_exists($name, $data)) {
64
                if ($node->hasRelation($name)) {
65
                    continue;
66
                }
67
68
                [$data[$name], $orig] = $relation->initPromise($node);
69
                $node->setRelation($name, $orig);
70
                continue;
71
            }
72
73
            $item = $data[$name];
74
            if (is_object($item) || $item === null) {
75
                // cyclic initialization
76
                $node->setRelation($name, $item);
77
                continue;
78
            }
79
80
            // init relation for the entity and for state and the same time
81
            [$data[$name], $orig] = $relation->init($node, $item);
82
            $node->setRelation($name, $orig);
83
        }
84
85
        return $data;
86
    }
87
88
    /**
89
     * Queue entity relations.
90
     *
91
     * @param CC     $parentStore
92
     * @param object $parentEntity
93
     * @param Node   $parentNode
94
     * @param array  $parentData
95
     *
96
     * @return CC
97
     */
98
    public function queueRelations(CC $parentStore, $parentEntity, Node $parentNode, array $parentData): CC
99
    {
100
        $state = $parentNode->getState();
101
        $sequence = new ContextSequence();
102
103
        // queue all "left" graph branches
104
        foreach ($this->dependencies as $name => $relation) {
105
            if (!$relation->isCascade() || $parentNode->getState()->visited($name)) {
106
                continue;
107
            }
108
            $state->markVisited($name);
109
110
            $command = $this->queueRelation(
111
                $parentStore,
112
                $parentEntity,
113
                $parentNode,
114
                $relation,
115
                $relation->extract($parentData[$name] ?? null),
116
                $parentNode->getRelation($name)
117
            );
118
119
            if ($command !== null) {
120
                $sequence->addCommand($command);
121
            }
122
        }
123
124
        // queue target entity
125
        $sequence->addPrimary($parentStore);
126
127
        // queue all "right" graph branches
128
        foreach ($this->relations as $name => $relation) {
129
            if (!$relation->isCascade() || $parentNode->getState()->visited($name)) {
130
                continue;
131
            }
132
            $state->markVisited($name);
133
134
            $command = $this->queueRelation(
135
                $parentStore,
136
                $parentEntity,
137
                $parentNode,
138
                $relation,
139
                $relation->extract($parentData[$name] ?? null),
140
                $parentNode->getRelation($name)
141
            );
142
143
            if ($command !== null) {
144
                $sequence->addCommand($command);
145
            }
146
        }
147
148
        if (\count($sequence) === 1) {
149
            return current($sequence->getCommands());
150
        }
151
152
        return $sequence;
153
    }
154
155
    /**
156
     * Queue the relation.
157
     *
158
     * @param CC                $parentStore
159
     * @param object            $parentEntity
160
     * @param Node              $parentNode
161
     * @param RelationInterface $relation
162
     * @param mixed             $related
163
     * @param mixed             $original
164
     *
165
     * @return CommandInterface|null
166
     */
167
    private function queueRelation(
168
        CC $parentStore,
169
        $parentEntity,
170
        Node $parentNode,
171
        RelationInterface $relation,
172
        $related,
173
        $original
174
    ): ?CommandInterface {
175
        if (!($relation instanceof ChangesCheckerInterface ? $relation->hasChanges($related, $original) : true)) {
176
            return null;
177
        }
178
179
        $relStore = $relation->queue(
180
            $parentStore,
181
            $parentEntity,
182
            $parentNode,
183
            $related,
184
            $original
185
        );
186
187
        // update current relation state
188
        $parentNode->getState()->setRelation($relation->getName(), $related);
189
190
        return $relStore;
191
    }
192
}
193