1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of slick/orm package |
5
|
|
|
* |
6
|
|
|
* For the full copyright and license information, please view the LICENSE |
7
|
|
|
* file that was distributed with this source code. |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace Slick\Orm\Mapper\Relation; |
11
|
|
|
|
12
|
|
|
use Slick\Database\Sql; |
13
|
|
|
use Slick\Orm\Entity\EntityCollection; |
14
|
|
|
use Slick\Orm\EntityInterface; |
15
|
|
|
use Slick\Orm\Event\Save; |
16
|
|
|
use Slick\Orm\Event\Select; |
17
|
|
|
use Slick\Orm\Mapper\RelationInterface; |
18
|
|
|
use Slick\Orm\Orm; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* BelongsTo (Many-To-On) relation |
22
|
|
|
* |
23
|
|
|
* @package Slick\Orm\Mapper\Relation |
24
|
|
|
* @author Filipe Silva <[email protected]> |
25
|
|
|
*/ |
26
|
|
|
class BelongsTo extends AbstractRelation implements RelationInterface |
27
|
|
|
{ |
28
|
|
|
/** |
29
|
|
|
* Relations utility methods |
30
|
|
|
*/ |
31
|
|
|
use RelationsUtilityMethods; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @readwrite |
35
|
|
|
* @var bool |
36
|
|
|
*/ |
37
|
|
|
protected $dependent = true; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* BelongsTo relation |
41
|
|
|
* |
42
|
|
|
* @param array|object $options The parameters from annotation |
43
|
|
|
*/ |
44
|
26 |
|
public function __construct($options) |
45
|
|
|
{ |
46
|
|
|
/** @var \Slick\Orm\Annotations\BelongsTo $annotation */ |
47
|
26 |
|
$annotation = $options['annotation']; |
48
|
26 |
|
unset($options['annotation']); |
49
|
26 |
|
$options['foreignKey'] = $annotation->getParameter('foreignKey'); |
50
|
26 |
|
$options['parentEntity'] = $annotation->getValue(); |
51
|
26 |
|
$options['lazyLoaded'] = $annotation->getParameter('lazyLoaded'); |
52
|
|
|
|
53
|
26 |
|
parent::__construct($options); |
54
|
|
|
|
55
|
26 |
|
$this->registerListeners(); |
56
|
26 |
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Handles the before select callback |
60
|
|
|
* |
61
|
|
|
* @param Select $event |
62
|
|
|
*/ |
63
|
4 |
View Code Duplication |
public function beforeSelect(Select $event) |
|
|
|
|
64
|
|
|
{ |
65
|
4 |
|
if ($this->isLazyLoaded()) { |
66
|
2 |
|
return; |
67
|
|
|
} |
68
|
|
|
|
69
|
2 |
|
$fields = $this->getFieldsPrefixed(); |
70
|
2 |
|
$table = $this->entityDescriptor->getTableName(); |
71
|
2 |
|
$relateTable = $this->getParentTableName(); |
72
|
2 |
|
$pmk = $this->getParentPrimaryKey(); |
73
|
|
|
|
74
|
2 |
|
$onClause = "{$table}.{$this->getForeignKey()} = ". |
75
|
2 |
|
"{$relateTable}.{$pmk}"; |
76
|
|
|
|
77
|
2 |
|
$query = $event->getQuery(); |
78
|
2 |
|
$query->join($relateTable, $onClause, $fields, $relateTable); |
79
|
2 |
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Handles the after select callback |
83
|
|
|
* |
84
|
|
|
* @param Select $event |
85
|
|
|
*/ |
86
|
6 |
|
public function afterSelect(Select $event) |
87
|
|
|
{ |
88
|
6 |
|
if ($this->isLazyLoaded()) { |
89
|
2 |
|
return; |
90
|
|
|
} |
91
|
4 |
|
foreach ($event->getEntityCollection() as $index => $entity) { |
92
|
4 |
|
$row = $event->getData()[$index]; |
93
|
4 |
|
$entity->{$this->propertyName} = $this->getFromMap($row); |
94
|
2 |
|
} |
95
|
4 |
|
} |
96
|
|
|
|
97
|
2 |
|
public function beforeSave(Save $event) |
98
|
|
|
{ |
99
|
2 |
|
$parent = $event->getEntity()->{$this->propertyName}; |
100
|
2 |
|
if ($parent instanceof EntityInterface) { |
101
|
2 |
|
$parent = $parent->getId(); |
102
|
1 |
|
} |
103
|
2 |
|
$event->params[$this->getForeignKey()] = $parent; |
104
|
2 |
|
$related = $this->getParentRepository() |
105
|
2 |
|
->get($parent); |
106
|
2 |
|
$event->getEntity()->{$this->propertyName} = $related; |
107
|
2 |
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Registers the listener for before select event |
111
|
|
|
*/ |
112
|
14 |
|
protected function registerListeners() |
113
|
|
|
{ |
114
|
14 |
|
Orm::addListener( |
115
|
14 |
|
$this->entityDescriptor->className(), |
116
|
14 |
|
Select::ACTION_BEFORE_SELECT, |
117
|
14 |
|
[$this, 'beforeSelect'] |
118
|
7 |
|
); |
119
|
|
|
|
120
|
14 |
|
Orm::addListener( |
121
|
14 |
|
$this->entityDescriptor->className(), |
122
|
14 |
|
Select::ACTION_AFTER_SELECT, |
123
|
14 |
|
[$this, 'afterSelect'] |
124
|
7 |
|
); |
125
|
|
|
|
126
|
14 |
|
Orm::addListener( |
127
|
14 |
|
$this->entityDescriptor->className(), |
128
|
14 |
|
Save::ACTION_BEFORE_INSERT, |
129
|
14 |
|
[$this, 'beforeSave'] |
130
|
7 |
|
); |
131
|
|
|
|
132
|
14 |
|
Orm::addListener( |
133
|
14 |
|
$this->entityDescriptor->className(), |
134
|
14 |
|
Save::ACTION_BEFORE_UPDATE, |
135
|
14 |
|
[$this, 'beforeSave'] |
136
|
7 |
|
); |
137
|
14 |
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Prefixed fields for join |
141
|
|
|
* |
142
|
|
|
* @return array |
143
|
|
|
*/ |
144
|
4 |
|
protected function getFieldsPrefixed() |
145
|
|
|
{ |
146
|
4 |
|
$table = $this->getParentTableName(); |
147
|
4 |
|
$data = []; |
148
|
|
|
|
149
|
4 |
|
foreach ($this->getParentFields() as $field) { |
150
|
4 |
|
$data[] = "{$field->getField()} AS ". |
151
|
4 |
|
"{$table}_{$field->getField()}"; |
152
|
2 |
|
} |
153
|
4 |
|
return $data; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Check if entity is already loaded and uses it. |
158
|
|
|
* |
159
|
|
|
* If not loaded the entity will be created and loaded to the repository's |
160
|
|
|
* identity map so that it can be reused next time. |
161
|
|
|
* |
162
|
|
|
* @param array $dataRow |
163
|
|
|
* |
164
|
|
|
* @return null|EntityCollection|EntityInterface|EntityInterface[] |
165
|
|
|
*/ |
166
|
2 |
|
protected function getFromMap($dataRow) |
167
|
|
|
{ |
168
|
2 |
|
$entity = $this->getParentRepository() |
169
|
2 |
|
->getIdentityMap() |
170
|
2 |
|
->get($dataRow[$this->getForeignKey()], false); |
171
|
2 |
|
if (false === $entity) { |
172
|
2 |
|
$entity = $this->map($dataRow); |
173
|
1 |
|
} |
174
|
2 |
|
return $entity; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Creates and maps related entity |
179
|
|
|
* |
180
|
|
|
* @param array $dataRow |
181
|
|
|
* |
182
|
|
|
* @return null|EntityCollection|EntityInterface|EntityInterface[] |
183
|
|
|
*/ |
184
|
4 |
|
protected function map($dataRow) |
185
|
|
|
{ |
186
|
4 |
|
$data = $this->getData($dataRow); |
187
|
4 |
|
$pmk = $this->getParentPrimaryKey(); |
188
|
4 |
|
$entity = (isset($data[$pmk]) && $data[$pmk]) |
189
|
4 |
|
? $this->getParentEntityMapper()->createFrom($data) |
190
|
4 |
|
: null; |
191
|
4 |
|
return null == $entity ? null : $this->registerEntity($entity); |
|
|
|
|
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* Gets a data array with fields and values for parent entity creation |
196
|
|
|
* |
197
|
|
|
* @param array $dataRow |
198
|
|
|
* |
199
|
|
|
* @return array |
200
|
|
|
*/ |
201
|
4 |
|
protected function getData($dataRow) |
202
|
|
|
{ |
203
|
4 |
|
$data = []; |
204
|
4 |
|
$relateTable = $this->getParentTableName(); |
205
|
4 |
|
$regexp = "/{$relateTable}_(?P<name>.+)/i"; |
206
|
4 |
|
foreach ($dataRow as $field => $value) { |
207
|
4 |
|
if (preg_match($regexp, $field, $matched)) { |
208
|
4 |
|
$data[$matched['name']] = $value; |
209
|
2 |
|
} |
210
|
2 |
|
} |
211
|
4 |
|
return $data; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
/** |
215
|
|
|
* Loads the entity or entity collection for this relation |
216
|
|
|
* |
217
|
|
|
* @param EntityInterface $entity |
218
|
|
|
* |
219
|
|
|
* @return null|EntityInterface |
220
|
|
|
*/ |
221
|
2 |
|
public function load(EntityInterface $entity) |
222
|
|
|
{ |
223
|
2 |
|
$adapter = $this->getAdapter(); |
224
|
|
|
|
225
|
2 |
|
$relTable = $this->getParentTableName(); |
226
|
2 |
|
$relPmk = $this->getParentPrimaryKey(); |
227
|
|
|
|
228
|
2 |
|
$table = $this->getEntityDescriptor()->getTableName(); |
229
|
2 |
|
$pmk = $this->getEntityDescriptor()->getPrimaryKey(); |
230
|
2 |
|
$fnk = $this->getForeignKey(); |
231
|
|
|
|
232
|
2 |
|
$onClause = "{$relTable}.{$relPmk} = {$table}.{$fnk}"; |
233
|
|
|
|
234
|
2 |
|
$data = Sql::createSql($adapter) |
235
|
2 |
|
->select($relTable) |
236
|
2 |
|
->join($table, $onClause, null) |
237
|
2 |
|
->where([ |
|
|
|
|
238
|
2 |
|
"{$table}.{$pmk->getField()} = :id" => [ |
239
|
2 |
|
':id' => $entity->{$pmk->getName()} |
240
|
1 |
|
] |
241
|
1 |
|
]) |
242
|
2 |
|
->first(); |
243
|
|
|
|
244
|
2 |
|
$relEntity = $this->getParentEntityMapper()->createFrom($data); |
245
|
|
|
|
246
|
2 |
|
return null == $relEntity ? null :$this->registerEntity($relEntity); |
|
|
|
|
247
|
|
|
} |
248
|
|
|
} |
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.