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