Completed
Push — master ( 9c7e8e...65e25f )
by Valentin
15s
created

HasOneRelation   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 123
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

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

5 Methods

Rating   Name   Duplication   Size   Complexity  
A setRelated() 0 16 3
A queueCommands() 0 19 2
B queueRelated() 0 30 4
A whereStatement() 0 14 2
A isPlaceholderNeeded() 0 4 1
1
<?php
2
/**
3
 * Spiral, Core Components
4
 *
5
 * @author Wolfy-J
6
 */
7
8
namespace Spiral\ORM\Entities\Relations;
9
10
use Spiral\ORM\CommandInterface;
11
use Spiral\ORM\Commands\NullCommand;
12
use Spiral\ORM\Commands\TransactionalCommand;
13
use Spiral\ORM\ContextualCommandInterface;
14
use Spiral\ORM\Entities\Relations\Traits\LookupTrait;
15
use Spiral\ORM\ORMInterface;
16
use Spiral\ORM\Record;
17
use Spiral\ORM\RecordInterface;
18
19
class HasOneRelation extends SingularRelation
20
{
21
    use LookupTrait;
22
23
    /** Automatically create related model when empty. */
24
    const CREATE_PLACEHOLDER = true;
25
26
    /**
27
     * Previously binded instance, to be deleted.
28
     *
29
     * @var RecordInterface
30
     */
31
    private $previous = null;
32
33
    /**
34
     * {@inheritdoc}
35
     */
36
    public function setRelated($value)
37
    {
38
        //Make sure value is accepted
39
        $this->assertValid($value);
40
        $this->loadData();
41
42
        if (empty($this->previous)) {
43
            //We are only keeping reference to the oldest (ie loaded) instance
44
            $this->previous = $this->instance;
45
        }
46
47
        $this->instance = $value;
48
        if (is_null($value)) {
49
            $this->data = [];
50
        }
51
    }
52
53
    /**
54
     * {@inheritdoc}
55
     */
56
    public function queueCommands(ContextualCommandInterface $parentCommand): CommandInterface
57
    {
58
        if (!empty($this->previous)) {
59
            $transaction = new TransactionalCommand();
60
61
            //Delete old entity
62
            $transaction->addCommand($this->previous->queueDelete());
63
64
            //Store new entity if any (leading)
65
            $transaction->addCommand($this->queueRelated($parentCommand), true);
66
67
            //We don't need previous reference anymore
68
            $this->previous = null;
69
70
            return $transaction;
71
        }
72
73
        return $this->queueRelated($parentCommand);
74
    }
75
76
    /**
77
     * Store related instance.
78
     *
79
     * @param ContextualCommandInterface $parentCommand
80
     *
81
     * @return CommandInterface
82
     */
83
    private function queueRelated(ContextualCommandInterface $parentCommand): CommandInterface
84
    {
85
        if (empty($this->instance)) {
86
            return new NullCommand();
87
        }
88
89
        //Related entity store command
90
        $innerCommand = $this->instance->queueStore(true);
91
92
        //Inversed version of BelongsTo
93
        if (!$this->isSynced($this->parent, $this->instance)) {
94
            //Syncing FKs after primary command been executed
95
            $parentCommand->onExecute(function ($outerCommand) use ($innerCommand) {
96
                $innerCommand->addContext(
97
                    $this->key(Record::OUTER_KEY),
98
                    $this->lookupKey(Record::INNER_KEY, $this->parent, $outerCommand)
99
                );
100
101
                if (!empty($morphKey = $this->key(Record::MORPH_KEY))) {
102
                    //HasOne relation support additional morph key
103
                    $innerCommand->addContext(
104
                        $this->key(Record::MORPH_KEY),
105
                        $this->orm->define(get_class($this->parent), ORMInterface::R_ROLE_NAME)
106
                    );
107
                }
108
            });
109
        }
110
111
        return $innerCommand;
112
    }
113
114
    /**
115
     * Where statement to load outer record.
116
     *
117
     * @return array
118
     */
119
    protected function whereStatement(): array
120
    {
121
        $where = parent::whereStatement();
122
123
        if (!empty($morphKey = $this->key(Record::MORPH_KEY))) {
124
            //HasOne relation support additional morph key
125
            $where['{@}.' . $this->key(Record::MORPH_KEY)] = $this->orm->define(
126
                get_class($this->parent),
127
                ORMInterface::R_ROLE_NAME
128
            );
129
        }
130
131
        return $where;
132
    }
133
134
    /**
135
     * @return bool
136
     */
137
    protected function isPlaceholderNeeded(): bool
138
    {
139
        return !$this->key(Record::NULLABLE);
140
    }
141
}