|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Doctrine\Common\Persistence; |
|
4
|
|
|
|
|
5
|
|
|
use BadMethodCallException; |
|
6
|
|
|
use Doctrine\Common\Collections\ArrayCollection; |
|
7
|
|
|
use Doctrine\Common\Collections\Collection; |
|
8
|
|
|
use Doctrine\Persistence\Mapping\ClassMetadata; |
|
9
|
|
|
use InvalidArgumentException; |
|
10
|
|
|
use RuntimeException; |
|
11
|
|
|
use function class_exists; |
|
12
|
|
|
use function lcfirst; |
|
13
|
|
|
use function substr; |
|
14
|
|
|
|
|
15
|
|
|
/** |
|
16
|
|
|
* PersistentObject base class that implements getter/setter methods for all mapped fields and associations |
|
17
|
|
|
* by overriding __call. |
|
18
|
|
|
* |
|
19
|
|
|
* This class is a forward compatible implementation of the PersistentObject trait. |
|
20
|
|
|
* |
|
21
|
|
|
* Limitations: |
|
22
|
|
|
* |
|
23
|
|
|
* 1. All persistent objects have to be associated with a single ObjectManager, multiple |
|
24
|
|
|
* ObjectManagers are not supported. You can set the ObjectManager with `PersistentObject#setObjectManager()`. |
|
25
|
|
|
* 2. Setters and getters only work if a ClassMetadata instance was injected into the PersistentObject. |
|
26
|
|
|
* This is either done on `postLoad` of an object or by accessing the global object manager. |
|
27
|
|
|
* 3. There are no hooks for setters/getters. Just implement the method yourself instead of relying on __call(). |
|
28
|
|
|
* 4. Slower than handcoded implementations: An average of 7 method calls per access to a field and 11 for an association. |
|
29
|
|
|
* 5. Only the inverse side associations get autoset on the owning side as well. Setting objects on the owning side |
|
30
|
|
|
* will not set the inverse side associations. |
|
31
|
|
|
* |
|
32
|
|
|
* @deprecated Deprecated `PersistentObject` class in 1.2. Please implement this functionality |
|
33
|
|
|
* directly in your application if you want ActiveRecord style functionality. |
|
34
|
|
|
* |
|
35
|
|
|
* @example |
|
36
|
|
|
* |
|
37
|
|
|
* PersistentObject::setObjectManager($em); |
|
38
|
|
|
* |
|
39
|
|
|
* class Foo extends PersistentObject |
|
40
|
|
|
* { |
|
41
|
|
|
* private $id; |
|
42
|
|
|
* } |
|
43
|
|
|
* |
|
44
|
|
|
* $foo = new Foo(); |
|
45
|
|
|
* $foo->getId(); // method exists through __call |
|
46
|
|
|
*/ |
|
47
|
|
|
abstract class PersistentObject implements ObjectManagerAware |
|
|
|
|
|
|
48
|
|
|
{ |
|
49
|
|
|
/** @var ObjectManager|null */ |
|
50
|
|
|
private static $objectManager = null; |
|
51
|
|
|
|
|
52
|
|
|
/** @var ClassMetadata|null */ |
|
53
|
|
|
private $cm = null; |
|
54
|
|
|
|
|
55
|
|
|
/** |
|
56
|
|
|
* Sets the object manager responsible for all persistent object base classes. |
|
57
|
|
|
* |
|
58
|
|
|
* @return void |
|
59
|
|
|
*/ |
|
60
|
18 |
|
public static function setObjectManager(?ObjectManager $objectManager = null) |
|
61
|
|
|
{ |
|
62
|
18 |
|
self::$objectManager = $objectManager; |
|
63
|
18 |
|
} |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* @return ObjectManager|null |
|
67
|
|
|
*/ |
|
68
|
1 |
|
public static function getObjectManager() |
|
69
|
|
|
{ |
|
70
|
1 |
|
return self::$objectManager; |
|
71
|
|
|
} |
|
72
|
|
|
|
|
73
|
|
|
/** |
|
74
|
|
|
* Injects the Doctrine Object Manager. |
|
75
|
|
|
* |
|
76
|
|
|
* @return void |
|
77
|
|
|
* |
|
78
|
|
|
* @throws RuntimeException |
|
79
|
|
|
*/ |
|
80
|
18 |
|
public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata) |
|
81
|
|
|
{ |
|
82
|
18 |
|
if ($objectManager !== self::$objectManager) { |
|
83
|
1 |
|
throw new RuntimeException('Trying to use PersistentObject with different ObjectManager instances. ' . |
|
84
|
1 |
|
'Was PersistentObject::setObjectManager() called?'); |
|
85
|
|
|
} |
|
86
|
|
|
|
|
87
|
18 |
|
$this->cm = $classMetadata; |
|
88
|
18 |
|
} |
|
89
|
|
|
|
|
90
|
|
|
/** |
|
91
|
|
|
* Sets a persistent fields value. |
|
92
|
|
|
* |
|
93
|
|
|
* @param string $field |
|
94
|
|
|
* @param mixed[] $args |
|
95
|
|
|
* |
|
96
|
|
|
* @return void |
|
97
|
|
|
* |
|
98
|
|
|
* @throws BadMethodCallException When no persistent field exists by that name. |
|
99
|
|
|
* @throws InvalidArgumentException When the wrong target object type is passed to an association. |
|
100
|
|
|
*/ |
|
101
|
7 |
|
private function set($field, $args) |
|
102
|
|
|
{ |
|
103
|
7 |
|
if ($this->cm->hasField($field) && ! $this->cm->isIdentifier($field)) { |
|
|
|
|
|
|
104
|
1 |
|
$this->$field = $args[0]; |
|
105
|
6 |
|
} elseif ($this->cm->hasAssociation($field) && $this->cm->isSingleValuedAssociation($field)) { |
|
106
|
4 |
|
$targetClass = $this->cm->getAssociationTargetClass($field); |
|
107
|
4 |
|
if (! ($args[0] instanceof $targetClass) && $args[0] !== null) { |
|
108
|
1 |
|
throw new InvalidArgumentException("Expected persistent object of type '" . $targetClass . "'"); |
|
109
|
|
|
} |
|
110
|
3 |
|
$this->$field = $args[0]; |
|
111
|
3 |
|
$this->completeOwningSide($field, $targetClass, $args[0]); |
|
|
|
|
|
|
112
|
|
|
} else { |
|
113
|
2 |
|
throw new BadMethodCallException("no field with name '" . $field . "' exists on '" . $this->cm->getName() . "'"); |
|
114
|
|
|
} |
|
115
|
4 |
|
} |
|
116
|
|
|
|
|
117
|
|
|
/** |
|
118
|
|
|
* Gets a persistent field value. |
|
119
|
|
|
* |
|
120
|
|
|
* @param string $field |
|
121
|
|
|
* |
|
122
|
|
|
* @return mixed |
|
123
|
|
|
* |
|
124
|
|
|
* @throws BadMethodCallException When no persistent field exists by that name. |
|
125
|
|
|
*/ |
|
126
|
8 |
|
private function get($field) |
|
127
|
|
|
{ |
|
128
|
8 |
|
if ($this->cm->hasField($field) || $this->cm->hasAssociation($field)) { |
|
129
|
7 |
|
return $this->$field; |
|
130
|
|
|
} |
|
131
|
|
|
|
|
132
|
1 |
|
throw new BadMethodCallException("no field with name '" . $field . "' exists on '" . $this->cm->getName() . "'"); |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
/** |
|
136
|
|
|
* If this is an inverse side association, completes the owning side. |
|
137
|
|
|
* |
|
138
|
|
|
* @param string $field |
|
139
|
|
|
* @param ClassMetadata $targetClass |
|
140
|
|
|
* @param object $targetObject |
|
141
|
|
|
* |
|
142
|
|
|
* @return void |
|
143
|
|
|
*/ |
|
144
|
3 |
|
private function completeOwningSide($field, $targetClass, $targetObject) |
|
145
|
|
|
{ |
|
146
|
|
|
// add this object on the owning side as well, for obvious infinite recursion |
|
147
|
|
|
// reasons this is only done when called on the inverse side. |
|
148
|
3 |
|
if (! $this->cm->isAssociationInverseSide($field)) { |
|
149
|
3 |
|
return; |
|
150
|
|
|
} |
|
151
|
|
|
|
|
152
|
1 |
|
$mappedByField = $this->cm->getAssociationMappedByTargetField($field); |
|
153
|
1 |
|
$targetMetadata = self::$objectManager->getClassMetadata($targetClass); |
|
|
|
|
|
|
154
|
|
|
|
|
155
|
1 |
|
$setter = ($targetMetadata->isCollectionValuedAssociation($mappedByField) ? 'add' : 'set') . $mappedByField; |
|
156
|
1 |
|
$targetObject->$setter($this); |
|
157
|
1 |
|
} |
|
158
|
|
|
|
|
159
|
|
|
/** |
|
160
|
|
|
* Adds an object to a collection. |
|
161
|
|
|
* |
|
162
|
|
|
* @param string $field |
|
163
|
|
|
* @param mixed[] $args |
|
164
|
|
|
* |
|
165
|
|
|
* @return void |
|
166
|
|
|
* |
|
167
|
|
|
* @throws BadMethodCallException |
|
168
|
|
|
* @throws InvalidArgumentException |
|
169
|
|
|
*/ |
|
170
|
3 |
|
private function add($field, $args) |
|
171
|
|
|
{ |
|
172
|
3 |
|
if (! $this->cm->hasAssociation($field) || ! $this->cm->isCollectionValuedAssociation($field)) { |
|
173
|
1 |
|
throw new BadMethodCallException('There is no method add' . $field . '() on ' . $this->cm->getName()); |
|
174
|
|
|
} |
|
175
|
|
|
|
|
176
|
2 |
|
$targetClass = $this->cm->getAssociationTargetClass($field); |
|
177
|
2 |
|
if (! ($args[0] instanceof $targetClass)) { |
|
178
|
1 |
|
throw new InvalidArgumentException("Expected persistent object of type '" . $targetClass . "'"); |
|
179
|
|
|
} |
|
180
|
1 |
|
if (! ($this->$field instanceof Collection)) { |
|
181
|
1 |
|
$this->$field = new ArrayCollection($this->$field ?: []); |
|
182
|
|
|
} |
|
183
|
1 |
|
$this->$field->add($args[0]); |
|
184
|
1 |
|
$this->completeOwningSide($field, $targetClass, $args[0]); |
|
|
|
|
|
|
185
|
1 |
|
} |
|
186
|
|
|
|
|
187
|
|
|
/** |
|
188
|
|
|
* Initializes Doctrine Metadata for this class. |
|
189
|
|
|
* |
|
190
|
|
|
* @return void |
|
191
|
|
|
* |
|
192
|
|
|
* @throws RuntimeException |
|
193
|
|
|
*/ |
|
194
|
16 |
|
private function initializeDoctrine() |
|
195
|
|
|
{ |
|
196
|
16 |
|
if ($this->cm !== null) { |
|
197
|
14 |
|
return; |
|
198
|
|
|
} |
|
199
|
|
|
|
|
200
|
3 |
|
if (! self::$objectManager) { |
|
201
|
1 |
|
throw new RuntimeException('No runtime object manager set. Call PersistentObject#setObjectManager().'); |
|
202
|
|
|
} |
|
203
|
|
|
|
|
204
|
2 |
|
$this->cm = self::$objectManager->getClassMetadata(static::class); |
|
205
|
2 |
|
} |
|
206
|
|
|
|
|
207
|
|
|
/** |
|
208
|
|
|
* Magic methods. |
|
209
|
|
|
* |
|
210
|
|
|
* @param string $method |
|
211
|
|
|
* @param mixed[] $args |
|
212
|
|
|
* |
|
213
|
|
|
* @return mixed |
|
214
|
|
|
* |
|
215
|
|
|
* @throws BadMethodCallException |
|
216
|
|
|
*/ |
|
217
|
16 |
|
public function __call($method, $args) |
|
218
|
|
|
{ |
|
219
|
16 |
|
$this->initializeDoctrine(); |
|
220
|
|
|
|
|
221
|
15 |
|
$command = substr($method, 0, 3); |
|
222
|
15 |
|
$field = lcfirst(substr($method, 3)); |
|
223
|
15 |
|
if ($command === 'set') { |
|
224
|
7 |
|
$this->set($field, $args); |
|
225
|
12 |
|
} elseif ($command === 'get') { |
|
226
|
8 |
|
return $this->get($field); |
|
227
|
5 |
|
} elseif ($command === 'add') { |
|
228
|
3 |
|
$this->add($field, $args); |
|
229
|
|
|
} else { |
|
230
|
2 |
|
throw new BadMethodCallException('There is no method ' . $method . ' on ' . $this->cm->getName()); |
|
231
|
|
|
} |
|
232
|
4 |
|
} |
|
233
|
|
|
} |
|
234
|
|
|
|
|
235
|
|
|
class_exists(\Doctrine\Common\Persistence\PersistentObject::class); |
|
236
|
|
|
|
This interface has been deprecated. The supplier of the interface has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.