Passed
Pull Request — master (#227)
by
unknown
02:12
created

RelationMap::queueRelations()   B

Complexity

Conditions 10
Paths 32

Size

Total Lines 55
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 10
eloc 32
c 2
b 0
f 0
nc 32
nop 4
dl 0
loc 55
rs 7.6666

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