1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author CONTENT CONTROL http://www.contentcontrol-berlin.de/ |
4
|
|
|
* @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/ |
5
|
|
|
* @license http://www.gnu.org/licenses/gpl.html GNU General Public License |
6
|
|
|
*/ |
7
|
|
|
namespace midgard\portable\api; |
8
|
|
|
|
9
|
|
|
use midgard\portable\storage\connection; |
10
|
|
|
use midgard\portable\storage\objectmanager; |
11
|
|
|
use midgard\portable\storage\collection; |
12
|
|
|
use midgard\portable\storage\interfaces\metadata as metadata_interface; |
13
|
|
|
use midgard\portable\mgdschema\translator; |
14
|
|
|
use midgard\portable\api\error\exception; |
15
|
|
|
use Doctrine\ORM\Query; |
16
|
|
|
use midgard_connection; |
17
|
|
|
use Doctrine\ORM\Proxy\Proxy; |
18
|
|
|
use Doctrine\ORM\QueryBuilder; |
19
|
|
|
use Doctrine\ORM\EntityNotFoundException; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* @property metadata $metadata |
23
|
|
|
*/ |
24
|
|
|
abstract class mgdobject extends dbobject |
25
|
|
|
{ |
26
|
|
|
protected $metadata; // compat with mgd behavior: If the schema has no metadata, the property is present anyway |
27
|
|
|
|
28
|
|
|
public $action = ''; // <== does this need to do anything? |
29
|
|
|
|
30
|
|
|
private array $collections = []; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @param mixed $id ID or GUID |
34
|
|
|
*/ |
35
|
137 |
|
public function __construct($id = null) |
36
|
|
|
{ |
37
|
137 |
|
if ($id !== null) { |
38
|
50 |
|
if (is_int($id)) { |
39
|
41 |
|
$this->get_by_id($id); |
40
|
14 |
|
} elseif (is_string($id)) { |
41
|
14 |
|
$this->get_by_guid($id); |
42
|
|
|
} |
43
|
|
|
} |
44
|
|
|
} |
45
|
|
|
|
46
|
16 |
|
private function get_collection(string $classname) : collection |
47
|
|
|
{ |
48
|
16 |
|
if (!isset($this->collections[$classname])) { |
49
|
16 |
|
$this->collections[$classname] = new collection($classname); |
50
|
|
|
} |
51
|
16 |
|
return $this->collections[$classname]; |
52
|
|
|
} |
53
|
|
|
|
54
|
1 |
|
public function __debugInfo() |
55
|
|
|
{ |
56
|
1 |
|
$ret = parent::__debugInfo(); |
57
|
1 |
|
if (property_exists($this, 'metadata')) { |
58
|
1 |
|
$metadata = new \stdClass; |
59
|
1 |
|
foreach ($this->cm->getFieldNames() as $name) { |
60
|
1 |
|
if (str_contains($name, 'metadata_')) { |
61
|
1 |
|
$fieldname = str_replace('metadata_', '', $name); |
62
|
1 |
|
$metadata->$fieldname = $this->__get($name); |
63
|
|
|
} |
64
|
|
|
} |
65
|
1 |
|
$ret['metadata'] = $metadata; |
66
|
|
|
} |
67
|
|
|
|
68
|
1 |
|
return $ret; |
69
|
|
|
} |
70
|
|
|
|
71
|
113 |
|
public function __set($field, $value) |
72
|
|
|
{ |
73
|
113 |
|
if ($field == 'guid') { |
74
|
6 |
|
return; |
75
|
|
|
} |
76
|
113 |
|
parent::__set($field, $value); |
77
|
|
|
} |
78
|
|
|
|
79
|
130 |
|
public function __get($field) |
80
|
|
|
{ |
81
|
130 |
|
if ( $field === 'metadata' |
82
|
130 |
|
&& $this->metadata === null |
83
|
130 |
|
&& $this instanceof metadata_interface) { |
84
|
104 |
|
$this->metadata = new metadata($this); |
85
|
|
|
} |
86
|
|
|
|
87
|
130 |
|
return parent::__get($field); |
88
|
|
|
} |
89
|
|
|
|
90
|
1 |
|
public function __call($method, $args) |
91
|
|
|
{ |
92
|
1 |
|
if ($method === 'list') { |
93
|
1 |
|
return $this->_list(); |
94
|
|
|
} |
95
|
|
|
throw new \BadMethodCallException("Unknown method " . $method . " on " . get_class($this)); |
96
|
|
|
} |
97
|
|
|
|
98
|
4 |
|
protected function load_parent(array $candidates) : ?dbobject |
99
|
|
|
{ |
100
|
4 |
|
foreach ($candidates as $candidate) { |
101
|
4 |
|
if ( is_string($this->$candidate) |
102
|
4 |
|
&& mgd_is_guid($this->$candidate)) { |
103
|
|
|
return \midgard_object_class::get_object_by_guid($this->$candidate); |
104
|
|
|
} |
105
|
4 |
|
if ($this->$candidate !== null) { |
106
|
|
|
//Proxies become stale if the object itself is detached, so we have to re-fetch |
107
|
4 |
|
if ( $this->$candidate instanceof Proxy |
108
|
4 |
|
&& $this->$candidate->__isInitialized()) { |
109
|
|
|
try { |
110
|
1 |
|
$this->$candidate->get_by_id($this->$candidate->id); |
111
|
|
|
} catch (exception $e) { |
112
|
|
|
connection::log()->error('Failed to refresh parent from proxy: ' . $e->getMessage()); |
113
|
|
|
return null; |
114
|
|
|
} |
115
|
|
|
} |
116
|
4 |
|
return $this->$candidate; |
117
|
|
|
} |
118
|
|
|
} |
119
|
1 |
|
return null; |
120
|
|
|
} |
121
|
|
|
|
122
|
43 |
|
public function get_by_id(int $id) : bool |
123
|
|
|
{ |
124
|
43 |
|
$entity = connection::get_em()->find(get_class($this), $id); |
125
|
|
|
|
126
|
43 |
|
if ($entity === null) { |
127
|
3 |
|
throw exception::not_exists(); |
128
|
|
|
} |
129
|
|
|
// Normally, the proxy should be automatically loaded when accessing e.g. $entity->guid, |
130
|
|
|
// because that triggers the proxy's magic __get method, which initializes. But since mgdobject |
131
|
|
|
// is the entity's (grand)parent class, PHP rules dictate that we have access to |
132
|
|
|
// its parents' protected variables. Hence, we need to load explicitly. |
133
|
42 |
|
if ($entity instanceof Proxy && !$entity->__isInitialized()) { |
134
|
|
|
try { |
135
|
7 |
|
$entity->__load(); |
136
|
1 |
|
} catch (EntityNotFoundException) { |
137
|
1 |
|
throw exception::object_purged(); |
138
|
|
|
} |
139
|
|
|
} |
140
|
42 |
|
if ($entity instanceof metadata_interface && $entity->{metadata_interface::DELETED_FIELD}) { |
141
|
|
|
// This can happen when the "deleted" entity is still in EM's identity map |
142
|
|
|
throw exception::object_deleted(); |
143
|
|
|
} |
144
|
|
|
|
145
|
42 |
|
$this->populate_from_entity($entity); |
146
|
|
|
|
147
|
42 |
|
connection::get_em()->detach($entity); |
148
|
42 |
|
exception::ok(); |
149
|
42 |
|
return true; |
150
|
|
|
} |
151
|
|
|
|
152
|
16 |
|
public function get_by_guid(string $guid) : bool |
153
|
|
|
{ |
154
|
16 |
|
if (!mgd_is_guid($guid)) { |
155
|
1 |
|
throw new \InvalidArgumentException("'$guid' is not a valid guid"); |
156
|
|
|
} |
157
|
15 |
|
$entity = connection::get_em()->getRepository(get_class($this))->findOneBy(['guid' => $guid]); |
158
|
15 |
|
if ($entity === null) { |
159
|
|
|
throw exception::not_exists(); |
160
|
|
|
} |
161
|
15 |
|
$this->populate_from_entity($entity); |
162
|
|
|
|
163
|
15 |
|
connection::get_em()->detach($entity); |
164
|
15 |
|
exception::ok(); |
165
|
15 |
|
return true; |
166
|
|
|
} |
167
|
|
|
|
168
|
110 |
|
public function create() : bool |
169
|
|
|
{ |
170
|
110 |
|
$this->initialize(); |
171
|
|
|
|
172
|
110 |
|
if ($this->cm->getIdentifierValues($this)) { |
173
|
2 |
|
exception::duplicate(); |
174
|
2 |
|
return false; |
175
|
|
|
} |
176
|
110 |
|
if ( !$this->is_unique() |
177
|
110 |
|
|| !$this->check_parent()) { |
178
|
2 |
|
return false; |
179
|
|
|
} |
180
|
110 |
|
if (!$this->check_fields()) { |
181
|
1 |
|
return false; |
182
|
|
|
} |
183
|
|
|
try { |
184
|
109 |
|
$om = new objectmanager(connection::get_em()); |
185
|
109 |
|
$om->create($this); |
186
|
1 |
|
} catch (\Exception $e) { |
187
|
1 |
|
exception::internal($e); |
188
|
1 |
|
return false; |
189
|
|
|
} |
190
|
109 |
|
return (bool) $this->cm->getIdentifierValues($this); |
191
|
|
|
} |
192
|
|
|
|
193
|
16 |
|
public function update() : bool |
194
|
|
|
{ |
195
|
16 |
|
$this->initialize(); |
196
|
|
|
|
197
|
16 |
|
if (!$this->cm->getIdentifierValues($this)) { |
198
|
1 |
|
midgard_connection::get_instance()->set_error(MGD_ERR_INTERNAL); |
199
|
1 |
|
return false; |
200
|
|
|
} |
201
|
15 |
|
if (!$this->check_fields()) { |
202
|
2 |
|
return false; |
203
|
|
|
} |
204
|
|
|
try { |
205
|
13 |
|
$om = new objectmanager(connection::get_em()); |
206
|
13 |
|
$om->update($this); |
207
|
|
|
} catch (\Exception $e) { |
208
|
|
|
exception::internal($e); |
209
|
|
|
return false; |
210
|
|
|
} |
211
|
|
|
|
212
|
13 |
|
return true; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* @todo: Tests indicate that $check_dependencies is ignored in the mgd2 extension, |
217
|
|
|
* so we might consider ignoring it, too |
218
|
|
|
*/ |
219
|
29 |
|
public function delete(bool $check_dependencies = true) : bool |
220
|
|
|
{ |
221
|
29 |
|
$this->initialize(); |
222
|
|
|
|
223
|
29 |
|
if (!$this->cm->getIdentifierValues($this)) { |
224
|
1 |
|
exception::invalid_property_value(); |
225
|
1 |
|
return false; |
226
|
|
|
} |
227
|
28 |
|
if ( $check_dependencies |
228
|
28 |
|
&& $this->has_dependents()) { |
229
|
4 |
|
exception::has_dependants(); |
230
|
4 |
|
return false; |
231
|
|
|
} |
232
|
28 |
|
if (!($this instanceof metadata_interface)) { |
233
|
1 |
|
exception::invalid_property_value(); |
234
|
1 |
|
return false; |
235
|
|
|
} |
236
|
27 |
|
if ($this->{metadata_interface::DELETED_FIELD}) { |
237
|
1 |
|
return true; |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
try { |
241
|
27 |
|
$om = new objectmanager(connection::get_em()); |
242
|
27 |
|
$om->delete($this); |
243
|
|
|
} catch (\Exception $e) { |
244
|
|
|
exception::internal($e); |
245
|
|
|
return false; |
246
|
|
|
} |
247
|
|
|
|
248
|
27 |
|
return true; |
249
|
|
|
} |
250
|
|
|
|
251
|
110 |
|
private function is_unique() : bool |
252
|
|
|
{ |
253
|
110 |
|
if (empty($this->cm->midgard['unique_fields'])) { |
254
|
104 |
|
return true; |
255
|
|
|
} |
256
|
|
|
|
257
|
8 |
|
$qb = connection::get_em()->createQueryBuilder(); |
258
|
8 |
|
$qb->from(get_class($this), 'c'); |
259
|
8 |
|
$conditions = $qb->expr()->andX(); |
260
|
8 |
|
$parameters = []; |
261
|
8 |
|
if ($identifier = $this->cm->getIdentifierValues($this)) { |
262
|
|
|
$parameters = array_merge($parameters, $identifier); |
263
|
|
|
$field = key($identifier); |
264
|
|
|
$conditions->add($qb->expr()->neq('c.' . $field, ':' . $field)); |
265
|
|
|
} |
266
|
8 |
|
$found = false; |
267
|
8 |
|
foreach ($this->cm->midgard['unique_fields'] as $field) { |
268
|
8 |
|
if (empty($this->$field)) { |
269
|
|
|
//empty names automatically pass according to Midgard logic |
270
|
2 |
|
continue; |
271
|
|
|
} |
272
|
7 |
|
$conditions->add($qb->expr()->eq('c.' . $field, ':' . $field)); |
273
|
7 |
|
$parameters[$field] = $this->$field; |
274
|
7 |
|
$found = true; |
275
|
|
|
} |
276
|
|
|
|
277
|
8 |
|
if (!$found) { |
278
|
2 |
|
return true; |
279
|
|
|
} |
280
|
|
|
|
281
|
7 |
|
foreach (['upfield', 'parentfield'] as $candidate) { |
282
|
7 |
|
if (!empty($this->cm->midgard[$candidate])) { |
283
|
|
|
// TODO: This needs to be changed so that value is always numeric, since this is how midgard does it |
284
|
6 |
|
if ($this->{$this->cm->midgard[$candidate]} === null) { |
285
|
6 |
|
$conditions->add($qb->expr()->isNull('c.' . $this->cm->midgard[$candidate])); |
286
|
|
|
} else { |
287
|
6 |
|
$conditions->add($qb->expr()->eq('c.' . $this->cm->midgard[$candidate], ':' . $this->cm->midgard[$candidate])); |
288
|
6 |
|
$parameters[$this->cm->midgard[$candidate]] = $this->{$this->cm->midgard[$candidate]}; |
289
|
|
|
} |
290
|
6 |
|
break; |
291
|
|
|
} |
292
|
|
|
} |
293
|
|
|
|
294
|
7 |
|
$qb->where($conditions) |
295
|
7 |
|
->setParameters($parameters); |
296
|
|
|
|
297
|
7 |
|
$qb->select("count(c)"); |
298
|
7 |
|
$count = (int) $qb->getQuery()->getSingleScalarResult(); |
299
|
|
|
|
300
|
7 |
|
if ($count !== 0) { |
301
|
1 |
|
exception::object_name_exists(); |
302
|
1 |
|
return false; |
303
|
|
|
} |
304
|
7 |
|
return true; |
305
|
|
|
} |
306
|
|
|
|
307
|
110 |
|
private function check_parent() : bool |
308
|
|
|
{ |
309
|
110 |
|
if ( empty($this->cm->midgard['parentfield']) |
310
|
110 |
|
|| empty($this->cm->midgard['parent'])) { |
311
|
110 |
|
return true; |
312
|
|
|
} |
313
|
|
|
|
314
|
8 |
|
if (empty($this->{$this->cm->midgard['parentfield']})) { |
315
|
1 |
|
exception::object_no_parent(); |
316
|
1 |
|
return false; |
317
|
|
|
} |
318
|
8 |
|
return true; |
319
|
|
|
} |
320
|
|
|
|
321
|
110 |
|
private function check_fields() : bool |
322
|
|
|
{ |
323
|
110 |
|
foreach ($this->cm->fieldMappings as $name => $field) { |
324
|
110 |
|
if ( $field['midgard:midgard_type'] == translator::TYPE_GUID |
325
|
110 |
|
&& !empty($this->$name) |
326
|
110 |
|
&& !mgd_is_guid($this->$name)) { |
327
|
2 |
|
exception::invalid_property_value("'" . $name . "' property's value is not a guid."); |
328
|
2 |
|
return false; |
329
|
|
|
} |
330
|
|
|
} |
331
|
109 |
|
return $this->check_upfield(); |
332
|
|
|
} |
333
|
|
|
|
334
|
109 |
|
private function check_upfield() : bool |
335
|
|
|
{ |
336
|
109 |
|
$identifier = $this->cm->getIdentifierValues($this); |
337
|
109 |
|
if ( $identifier |
|
|
|
|
338
|
109 |
|
&& !empty($this->cm->midgard['upfield']) |
339
|
109 |
|
&& $this->__get($this->cm->midgard['upfield']) === reset($identifier) |
340
|
109 |
|
&& $this->cm->getAssociationMapping($this->cm->midgard['upfield'])['targetEntity'] === $this->cm->getName()) { |
341
|
1 |
|
exception::tree_is_circular(); |
342
|
1 |
|
return false; |
343
|
|
|
} |
344
|
|
|
// @todo this should be recursive |
345
|
109 |
|
return true; |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
public function is_in_parent_tree($root_id, $id) : bool |
349
|
|
|
{ |
350
|
|
|
return false; |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
public function is_in_tree($root_id, $id) : bool |
354
|
|
|
{ |
355
|
|
|
return false; |
356
|
|
|
} |
357
|
|
|
|
358
|
35 |
|
public function has_dependents() : bool |
359
|
|
|
{ |
360
|
35 |
|
$this->initialize(); |
361
|
|
|
|
362
|
35 |
|
$stat = false; |
363
|
|
|
|
364
|
35 |
|
if (!empty($this->cm->midgard['upfield'])) { |
365
|
30 |
|
$identifier = $this->cm->getIdentifierValues($this); |
366
|
30 |
|
$qb = connection::get_em()->createQueryBuilder(); |
367
|
30 |
|
$qb->from(get_class($this), 'c') |
368
|
30 |
|
->where('c.' . $this->cm->midgard['upfield'] . ' = ?0') |
369
|
30 |
|
->setParameter(0, (int) reset($identifier)) |
370
|
30 |
|
->select("COUNT(c)"); |
371
|
30 |
|
$results = (int) $qb->getQuery()->getSingleScalarResult(); |
372
|
30 |
|
$stat = $results > 0; |
373
|
|
|
} |
374
|
|
|
|
375
|
35 |
|
if ( !$stat |
376
|
35 |
|
&& !empty($this->cm->midgard['childtypes'])) { |
377
|
28 |
|
foreach ($this->cm->midgard['childtypes'] as $typename => $parentfield) { |
378
|
28 |
|
$identifier = $this->cm->getIdentifierValues($this); |
379
|
28 |
|
$qb = connection::get_em()->createQueryBuilder(); |
380
|
28 |
|
$qb->from(connection::get_fqcn($typename), 'c') |
381
|
28 |
|
->where('c.' . $parentfield . ' = ?0') |
382
|
28 |
|
->setParameter(0, (int) reset($identifier)) |
383
|
28 |
|
->select("COUNT(c)"); |
384
|
|
|
|
385
|
28 |
|
$results = (int) $qb->getQuery()->getSingleScalarResult(); |
386
|
28 |
|
$stat = $results > 0; |
387
|
28 |
|
if ($stat) { |
388
|
3 |
|
break; |
389
|
|
|
} |
390
|
|
|
} |
391
|
|
|
} |
392
|
|
|
|
393
|
35 |
|
return $stat; |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
public function get_parent() |
397
|
|
|
{ |
398
|
|
|
return null; |
399
|
|
|
} |
400
|
|
|
|
401
|
|
|
/** |
402
|
|
|
* This function is called list() in Midgard, but that doesn't work in plain PHP |
403
|
|
|
*/ |
404
|
1 |
|
private function _list() : array |
405
|
|
|
{ |
406
|
1 |
|
$this->initialize(); |
407
|
|
|
|
408
|
1 |
|
if (!empty($this->cm->midgard['upfield'])) { |
409
|
1 |
|
$identifier = $this->cm->getIdentifierValues($this); |
410
|
1 |
|
$qb = connection::get_em()->createQueryBuilder(); |
411
|
1 |
|
$qb->from(get_class($this), 'c') |
412
|
1 |
|
->where('c.' . $this->cm->midgard['upfield'] . ' = ?0') |
413
|
1 |
|
->setParameter(0, (int) reset($identifier)) |
414
|
1 |
|
->select("c"); |
415
|
1 |
|
return $qb->getQuery()->getResult(); |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
return []; |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* This should return child objects, but only if they are of a different type |
423
|
|
|
* For all other input, an empty array is returned |
424
|
|
|
* (not implemented yet) |
425
|
|
|
*/ |
426
|
|
|
public function list_children(string $classname) : array |
427
|
|
|
{ |
428
|
|
|
return []; |
429
|
|
|
} |
430
|
|
|
|
431
|
1 |
|
public function get_by_path(string $path) : bool |
432
|
|
|
{ |
433
|
1 |
|
$parts = explode('/', trim($path, '/')); |
434
|
1 |
|
if (empty($parts)) { |
435
|
|
|
return false; |
436
|
|
|
} |
437
|
1 |
|
$this->initialize(); |
438
|
|
|
|
439
|
1 |
|
if (count($this->cm->midgard['unique_fields']) != 1) { |
440
|
|
|
return false; |
441
|
|
|
} |
442
|
|
|
|
443
|
1 |
|
$field = $this->cm->midgard['unique_fields'][0]; |
444
|
|
|
|
445
|
1 |
|
if (!empty($this->cm->midgard['parent'])) { |
446
|
1 |
|
$parent_cm = connection::get_em()->getClassMetadata(connection::get_fqcn($this->cm->midgard['parent'])); |
447
|
1 |
|
$parentclass = $this->cm->fullyQualifiedClassName($this->cm->midgard['parent']); |
448
|
1 |
|
$parentfield = $parent_cm->midgard['upfield']; |
|
|
|
|
449
|
1 |
|
$upfield = $this->cm->midgard['parentfield']; |
450
|
1 |
|
} elseif (!empty($this->cm->midgard['upfield'])) { |
451
|
1 |
|
$parentclass = get_class($this); |
452
|
1 |
|
$upfield = $this->cm->midgard['upfield']; |
453
|
1 |
|
$parentfield = $upfield; |
454
|
|
|
} else { |
455
|
|
|
return false; |
456
|
|
|
} |
457
|
|
|
|
458
|
1 |
|
$name = array_pop($parts); |
459
|
1 |
|
$up = 0; |
460
|
1 |
|
foreach ($parts as $part) { |
461
|
1 |
|
$qb = $this->get_uniquefield_query($parentclass, $field, $part, $parentfield, $up); |
|
|
|
|
462
|
1 |
|
$qb->select("c.id"); |
463
|
1 |
|
$up = (int) $qb->getQuery()->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR); |
464
|
1 |
|
if ($up === 0) { |
465
|
1 |
|
return $this->reset(); |
466
|
|
|
} |
467
|
|
|
} |
468
|
|
|
|
469
|
1 |
|
$qb = $this->get_uniquefield_query(get_class($this), $field, $name, $upfield, $up); |
470
|
1 |
|
$qb->select("c"); |
471
|
|
|
|
472
|
1 |
|
$entity = $qb->getQuery()->getOneOrNullResult(); |
473
|
|
|
|
474
|
1 |
|
if ($entity === null) { |
475
|
1 |
|
return $this->reset(); |
476
|
|
|
} |
477
|
1 |
|
$this->populate_from_entity($entity); |
|
|
|
|
478
|
|
|
|
479
|
1 |
|
return true; |
480
|
|
|
} |
481
|
|
|
|
482
|
1 |
|
private function reset() : bool |
483
|
|
|
{ |
484
|
1 |
|
exception::not_exists(); |
485
|
1 |
|
foreach ($this->cm->getIdentifierFieldNames() as $field) { |
486
|
1 |
|
$this->$field = 0; |
487
|
|
|
} |
488
|
1 |
|
$this->set_guid(''); |
489
|
1 |
|
return false; |
490
|
|
|
} |
491
|
|
|
|
492
|
1 |
|
protected function get_uniquefield_query(string $classname, string $field, string $part, string $upfield, int $up) : QueryBuilder |
493
|
|
|
{ |
494
|
1 |
|
$qb = connection::get_em()->createQueryBuilder(); |
495
|
1 |
|
$qb->from($classname, 'c'); |
496
|
1 |
|
$conditions = $qb->expr()->andX(); |
497
|
1 |
|
$conditions->add($qb->expr()->eq('c.' . $field, ':' . $field)); |
498
|
1 |
|
$parameters = [ |
499
|
1 |
|
$field => $part |
500
|
1 |
|
]; |
501
|
|
|
|
502
|
1 |
|
if (empty($up)) { |
503
|
|
|
// If the database was created by Midgard, it might contain 0 instead of NULL, so... |
504
|
1 |
|
$empty_conditions = $qb->expr()->orX() |
505
|
1 |
|
->add($qb->expr()->isNull('c.' . $upfield)) |
506
|
1 |
|
->add($qb->expr()->eq('c.' . $upfield, '0')); |
507
|
1 |
|
$conditions->add($empty_conditions); |
508
|
|
|
} else { |
509
|
1 |
|
$conditions->add($qb->expr()->eq('c.' . $upfield, ':' . $upfield)); |
510
|
1 |
|
$parameters[$upfield] = $up; |
511
|
|
|
} |
512
|
|
|
|
513
|
1 |
|
$qb->where($conditions) |
514
|
1 |
|
->setParameters($parameters); |
515
|
|
|
|
516
|
1 |
|
return $qb; |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
/** |
520
|
|
|
* @return boolean |
521
|
|
|
*/ |
522
|
|
|
public function parent() |
523
|
|
|
{ |
524
|
|
|
return false; |
525
|
|
|
} |
526
|
|
|
|
527
|
1 |
|
public function has_parameters() : bool |
528
|
|
|
{ |
529
|
1 |
|
return !$this->get_collection('midgard_parameter')->is_empty($this->guid); |
530
|
|
|
} |
531
|
|
|
|
532
|
4 |
|
public function list_parameters(?string $domain = null) : array |
533
|
|
|
{ |
534
|
4 |
|
$constraints = []; |
535
|
4 |
|
if ($domain) { |
536
|
1 |
|
$constraints = ["domain" => $domain]; |
537
|
|
|
} |
538
|
|
|
|
539
|
4 |
|
return $this->get_collection('midgard_parameter')->find($this->guid, $constraints); |
540
|
|
|
} |
541
|
|
|
|
542
|
3 |
|
public function find_parameters(array $constraints = []) : array |
543
|
|
|
{ |
544
|
3 |
|
return $this->get_collection('midgard_parameter')->find($this->guid, $constraints); |
545
|
|
|
} |
546
|
|
|
|
547
|
1 |
|
public function delete_parameters(array $constraints = []) : int |
548
|
|
|
{ |
549
|
1 |
|
return $this->get_collection('midgard_parameter')->delete($this->guid, $constraints); |
550
|
|
|
} |
551
|
|
|
|
552
|
1 |
|
public function purge_parameters(array $constraints = []) : int |
553
|
|
|
{ |
554
|
1 |
|
return $this->get_collection('midgard_parameter')->purge($this->guid, $constraints); |
555
|
|
|
} |
556
|
|
|
|
557
|
2 |
|
public function get_parameter(string $domain, string $name) |
558
|
|
|
{ |
559
|
2 |
|
if (!$this->guid) { |
560
|
1 |
|
return false; |
561
|
|
|
} |
562
|
2 |
|
$qb = connection::get_em()->createQueryBuilder(); |
563
|
2 |
|
$qb |
564
|
2 |
|
->select('c.value') |
565
|
2 |
|
->from(connection::get_fqcn('midgard_parameter'), 'c') |
566
|
2 |
|
->where('c.domain = :domain AND c.name = :name AND c.parentguid = :parentguid') |
567
|
2 |
|
->setParameters(['domain' => $domain, 'name' => $name, 'parentguid' => $this->guid]); |
568
|
|
|
|
569
|
2 |
|
return $qb->getQuery()->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR); |
570
|
|
|
} |
571
|
|
|
|
572
|
11 |
|
public function set_parameter(string $domain, string $name, $value) : bool |
573
|
|
|
{ |
574
|
11 |
|
$constraints = [ |
575
|
11 |
|
'domain' => $domain, |
576
|
11 |
|
'name' => $name, |
577
|
11 |
|
]; |
578
|
11 |
|
$params = $this->get_collection('midgard_parameter')->find($this->guid, $constraints); |
579
|
|
|
|
580
|
|
|
// check value |
581
|
11 |
|
if (in_array($value, [false, null, ''], true)) { |
582
|
2 |
|
if (empty($params)) { |
583
|
1 |
|
exception::not_exists(); |
584
|
1 |
|
return false; |
585
|
|
|
} |
586
|
2 |
|
foreach ($params as $param) { |
587
|
2 |
|
$stat = $param->delete(); |
588
|
|
|
} |
589
|
2 |
|
return $stat; |
|
|
|
|
590
|
|
|
} |
591
|
|
|
|
592
|
11 |
|
$om = new objectmanager(connection::get_em()); |
593
|
|
|
try { |
594
|
|
|
// create new |
595
|
11 |
|
if (empty($params)) { |
596
|
11 |
|
$parameter = $om->new_instance(connection::get_fqcn('midgard_parameter')); |
597
|
11 |
|
$parameter->parentguid = $this->guid; |
|
|
|
|
598
|
11 |
|
$parameter->domain = $domain; |
|
|
|
|
599
|
11 |
|
$parameter->name = $name; |
|
|
|
|
600
|
11 |
|
$parameter->value = $value; |
|
|
|
|
601
|
11 |
|
$om->create($parameter); |
602
|
|
|
} |
603
|
|
|
// use existing |
604
|
|
|
else { |
605
|
1 |
|
$parameter = $params[0]; |
606
|
1 |
|
$parameter->value = $value; |
|
|
|
|
607
|
1 |
|
$om->update($parameter); |
608
|
|
|
} |
609
|
11 |
|
return true; |
610
|
|
|
} catch (\Exception $e) { |
611
|
|
|
exception::internal($e); |
612
|
|
|
return false; |
613
|
|
|
} |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
/** |
617
|
|
|
* The signature is a little different from original, because Doctrine doesn't support func_get_args() in proxies |
618
|
|
|
*/ |
619
|
2 |
|
public function parameter(string $domain, string $name, $value = '__UNINITIALIZED__') |
620
|
|
|
{ |
621
|
2 |
|
if ($value === '__UNINITIALIZED__') { |
622
|
1 |
|
return $this->get_parameter($domain, $name); |
623
|
|
|
} |
624
|
2 |
|
return $this->set_parameter($domain, $name, $value); |
625
|
|
|
} |
626
|
|
|
|
627
|
1 |
|
public function has_attachments() : bool |
628
|
|
|
{ |
629
|
1 |
|
return !$this->get_collection('midgard_attachment')->is_empty($this->guid); |
630
|
|
|
} |
631
|
|
|
|
632
|
2 |
|
public function list_attachments() : array |
633
|
|
|
{ |
634
|
2 |
|
return $this->get_collection('midgard_attachment')->find($this->guid, []); |
635
|
|
|
} |
636
|
|
|
|
637
|
|
|
public function find_attachments(array $constraints = []) : array |
638
|
|
|
{ |
639
|
|
|
return $this->get_collection('midgard_attachment')->find($this->guid, $constraints); |
640
|
|
|
} |
641
|
|
|
|
642
|
|
|
public function delete_attachments(array $constraints = []) : int |
643
|
|
|
{ |
644
|
|
|
return $this->get_collection('midgard_attachment')->delete($this->guid, $constraints); |
645
|
|
|
} |
646
|
|
|
|
647
|
|
|
/** |
648
|
|
|
* @return boolean False if one or more attachments couldn't be deleted |
649
|
|
|
* @todo Implement delete_blob & return value |
650
|
|
|
*/ |
651
|
|
|
public function purge_attachments(array $constraints = [], bool $delete_blob = true) |
652
|
|
|
{ |
653
|
|
|
return $this->get_collection('midgard_attachment')->purge($this->guid, $constraints); |
654
|
|
|
} |
655
|
|
|
|
656
|
3 |
|
public function create_attachment(string $name, string $title = '', string $mimetype = '') : ?attachment |
657
|
|
|
{ |
658
|
3 |
|
$existing = $this->get_collection('midgard_attachment')->find($this->guid, ['name' => $name]); |
659
|
3 |
|
if (!empty($existing)) { |
660
|
1 |
|
exception::object_name_exists(); |
661
|
1 |
|
return null; |
662
|
|
|
} |
663
|
3 |
|
$om = new objectmanager(connection::get_em()); |
664
|
3 |
|
$att = $om->new_instance(connection::get_fqcn('midgard_attachment')); |
665
|
|
|
|
666
|
3 |
|
$att->parentguid = $this->guid; |
|
|
|
|
667
|
3 |
|
$att->title = $title; |
|
|
|
|
668
|
3 |
|
$att->name = $name; |
|
|
|
|
669
|
3 |
|
$att->mimetype = $mimetype; |
|
|
|
|
670
|
|
|
try { |
671
|
3 |
|
$om->create($att); |
672
|
3 |
|
return $att; |
673
|
|
|
} catch (\Exception $e) { |
674
|
|
|
exception::internal($e); |
675
|
|
|
return null; |
676
|
|
|
} |
677
|
|
|
} |
678
|
|
|
|
679
|
|
|
/** |
680
|
|
|
* @todo: Tests indicate that $check_dependencies is ignored in the mgd2 extension, |
681
|
|
|
* so we might consider ignoring it, too |
682
|
|
|
*/ |
683
|
17 |
|
public function purge(bool $check_dependencies = true) : bool |
684
|
|
|
{ |
685
|
17 |
|
if (!$this->cm->getIdentifierValues($this)) { |
686
|
|
|
// This usually means that the object has been purged already |
687
|
|
|
exception::not_exists(); |
688
|
|
|
return false; |
689
|
|
|
} |
690
|
17 |
|
if ( $check_dependencies |
691
|
17 |
|
&& $this->has_dependents()) { |
692
|
2 |
|
exception::has_dependants(); |
693
|
2 |
|
return false; |
694
|
|
|
} |
695
|
|
|
|
696
|
|
|
try { |
697
|
17 |
|
$om = new objectmanager(connection::get_em()); |
698
|
17 |
|
$om->purge($this); |
699
|
2 |
|
} catch (\Doctrine\ORM\EntityNotFoundException) { |
700
|
2 |
|
exception::not_exists(); |
701
|
2 |
|
return false; |
702
|
|
|
} catch (\Exception $e) { |
703
|
|
|
exception::internal($e); |
704
|
|
|
return false; |
705
|
|
|
} |
706
|
|
|
|
707
|
17 |
|
return true; |
708
|
|
|
} |
709
|
|
|
|
710
|
2 |
|
public static function undelete(string $guid) : bool |
711
|
|
|
{ |
712
|
2 |
|
return \midgard_object_class::undelete($guid); |
713
|
|
|
} |
714
|
|
|
|
715
|
1 |
|
public static function new_query_builder() : \midgard_query_builder |
716
|
|
|
{ |
717
|
1 |
|
return new \midgard_query_builder(static::class); |
718
|
|
|
} |
719
|
|
|
|
720
|
1 |
|
public static function new_collector(string $field, $value) : \midgard_collector |
721
|
|
|
{ |
722
|
1 |
|
return new \midgard_collector(static::class, $field, $value); |
723
|
|
|
} |
724
|
|
|
|
725
|
|
|
public static function new_reflection_property() : \midgard_reflection_property |
726
|
|
|
{ |
727
|
|
|
return new \midgard_reflection_property(static::class); |
728
|
|
|
} |
729
|
|
|
|
730
|
110 |
|
public function set_guid(string $guid) |
731
|
|
|
{ |
732
|
110 |
|
parent::__set('guid', $guid); |
733
|
|
|
} |
734
|
|
|
|
735
|
|
|
/** |
736
|
|
|
* Helper for managing the isapproved and islocked metadata properties |
737
|
|
|
*/ |
738
|
8 |
|
private function manage_meta_property(string $action, bool $value) : bool |
739
|
|
|
{ |
740
|
8 |
|
if (!($this instanceof metadata_interface)) { |
741
|
4 |
|
exception::no_metadata(); |
742
|
4 |
|
return false; |
743
|
|
|
} |
744
|
4 |
|
$user = connection::get_user(); |
745
|
4 |
|
if ($user === null) { |
746
|
4 |
|
exception::access_denied(); |
747
|
4 |
|
return false; |
748
|
|
|
} |
749
|
4 |
|
if ($action == 'lock') { |
750
|
2 |
|
$flag = 'islocked'; |
751
|
2 |
|
} elseif ($action == 'approve') { |
752
|
2 |
|
$flag = 'isapproved'; |
753
|
|
|
} else { |
754
|
|
|
throw new exception('Unsupported action ' . $action); |
755
|
|
|
} |
756
|
|
|
// same val |
757
|
4 |
|
if ($this->__get('metadata')->$flag === $value) { |
758
|
3 |
|
return false; |
759
|
|
|
} |
760
|
4 |
|
if ($value === false) { |
761
|
2 |
|
$action = 'un' . $action; |
762
|
|
|
} |
763
|
|
|
|
764
|
4 |
|
if ($this->cm->getIdentifierValues($this)) { |
765
|
|
|
try { |
766
|
4 |
|
$om = new objectmanager(connection::get_em()); |
767
|
4 |
|
$om->{$action}($this); |
768
|
|
|
} catch (\Exception $e) { |
769
|
|
|
exception::internal($e); |
770
|
|
|
return false; |
771
|
|
|
} |
772
|
|
|
} |
773
|
|
|
|
774
|
4 |
|
return true; |
775
|
|
|
} |
776
|
|
|
|
777
|
3 |
|
public function approve() : bool |
778
|
|
|
{ |
779
|
3 |
|
return $this->manage_meta_property("approve", true); |
780
|
|
|
} |
781
|
|
|
|
782
|
2 |
|
public function is_approved() : bool |
783
|
|
|
{ |
784
|
2 |
|
if (!($this instanceof metadata_interface)) { |
785
|
|
|
exception::no_metadata(); |
786
|
|
|
return false; |
787
|
|
|
} |
788
|
2 |
|
return $this->metadata_isapproved; |
|
|
|
|
789
|
|
|
} |
790
|
|
|
|
791
|
2 |
|
public function unapprove() : bool |
792
|
|
|
{ |
793
|
2 |
|
return $this->manage_meta_property("approve", false); |
794
|
|
|
} |
795
|
|
|
|
796
|
3 |
|
public function lock() : bool |
797
|
|
|
{ |
798
|
3 |
|
if ($this->is_locked()) { |
799
|
1 |
|
exception::object_is_locked(); |
800
|
1 |
|
return false; |
801
|
|
|
} |
802
|
3 |
|
return $this->manage_meta_property("lock", true); |
803
|
|
|
} |
804
|
|
|
|
805
|
3 |
|
public function is_locked() : bool |
806
|
|
|
{ |
807
|
3 |
|
if (!($this instanceof metadata_interface)) { |
808
|
1 |
|
exception::no_metadata(); |
809
|
1 |
|
return false; |
810
|
|
|
} |
811
|
2 |
|
return $this->metadata_islocked; |
|
|
|
|
812
|
|
|
} |
813
|
|
|
|
814
|
2 |
|
public function unlock() : bool |
815
|
|
|
{ |
816
|
2 |
|
return $this->manage_meta_property("lock", false); |
817
|
|
|
} |
818
|
|
|
} |
819
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.