RefersTo   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 141
Duplicated Lines 0 %

Test Coverage

Coverage 91.18%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 81
c 1
b 0
f 0
dl 0
loc 141
ccs 62
cts 68
cp 0.9118
rs 9.6
wmc 35

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A pullValues() 0 6 3
A checkNullValue() 0 17 4
D queue() 0 62 18
B prepare() 0 35 9
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\ORM\Relation;
6
7
use Cycle\ORM\Heap\Node;
8
use Cycle\ORM\Heap\State;
9
use Cycle\ORM\ORMInterface;
10
use Cycle\ORM\Reference\ReferenceInterface;
11
use Cycle\ORM\Relation\Traits\ToOneTrait;
12
use Cycle\ORM\Service\EntityProviderInterface;
13
use Cycle\ORM\Transaction\Pool;
14
use Cycle\ORM\Transaction\Tuple;
15
16
/**
17
 * Variation of belongs-to relation which provides the ability to be self linked. Relation can be used
18
 * to create cyclic references. Relation does not trigger store operation of referenced object!
19
 *
20
 * @internal
21
 */
22
class RefersTo extends AbstractRelation implements DependencyInterface
23
{
24
    use ToOneTrait;
25
26 870
    public function __construct(ORMInterface $orm, string $role, string $name, string $target, array $schema)
27
    {
28 870
        $this->entityProvider = $orm->getService(EntityProviderInterface::class);
29
30 870
        parent::__construct($orm, $role, $name, $target, $schema);
31
    }
32
33 406
    public function prepare(Pool $pool, Tuple $tuple, mixed $related, bool $load = true): void
34
    {
35 406
        $state = $tuple->state;
36 406
        $relName = $this->getName();
37
38 406
        if (SpecialValue::isNotSet($related)) {
39 8
            if (!$state->hasRelation($relName)) {
40 8
                $state->setRelationStatus($relName, RelationInterface::STATUS_DEFERRED);
41
                return;
42 406
            }
43 142
44
            $related = $state->getRelation($relName);
45 376
        }
46 376
47 96
        $node = $tuple->node;
48 96
        $tuple->state->setRelation($relName, $related);
49
50
        if ($related instanceof ReferenceInterface && $this->resolve($related, false) !== null) {
51 352
            $related = $related->getValue();
52 352
            $tuple->state->setRelation($relName, $related);
53 352
        }
54 320
        if ($this->checkNullValue($node, $tuple->state, $related)) {
55
            return;
56
        }
57
        $this->registerWaitingFields($tuple->state, false);
58 424
59
        if ($related instanceof ReferenceInterface) {
60 424
            $tuple->state->setRelationStatus($relName, RelationInterface::STATUS_DEFERRED);
61 424
            return;
62
        }
63 424
64 8
        $tuple->state->setRelationStatus($relName, RelationInterface::STATUS_PROCESS);
65 8
        $rTuple = $pool->offsetGet($related);
66
        if ($rTuple === null && $this->isCascade()) {
67 424
            $pool->attachStore($related, false, null, null, false);
68 88
        }
69 88
    }
70 88
71 88
    public function queue(Pool $pool, Tuple $tuple): void
72
    {
73 88
        $state = $tuple->state;
74 88
        $relName = $this->getName();
75 88
76
        if (!$state->hasRelation($relName)) {
77
            $state->setRelationStatus($relName, RelationInterface::STATUS_RESOLVED);
78 408
            return;
79 56
        }
80
81 352
        $node = $tuple->node;
82 352
        $related = $tuple->state->getRelation($relName);
83
84
        if ($related instanceof ReferenceInterface && ($related->hasValue() || $this->resolve($related, false) !== null)) {
85
            $related = $related->getValue();
86
            $tuple->state->setRelation($relName, $related);
87
        }
88
        if ($related instanceof ReferenceInterface) {
89
            $scope = $related->getScope();
90
            if (\array_intersect($this->outerKeys, \array_keys($scope))) {
91
                foreach ($this->outerKeys as $i => $outerKey) {
92
                    $tuple->state->register($this->innerKeys[$i], $scope[$outerKey]);
93
                }
94
                $node->setRelation($relName, $related);
95 352
                $tuple->state->setRelationStatus($relName, RelationInterface::STATUS_RESOLVED);
96 208
                return;
97 208
            }
98 352
        }
99
        if ($this->checkNullValue($tuple->node, $tuple->state, $related)) {
100 328
            return;
101 328
        }
102 328
103 328
        $rTuple = $pool->offsetGet($related);
104
        if ($rTuple === null) {
105
            if ($this->isCascade()) {
106 192
                // todo: cascade true?
107 192
                $rTuple = $pool->attachStore($related, false, null, null, false);
108
            } elseif (
109
                $tuple->state->getRelationStatus($relName) !== RelationInterface::STATUS_DEFERRED
110
                || $tuple->status !== Tuple::STATUS_PROPOSED_RESOLVED
111 328
            ) {
112
                $tuple->state->setRelationStatus($relName, RelationInterface::STATUS_DEFERRED);
113 328
                return;
114 328
            } else {
115 328
                $tuple->state->setRelationStatus($relName, RelationInterface::STATUS_RESOLVED);
116 328
                return;
117
            }
118
        }
119
120
        if ($rTuple->status === Tuple::STATUS_PROCESSED
121 454
            || ($rTuple->status > Tuple::STATUS_PREPARING
122
                && $rTuple->state->getStatus() !== node::NEW
123 454
                && \array_intersect($this->outerKeys, $rTuple->state->getWaitingFields()) === [])
124 376
        ) {
125
            $this->pullValues($tuple->state, $rTuple->state);
126 198
            $node->setRelation($relName, $related);
127
            $tuple->state->setRelationStatus($relName, RelationInterface::STATUS_RESOLVED);
128 198
            return;
129
        }
130 96
131 96
        if ($tuple->status !== Tuple::STATUS_PREPARING) {
132
            $tuple->state->setRelationStatus($relName, RelationInterface::STATUS_DEFERRED);
133
        }
134
    }
135 198
136 198
    private function pullValues(State $state, State $rState): void
137 198
    {
138
        $changes = $rState->getTransactionData();
139
        foreach ($this->outerKeys as $i => $outerKey) {
140
            if (isset($changes[$outerKey])) {
141
                $state->register($this->innerKeys[$i], $changes[$outerKey]);
142
            }
143
        }
144
    }
145
146
    private function checkNullValue(Node $node, State $state, mixed $value): bool
147
    {
148
        if ($value !== null) {
149
            return false;
150
        }
151
        $original = $node->getRelation($this->getName());
152
        // Original is not null
153
        if ($original !== null) {
154
            // Reset keys
155
            foreach ($this->innerKeys as $innerKey) {
156
                $state->register($innerKey, null);
157
            }
158
        }
159
160
        $node->setRelation($this->getName(), null);
161
        $state->setRelationStatus($this->getName(), RelationInterface::STATUS_RESOLVED);
162
        return true;
163
    }
164
}
165