Newer
Older
<?php
/**
* @file
* Contains \Drupal\Core\Entity\EntityBCDecorator.
*/
namespace Drupal\Core\Entity;
use Drupal\Core\Language\Language;
use IteratorAggregate;
use Drupal\Core\Entity\EntityInterface;
catch
committed
use Drupal\Core\TypedData\TypedDataInterface;
/**
* Provides backwards compatible (BC) access to entity fields.
*
* Allows using entities converted to the new Entity Field API with the previous
* way of accessing fields or properties. For example, via the backwards
* compatible (BC) decorator you can do:
* @code
* $node->title = $value;
* $node->body[LANGUAGE_NONE][0]['value'] = $value;
* @endcode
* Without the BC decorator the same assignment would have to look like this:
* @code
* $node->title->value = $value;
* $node->body->value = $value;
* @endcode
* Without the BC decorator the language always default to the entity language,
* whereas a specific translation can be access via the getTranslation() method.
*
* The BC decorator should be only used during conversion to the new entity
* field API, such that existing code can be converted iteratively. Any new code
* should directly use the new entity field API and avoid using the
* EntityBCDecorator, if possible.
*
* @todo: Remove once everything is converted to use the new entity field API.
*/
class EntityBCDecorator implements IteratorAggregate, EntityInterface {
/**
* The EntityInterface object being decorated.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $decorated;
Angie Byron
committed
/**
* Local cache for field definitions.
*
* @var array
*/
protected $definitions;
/**
* Constructs a Drupal\Core\Entity\EntityCompatibilityDecorator object.
*
* @param \Drupal\Core\Entity\EntityInterface $decorated
* The decorated entity.
Angie Byron
committed
* @param array &$definitions
* An array of field definitions.
*/
Angie Byron
committed
function __construct(EntityNG $decorated, array &$definitions) {
$this->decorated = $decorated;
Angie Byron
committed
$this->definitions = &$definitions;
}
/**
* Overrides Entity::getNGEntity().
*/
public function getNGEntity() {
return $this->decorated;
}
/**
* Overrides Entity::getBCEntity().
*/
public function getBCEntity() {
return $this;
}
/**
* Implements the magic method for getting object properties.
*
* Directly accesses the plain field values, as done in Drupal 7.
*/
public function &__get($name) {
Angie Byron
committed
// Directly return the original property.
if ($name == 'original') {
return $this->decorated->values[$name];
}
// We access the protected 'values' and 'fields' properties of the decorated
// entity via the magic getter - which returns them by reference for us. We
// do so, as providing references to these arrays would make $entity->values
// and $entity->fields reference themselves, which is problematic during
// __clone() (this is something we cannot work-a-round easily as an unset()
// on the variable is problematic in conjunction with the magic
// getter/setter).
if (!empty($this->decorated->fields[$name])) {
// Any field value set via the new Entity Field API will be stored inside
// the field objects managed by the entity, thus we need to ensure
// $this->decorated->values reflects the latest values first.
foreach ($this->decorated->fields[$name] as $langcode => $field) {
Angie Byron
committed
// Only set if it's not empty, otherwise there can be ghost values.
if (!$field->isEmpty()) {
$this->decorated->values[$name][$langcode] = $field->getValue();
}
}
// The returned values might be changed by reference, so we need to remove
// the field object to avoid the field object and the value getting out of
// sync. That way, the next field object instantiated by EntityNG will
// receive the possibly updated value.
unset($this->decorated->fields[$name]);
}
Angie Byron
committed
// When accessing values for entity properties that have been converted to
// an entity field, provide direct access to the plain value. This makes it
// possible to use the BC-decorator with properties; e.g., $node->title.
if (isset($this->definitions[$name]) && empty($this->definitions[$name]['configurable'])) {
if (!isset($this->decorated->values[$name][Language::LANGCODE_DEFAULT])) {
$this->decorated->values[$name][Language::LANGCODE_DEFAULT][0]['value'] = NULL;
Angie Byron
committed
}
if (is_array($this->decorated->values[$name][Language::LANGCODE_DEFAULT])) {
Angie Byron
committed
// This will work with all defined properties that have a single value.
// We need to ensure the key doesn't matter. Mostly it's 'value' but
// e.g. EntityReferenceItem uses target_id.
if (isset($this->decorated->values[$name][Language::LANGCODE_DEFAULT][0]) && count($this->decorated->values[$name][Language::LANGCODE_DEFAULT][0]) == 1) {
return $this->decorated->values[$name][Language::LANGCODE_DEFAULT][0][key($this->decorated->values[$name][Language::LANGCODE_DEFAULT][0])];
Angie Byron
committed
}
Dries Buytaert
committed
}
return $this->decorated->values[$name][Language::LANGCODE_DEFAULT];
}
Angie Byron
committed
else {
// Allow accessing field values in an entity default language other than
// Language::LANGCODE_DEFAULT by mapping the values to
// Language::LANGCODE_DEFAULT. This is necessary as EntityNG always keys
// default language values with Language::LANGCODE_DEFAULT while field API
// expects them to be keyed by langcode.
Angie Byron
committed
$langcode = $this->decorated->language()->langcode;
if ($langcode != Language::LANGCODE_DEFAULT && isset($this->decorated->values[$name]) && is_array($this->decorated->values[$name])) {
if (isset($this->decorated->values[$name][Language::LANGCODE_DEFAULT]) && !isset($this->decorated->values[$name][$langcode])) {
$this->decorated->values[$name][$langcode] = &$this->decorated->values[$name][Language::LANGCODE_DEFAULT];
Angie Byron
committed
}
}
if (!isset($this->decorated->values[$name])) {
$this->decorated->values[$name] = NULL;
}
return $this->decorated->values[$name];
}
}
/**
* Implements the magic method for setting object properties.
*
* Directly writes to the plain field values, as done by Drupal 7.
*/
public function __set($name, $value) {
Angie Byron
committed
$defined = isset($this->definitions[$name]);
// When updating values for entity properties that have been converted to
// an entity field, directly write to the plain value. This makes it
// possible to use the BC-decorator with properties; e.g., $node->title.
if ($defined && empty($this->definitions[$name]['configurable'])) {
$this->decorated->values[$name][Language::LANGCODE_DEFAULT] = $value;
Angie Byron
committed
}
else {
if ($defined && is_array($value)) {
// If field API sets a value with a langcode in entity language, move it
// to Language::LANGCODE_DEFAULT.
Angie Byron
committed
// This is necessary as EntityNG always keys default language values
// with Language::LANGCODE_DEFAULT while field API expects them to be
// keyed by langcode.
Angie Byron
committed
foreach ($value as $langcode => $data) {
if ($langcode != Language::LANGCODE_DEFAULT && $langcode == $this->decorated->language()->langcode) {
$value[Language::LANGCODE_DEFAULT] = $data;
Angie Byron
committed
unset($value[$langcode]);
}
}
}
Angie Byron
committed
$this->decorated->values[$name] = $value;
}
// Remove the field object to avoid the field object and the value getting
// out of sync. That way, the next field object instantiated by EntityNG
// will hold the updated value.
unset($this->decorated->fields[$name]);
}
/**
* Implements the magic method for isset().
*/
public function __isset($name) {
$value = $this->__get($name);
return isset($value);
}
/**
* Implements the magic method for unset().
*/
public function __unset($name) {
Angie Byron
committed
// Set the value to NULL.
$value = &$this->__get($name);
Angie Byron
committed
$value = NULL;
}
/**
* Implements the magic method for clone().
*/
function __clone() {
$this->decorated = clone $this->decorated;
}
/**
* Forwards the call to the decorated entity.
*/
public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User $account = NULL) {
return $this->decorated->access($operation, $account);
}
/**
* Forwards the call to the decorated entity.
*/
public function get($property_name) {
Angie Byron
committed
// Ensure this works with not yet defined fields.
if (!isset($this->definitions[$property_name])) {
return $this->__get($property_name);
}
return $this->decorated->get($property_name);
}
/**
* Forwards the call to the decorated entity.
*/
catch
committed
public function set($property_name, $value, $notify = TRUE) {
Angie Byron
committed
// Ensure this works with not yet defined fields.
if (!isset($this->definitions[$property_name])) {
return $this->__set($property_name, $value);
}
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
return $this->decorated->set($property_name, $value);
}
/**
* Forwards the call to the decorated entity.
*/
public function getProperties($include_computed = FALSE) {
return $this->decorated->getProperties($include_computed);
}
/**
* Forwards the call to the decorated entity.
*/
public function getPropertyValues() {
return $this->decorated->getPropertyValues();
}
/**
* Forwards the call to the decorated entity.
*/
public function setPropertyValues($values) {
return $this->decorated->setPropertyValues($values);
}
/**
* Forwards the call to the decorated entity.
*/
public function getPropertyDefinition($name) {
return $this->decorated->getPropertyDefinition($name);
}
/**
* Forwards the call to the decorated entity.
*/
public function getPropertyDefinitions() {
return $this->decorated->getPropertyDefinitions();
}
/**
* Forwards the call to the decorated entity.
*/
public function isEmpty() {
return $this->decorated->isEmpty();
}
/**
* Forwards the call to the decorated entity.
*/
public function getIterator() {
return $this->decorated->getIterator();
}
/**
* Forwards the call to the decorated entity.
*/
public function id() {
return $this->decorated->id();
}
/**
* Forwards the call to the decorated entity.
*/
public function uuid() {
return $this->decorated->uuid();
}
/**
* Forwards the call to the decorated entity.
*/
public function isNew() {
return $this->decorated->isNew();
}
/**
* Forwards the call to the decorated entity.
*/
public function isNewRevision() {
return $this->decorated->isNewRevision();
}
/**
* Forwards the call to the decorated entity.
*/
public function setNewRevision($value = TRUE) {
return $this->decorated->setNewRevision($value);
}
/**
* Forwards the call to the decorated entity.
*/
public function enforceIsNew($value = TRUE) {
return $this->decorated->enforceIsNew($value);
}
/**
* Forwards the call to the decorated entity.
*/
public function entityType() {
return $this->decorated->entityType();
}
/**
* Forwards the call to the decorated entity.
*/
public function bundle() {
return $this->decorated->bundle();
}
/**
* Forwards the call to the decorated entity.
*/
public function label($langcode = NULL) {
return $this->decorated->label($langcode);
}
/**
* Forwards the call to the decorated entity.
*/
public function uri() {
return $this->decorated->uri();
}
/**
* Forwards the call to the decorated entity.
*/
public function save() {
return $this->decorated->save();
}
/**
* Forwards the call to the decorated entity.
*/
public function delete() {
return $this->decorated->delete();
}
/**
* Forwards the call to the decorated entity.
*/
public function createDuplicate() {
return $this->decorated->createDuplicate();
}
/**
* Forwards the call to the decorated entity.
*/
public function entityInfo() {
return $this->decorated->entityInfo();
}
/**
* Forwards the call to the decorated entity.
*/
public function getRevisionId() {
return $this->decorated->getRevisionId();
}
/**
* Forwards the call to the decorated entity.
*/
public function isDefaultRevision($new_value = NULL) {
return $this->decorated->isDefaultRevision($new_value);
}
/**
* Forwards the call to the decorated entity.
*/
public function language() {
return $this->decorated->language();
}
/**
* Forwards the call to the decorated entity.
*/
public function getTranslationLanguages($include_default = TRUE) {
return $this->decorated->getTranslationLanguages($include_default);
}
/**
* Forwards the call to the decorated entity.
*/
public function getTranslation($langcode, $strict = TRUE) {
return $this->decorated->getTranslation($langcode, $strict);
}
catch
committed
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
/**
* Forwards the call to the decorated entity.
*/
public function getType() {
return $this->decorated->getType();
}
/**
* Forwards the call to the decorated entity.
*/
public function getDefinition() {
return $this->decorated->getDefinition();
}
/**
* Forwards the call to the decorated entity.
*/
public function getValue() {
return $this->decorated->getValue();
}
/**
* Forwards the call to the decorated entity.
*/
public function setValue($value, $notify = TRUE) {
return $this->decorated->setValue($value, $notify);
}
/**
* Forwards the call to the decorated entity.
*/
public function getString() {
return $this->decorated->getString();
}
/**
* Forwards the call to the decorated entity.
*/
public function getConstraints() {
return $this->decorated->getConstraints();
}
/**
* Forwards the call to the decorated entity.
*/
public function validate() {
return $this->decorated->validate();
}
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
/**
* Forwards the call to the decorated entity.
*/
public function getName() {
return $this->decorated->getName();
}
/**
* Forwards the call to the decorated entity.
*/
public function getRoot() {
return $this->decorated->getRoot();
}
/**
* Forwards the call to the decorated entity.
*/
public function getPropertyPath() {
return $this->decorated->getPropertyPath();
}
/**
* Forwards the call to the decorated entity.
*/
public function getParent() {
return $this->decorated->getParent();
}
/**
* Forwards the call to the decorated entity.
*/
catch
committed
public function setContext($name = NULL, TypedDataInterface $parent = NULL) {
$this->decorated->setContext($name, $parent);
}
/**
* Forwards the call to the decorated entity.
*/
public function getExportProperties() {
$this->decorated->getExportProperties();
}
Angie Byron
committed
catch
committed
/**
* Forwards the call to the decorated entity.
*/
public function onChange($property_name) {
$this->decorated->onChange($property_name);
}
Angie Byron
committed
/**
* Forwards the call to the decorated entity.
*/
public function isTranslatable() {
return $this->decorated->isTranslatable();
}