1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Spiral Framework. |
4
|
|
|
* |
5
|
|
|
* @license MIT |
6
|
|
|
* @author Anton Titov (Wolfy-J) |
7
|
|
|
*/ |
8
|
|
|
namespace Spiral\ORM\Entities\Relations; |
9
|
|
|
|
10
|
|
|
use Spiral\Models\EntityInterface; |
11
|
|
|
use Spiral\ORM\Exceptions\RelationException; |
12
|
|
|
use Spiral\ORM\ORM; |
13
|
|
|
use Spiral\ORM\RecordEntity; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Represents simple BELONGS_TO relation with ability to associate and de-associate parent. |
17
|
|
|
*/ |
18
|
|
|
class BelongsTo extends HasOne |
19
|
|
|
{ |
20
|
|
|
/** |
21
|
|
|
* Relation type, required to fetch record class from relation definition. |
22
|
|
|
*/ |
23
|
|
|
const RELATION_TYPE = RecordEntity::BELONGS_TO; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* {@inheritdoc} |
27
|
|
|
*/ |
28
|
|
|
public function isLoaded() |
29
|
|
|
{ |
30
|
|
|
$this->fromCache(); |
31
|
|
|
|
32
|
|
|
if (empty($this->parent->getField($this->definition[RecordEntity::INNER_KEY], false))) { |
33
|
|
|
return true; |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
return $this->loaded; |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* {@inheritdoc} |
41
|
|
|
*/ |
42
|
|
|
public function getRelated() |
43
|
|
|
{ |
44
|
|
|
$this->fromCache(); |
45
|
|
|
|
46
|
|
|
return parent::getRelated(); |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* {@inheritdoc} |
51
|
|
|
* |
52
|
|
|
* Parent record MUST be saved in order to preserve parent association. |
53
|
|
|
*/ |
54
|
|
|
public function associate(EntityInterface $related = null) |
55
|
|
|
{ |
56
|
|
|
if ($related === null) { |
57
|
|
|
$this->deassociate(); |
58
|
|
|
|
59
|
|
|
return; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* @var RecordEntity $related |
64
|
|
|
*/ |
65
|
|
|
if (!$related->isLoaded()) { |
66
|
|
|
throw new RelationException( |
67
|
|
|
"Unable to set 'belongs to' parent, parent has be fetched from database." |
68
|
|
|
); |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
parent::associate($related); |
72
|
|
|
|
73
|
|
|
//Key in parent record |
74
|
|
|
$outerKey = $this->definition[RecordEntity::OUTER_KEY]; |
75
|
|
|
|
76
|
|
|
//Key in child record |
77
|
|
|
$innerKey = $this->definition[RecordEntity::INNER_KEY]; |
78
|
|
|
|
79
|
|
View Code Duplication |
if ($this->parent->getField($innerKey, false) != $related->getField($outerKey, false)) { |
|
|
|
|
80
|
|
|
//We are going to set relation keys right on assertion |
81
|
|
|
$this->parent->setField($innerKey, $related->getField($outerKey, false)); |
82
|
|
|
} |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* {@inheritdoc} |
87
|
|
|
*/ |
88
|
|
|
protected function mountRelation(EntityInterface $record) |
89
|
|
|
{ |
90
|
|
|
//Nothing to do, children can not update parent relation |
91
|
|
|
return $record; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* {@inheritdoc} |
96
|
|
|
* |
97
|
|
|
* @throws RelationException |
98
|
|
|
*/ |
99
|
|
|
protected function createSelector() |
100
|
|
|
{ |
101
|
|
|
if (empty($this->parent->getField($this->definition[RecordEntity::INNER_KEY], false))) { |
102
|
|
|
throw new RelationException( |
103
|
|
|
"Belongs-to selector can not be constructed when inner key (" |
104
|
|
|
. $this->definition[RecordEntity::INNER_KEY] |
105
|
|
|
. ") is null." |
106
|
|
|
); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
return parent::createSelector(); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* {@inheritdoc} |
114
|
|
|
* |
115
|
|
|
* Belongs-to can not automatically create parent. |
116
|
|
|
*/ |
117
|
|
|
protected function emptyRecord() |
118
|
|
|
{ |
119
|
|
|
return null; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* De associate related record. |
124
|
|
|
*/ |
125
|
|
|
protected function deassociate() |
126
|
|
|
{ |
127
|
|
|
if (!$this->definition[RecordEntity::NULLABLE]) { |
128
|
|
|
throw new RelationException( |
129
|
|
|
"Unable to de-associate relation data, relation is not nullable." |
130
|
|
|
); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
$innerKey = $this->definition[RecordEntity::INNER_KEY]; |
134
|
|
|
$this->parent->setField($innerKey, null); |
135
|
|
|
|
136
|
|
|
$this->loaded = true; |
137
|
|
|
$this->instance = null; |
138
|
|
|
$this->data = []; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* Loadable when parent is loaded as well. |
143
|
|
|
* |
144
|
|
|
* @return bool |
145
|
|
|
*/ |
146
|
|
|
protected function isLoadable() |
147
|
|
|
{ |
148
|
|
|
return true; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* Try to fetch outer model using entity cache. |
153
|
|
|
*/ |
154
|
|
|
private function fromCache() |
155
|
|
|
{ |
156
|
|
|
if ($this->loaded) { |
157
|
|
|
return; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
$innerKey = $this->parent->getField($this->definition[RecordEntity::INNER_KEY], false); |
161
|
|
|
|
162
|
|
|
if (empty($innerKey)) { |
163
|
|
|
return; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
if (empty($this->definition[ORM::M_PRIMARY_KEY])) { |
167
|
|
|
//Linked not by primary key |
168
|
|
|
return; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
if (empty($entity = $this->orm->cache()->get($this->getClass(), $innerKey))) { |
172
|
|
|
return; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
$this->loaded = true; |
176
|
|
|
$this->instance = $entity; |
177
|
|
|
} |
178
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.