Entity.php 17.4 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * Contains \Drupal\Core\Entity\Entity.
6 7
 */

8
namespace Drupal\Core\Entity;
9

10
use Drupal\Component\Uuid\Uuid;
11
use Drupal\Core\Language\Language;
12
use Drupal\Core\TypedData\TranslatableInterface;
13
use Drupal\Core\TypedData\TypedDataInterface;
14
use IteratorAggregate;
15
use Drupal\Core\Session\AccountInterface;
16

17 18 19 20 21 22 23 24
/**
 * Defines a base entity class.
 *
 * Default implementation of EntityInterface.
 *
 * This class can be used as-is by simple entity types. Entity types requiring
 * special handling can extend the class.
 */
25
class Entity implements IteratorAggregate, EntityInterface {
26 27 28 29 30 31

  /**
   * The language code of the entity's default language.
   *
   * @var string
   */
32
  public $langcode = Language::LANGCODE_NOT_SPECIFIED;
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

  /**
   * The entity type.
   *
   * @var string
   */
  protected $entityType;

  /**
   * Boolean indicating whether the entity should be forced to be new.
   *
   * @var bool
   */
  protected $enforceIsNew;

48 49 50 51 52 53 54
  /**
   * Boolean indicating whether a new revision should be created on save.
   *
   * @var bool
   */
  protected $newRevision = FALSE;

55
  /**
56
   * Indicates whether this is the default revision.
57 58 59
   *
   * @var bool
   */
60
  protected $isDefaultRevision = TRUE;
61 62

  /**
63 64 65 66 67 68 69
   * Constructs an Entity object.
   *
   * @param array $values
   *   An array of values to set, keyed by property name. If the entity type
   *   has bundles, the bundle key has to be specified.
   * @param string $entity_type
   *   The type of the entity to create.
70
   */
71
  public function __construct(array $values, $entity_type) {
72 73 74 75 76 77 78 79
    $this->entityType = $entity_type;
    // Set initial values.
    foreach ($values as $key => $value) {
      $this->$key = $value;
    }
  }

  /**
80
   * Implements \Drupal\Core\Entity\EntityInterface::id().
81 82 83 84 85 86
   */
  public function id() {
    return isset($this->id) ? $this->id : NULL;
  }

  /**
87
   * Implements \Drupal\Core\Entity\EntityInterface::uuid().
88 89 90 91 92 93
   */
  public function uuid() {
    return isset($this->uuid) ? $this->uuid : NULL;
  }

  /**
94
   * Implements \Drupal\Core\Entity\EntityInterface::isNew().
95 96
   */
  public function isNew() {
97
    return !empty($this->enforceIsNew) || !$this->id();
98 99
  }

100
  /**
101
   * Implements \Drupal\Core\Entity\EntityInterface::isNewRevision().
102 103 104
   */
  public function isNewRevision() {
    $info = $this->entityInfo();
105
    return $this->newRevision || (!empty($info['entity_keys']['revision']) && !$this->getRevisionId());
106 107
  }

108
  /**
109
   * Implements \Drupal\Core\Entity\EntityInterface::enforceIsNew().
110 111 112 113 114
   */
  public function enforceIsNew($value = TRUE) {
    $this->enforceIsNew = $value;
  }

115
  /**
116
   * Implements \Drupal\Core\Entity\EntityInterface::setNewRevision().
117 118 119 120 121
   */
  public function setNewRevision($value = TRUE) {
    $this->newRevision = $value;
  }

122
  /**
123
   * Implements \Drupal\Core\Entity\EntityInterface::entityType().
124 125 126 127 128 129
   */
  public function entityType() {
    return $this->entityType;
  }

  /**
130
   * Implements \Drupal\Core\Entity\EntityInterface::bundle().
131 132 133 134 135 136
   */
  public function bundle() {
    return $this->entityType;
  }

  /**
137
   * Implements \Drupal\Core\Entity\EntityInterface::label().
138 139 140 141
   */
  public function label($langcode = NULL) {
    $label = NULL;
    $entity_info = $this->entityInfo();
142 143
    if (isset($entity_info['label_callback']) && function_exists($entity_info['label_callback'])) {
      $label = $entity_info['label_callback']($this->entityType, $this, $langcode);
144
    }
145 146
    elseif (!empty($entity_info['entity_keys']['label']) && isset($this->{$entity_info['entity_keys']['label']})) {
      $label = $this->{$entity_info['entity_keys']['label']};
147 148 149 150 151
    }
    return $label;
  }

  /**
152
   * Implements \Drupal\Core\Entity\EntityInterface::uri().
153 154 155 156 157 158
   */
  public function uri() {
    $bundle = $this->bundle();
    // A bundle-specific callback takes precedence over the generic one for the
    // entity type.
    $entity_info = $this->entityInfo();
159 160 161
    $bundles = entity_get_bundles($this->entityType);
    if (isset($bundles[$bundle]['uri_callback'])) {
      $uri_callback = $bundles[$bundle]['uri_callback'];
162
    }
163 164
    elseif (isset($entity_info['uri_callback'])) {
      $uri_callback = $entity_info['uri_callback'];
165 166
    }

167 168
    // Invoke the callback to get the URI. If there is no callback, use the
    // default URI format.
169 170 171
    if (isset($uri_callback) && function_exists($uri_callback)) {
      $uri = $uri_callback($this);
    }
172 173 174 175 176 177 178 179 180 181
    else {
      $uri = array(
        'path' => 'entity/' . $this->entityType . '/' . $this->id(),
      );
    }
    // Pass the entity data to url() so that alter functions do not need to
    // look up this entity again.
    $uri['options']['entity_type'] = $this->entityType;
    $uri['options']['entity'] = $this;
    return $uri;
182 183
  }

184 185 186 187 188 189 190 191 192 193 194 195 196
  /**
   * {@inheritdoc}
   *
   * Returns a list of URI relationships supported by this entity.
   *
   * @return array
   *   An array of link relationships supported by this entity.
   */
  public function uriRelationships() {
    $entity_info = $this->entityInfo();
    return isset($entity_info['links']) ? array_keys($entity_info['links']) : array();
  }

197
  /**
198
   * Implements \Drupal\Core\Entity\EntityInterface::get().
199 200 201 202 203 204 205 206
   */
  public function get($property_name, $langcode = NULL) {
    // @todo: Replace by EntityNG implementation once all entity types have been
    // converted to use the entity field API.
    return isset($this->{$property_name}) ? $this->{$property_name} : NULL;
  }

  /**
207
   * Implements \Drupal\Core\TypedData\ComplexDataInterface::set().
208
   */
209
  public function set($property_name, $value, $notify = TRUE) {
210 211 212 213 214 215
    // @todo: Replace by EntityNG implementation once all entity types have been
    // converted to use the entity field API.
    $this->{$property_name} = $value;
  }

  /**
216
   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getProperties().
217 218 219 220 221 222 223
   */
  public function getProperties($include_computed = FALSE) {
    // @todo: Replace by EntityNG implementation once all entity types have been
    // converted to use the entity field API.
  }

  /**
224
   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyValues().
225 226 227 228 229 230 231
   */
  public function getPropertyValues() {
    // @todo: Replace by EntityNG implementation once all entity types have been
    // converted to use the entity field API.
  }

  /**
232
   * Implements \Drupal\Core\TypedData\ComplexDataInterface::setPropertyValues().
233 234 235 236 237 238 239
   */
  public function setPropertyValues($values) {
    // @todo: Replace by EntityNG implementation once all entity types have been
    // converted to use the entity field API.
  }

  /**
240
   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinition().
241 242 243 244 245 246 247
   */
  public function getPropertyDefinition($name) {
    // @todo: Replace by EntityNG implementation once all entity types have been
    // converted to use the entity field API.
  }

  /**
248
   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
249 250 251 252 253 254 255
   */
  public function getPropertyDefinitions() {
    // @todo: Replace by EntityNG implementation once all entity types have been
    // converted to use the entity field API.
  }

  /**
256
   * Implements \Drupal\Core\TypedData\ComplexDataInterface::isEmpty().
257 258 259 260 261 262 263
   */
  public function isEmpty() {
    // @todo: Replace by EntityNG implementation once all entity types have been
    // converted to use the entity field API.
  }

  /**
264
   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getIterator().
265 266 267 268
   */
  public function getIterator() {
    // @todo: Replace by EntityNG implementation once all entity types have been
    // converted to use the entity field API.
269
    return new \ArrayIterator(array());
270 271 272
  }

  /**
273
   * Implements \Drupal\Core\TypedData\AccessibleInterface::access().
274
   */
275
  public function access($operation = 'view', AccountInterface $account = NULL) {
276 277 278 279 280
    if ($operation == 'create') {
      return \Drupal::entityManager()
        ->getAccessController($this->entityType)
        ->createAccess($this->bundle(), $account);
    }
281
    return \Drupal::entityManager()
282
      ->getAccessController($this->entityType)
283
      ->access($this, $operation, Language::LANGCODE_DEFAULT, $account);
284 285 286
  }

  /**
287
   * Implements \Drupal\Core\TypedData\TranslatableInterface::language().
288 289
   */
  public function language() {
290 291
    // @todo: Replace by EntityNG implementation once all entity types have been
    // converted to use the entity field API.
292 293 294
    $language = language_load($this->langcode);
    if (!$language) {
      // Make sure we return a proper language object.
295
      $language = new Language(array('id' => Language::LANGCODE_NOT_SPECIFIED));
296 297
    }
    return $language;
298 299 300
  }

  /**
301
   * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslation().
302 303
   *
   * @return \Drupal\Core\Entity\EntityInterface
304
   */
305
  public function getTranslation($langcode) {
306 307
    // @todo: Replace by EntityNG implementation once all entity types have been
    // converted to use the entity field API.
308
    return $this;
309 310 311 312 313
  }

  /**
   * Returns the languages the entity is translated to.
   *
314 315
   * @todo: Remove once all entity types implement the entity field API.
   *   This is deprecated by
316
   *   \Drupal\Core\TypedData\TranslatableInterface::getTranslationLanguages().
317 318
   */
  public function translations() {
319 320 321 322
    return $this->getTranslationLanguages(FALSE);
  }

  /**
323
   * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslationLanguages().
324 325 326 327 328
   */
  public function getTranslationLanguages($include_default = TRUE) {
    // @todo: Replace by EntityNG implementation once all entity types have been
    // converted to use the entity field API.
    $default_language = $this->language();
329
    $languages = array($default_language->id => $default_language);
330
    $entity_info = $this->entityInfo();
331 332

    if ($entity_info['fieldable']) {
333 334 335
      // Go through translatable properties and determine all languages for
      // which translated values are available.
      foreach (field_info_instances($this->entityType, $this->bundle()) as $field_name => $instance) {
336
        if (field_is_translatable($this->entityType, $instance->getField()) && isset($this->$field_name)) {
337
          foreach (array_filter($this->$field_name) as $langcode => $value)  {
338 339 340 341
            $languages[$langcode] = TRUE;
          }
        }
      }
342
      $languages = array_intersect_key(language_list(Language::STATE_ALL), $languages);
343 344 345
    }

    if (empty($include_default)) {
346
      unset($languages[$default_language->id]);
347 348
    }

349
    return $languages;
350 351 352
  }

  /**
353
   * Implements \Drupal\Core\Entity\EntityInterface::save().
354 355
   */
  public function save() {
356
    return \Drupal::entityManager()->getStorageController($this->entityType)->save($this);
357 358 359
  }

  /**
360
   * Implements \Drupal\Core\Entity\EntityInterface::delete().
361 362 363
   */
  public function delete() {
    if (!$this->isNew()) {
364
      \Drupal::entityManager()->getStorageController($this->entityType)->delete(array($this->id() => $this));
365 366 367 368
    }
  }

  /**
369
   * Implements \Drupal\Core\Entity\EntityInterface::createDuplicate().
370 371 372 373
   */
  public function createDuplicate() {
    $duplicate = clone $this;
    $entity_info = $this->entityInfo();
374
    $duplicate->{$entity_info['entity_keys']['id']} = NULL;
375 376

    // Check if the entity type supports UUIDs and generate a new one if so.
377
    if (!empty($entity_info['entity_keys']['uuid'])) {
378
      $uuid = new Uuid();
379
      $duplicate->{$entity_info['entity_keys']['uuid']} = $uuid->generate();
380 381 382 383 384
    }
    return $duplicate;
  }

  /**
385
   * Implements \Drupal\Core\Entity\EntityInterface::entityInfo().
386 387
   */
  public function entityInfo() {
388
    return \Drupal::entityManager()->getDefinition($this->entityType());
389 390 391
  }

  /**
392
   * Implements \Drupal\Core\Entity\EntityInterface::getRevisionId().
393 394 395 396 397 398
   */
  public function getRevisionId() {
    return NULL;
  }

  /**
399
   * Implements \Drupal\Core\Entity\EntityInterface::isDefaultRevision().
400
   */
401 402
  public function isDefaultRevision($new_value = NULL) {
    $return = $this->isDefaultRevision;
403
    if (isset($new_value)) {
404
      $this->isDefaultRevision = (bool) $new_value;
405 406
    }
    return $return;
407
  }
408 409

  /**
410
   * Implements \Drupal\Core\Entity\EntityInterface::getExportProperties().
411 412 413 414 415
   */
  public function getExportProperties() {
    return array();
  }

416
  /**
417
   * Implements \Drupal\Core\Entity\EntityInterface::getBCEntity().
418 419 420 421 422 423
   */
  public function getBCEntity() {
    return $this;
  }

  /**
424
   * Implements \Drupal\Core\Entity\EntityInterface::getNGEntity().
425
   */
426
  public function getNGEntity() {
427 428 429 430
    return $this;
  }

  /**
431
   * {@inheritdoc}
432
   */
433
  public function getDefinition() {
434 435 436
    // @todo: This does not make much sense, so remove once TypedDataInterface
    // is removed. See https://drupal.org/node/2002138.
    if ($this->bundle() != $this->entityType()) {
437
      $type = 'entity:' . $this->entityType() . ':' . $this->bundle();
438
    }
439 440 441 442
    else {
      $type = 'entity:' . $this->entityType();
    }
    return array('type' => $type);
443 444 445
  }

  /**
446
   * {@inheritdoc}
447 448
   */
  public function getValue() {
449 450 451
    // @todo: This does not make much sense, so remove once TypedDataInterface
    // is removed. See https://drupal.org/node/2002138.
    return $this->getPropertyValues();
452 453 454 455 456 457
  }

  /**
   * Implements \Drupal\Core\TypedData\TypedDataInterface::setValue().
   */
  public function setValue($value, $notify = TRUE) {
458 459 460
    // @todo: This does not make much sense, so remove once TypedDataInterface
    // is removed. See https://drupal.org/node/2002138.
    $this->setPropertyValues($value);
461 462 463
  }

  /**
464
   * {@inheritdoc}
465 466
   */
  public function getString() {
467
    return $this->label();
468 469 470
  }

  /**
471
   * {@inheritdoc}
472 473 474 475 476 477
   */
  public function getConstraints() {
    return array();
  }

  /**
478
   * {@inheritdoc}
479 480
   */
  public function validate() {
481 482
    // @todo: Add the typed data manager as proper dependency.
    return \Drupal::typedData()->getValidator()->validate($this);
483 484
  }

485 486 487 488 489 490 491 492 493
  /**
   * {@inheritdoc}
   */
  public function applyDefaultValue($notify = TRUE) {
    foreach ($this->getProperties() as $property) {
      $property->applyDefaultValue(FALSE);
    }
  }

494 495 496 497 498 499 500 501 502
  /**
   * Implements \Drupal\Core\TypedData\ComplexDataInterface::onChange().
   */
  public function onChange($property_name) {
    // Nothing to do.
  }

  /**
   * Implements \Drupal\Core\TypedData\TypedDataInterface::getName().
503 504 505 506 507 508
   */
  public function getName() {
    return NULL;
  }

  /**
509
   * Implements \Drupal\Core\TypedData\TypedDataInterface::getRoot().
510 511 512 513 514 515
   */
  public function getRoot() {
    return $this;
  }

  /**
516
   * Implements \Drupal\Core\TypedData\TypedDataInterface::getPropertyPath().
517 518 519 520 521 522
   */
  public function getPropertyPath() {
    return '';
  }

  /**
523
   * Implements \Drupal\Core\TypedData\TypedDataInterface::getParent().
524 525 526 527 528 529
   */
  public function getParent() {
    return NULL;
  }

  /**
530
   * Implements \Drupal\Core\TypedData\TypedDataInterface::setContext().
531
   */
532
  public function setContext($name = NULL, TypedDataInterface $parent = NULL) {
533 534
    // As entities are always the root of the tree of typed data, we do not need
    // to set any parent or name.
535
  }
536 537 538 539 540 541 542 543 544 545

  /**
   * Implements \Drupal\Core\Entity\EntityInterface::isTranslatable().
   */
  public function isTranslatable() {
    // @todo Inject the entity manager and retrieve bundle info from it.
    $bundles = entity_get_bundles($this->entityType);
    return !empty($bundles[$this->bundle()]['translatable']);
  }

546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
  /**
   * {@inheritdoc}
   */
  public function preSave(EntityStorageControllerInterface $storage_controller) {
  }

  /**
   * {@inheritdoc}
   */
  public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
  }

  /**
   * {@inheritdoc}
   */
  public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
  }

  /**
   * {@inheritdoc}
   */
  public function postCreate(EntityStorageControllerInterface $storage_controller) {
  }

  /**
   * {@inheritdoc}
   */
  public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
  }

  /**
   * {@inheritdoc}
   */
  public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
  }

  /**
   * {@inheritdoc}
   */
  public static function postLoad(EntityStorageControllerInterface $storage_controller, array $entities) {
  }

  /**
   * {@inheritdoc}
   */
  public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record) {
  }

594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
  /**
   * {@inheritdoc}
   */
  public function getUntranslated() {
    return $this->getTranslation(Language::LANGCODE_DEFAULT);
  }

  /**
   * {@inheritdoc}
   */
  public function hasTranslation($langcode) {
    $translations = $this->getTranslationLanguages();
    return isset($translations[$langcode]);
  }

  /**
   * {@inheritdoc}
   */
  public function addTranslation($langcode, array $values = array()) {
    // @todo Config entities do not support entity translation hence we need to
    //   move the TranslatableInterface implementation to EntityNG. See
    //   http://drupal.org/node/2004244
  }

  /**
   * {@inheritdoc}
   */
  public function removeTranslation($langcode) {
    // @todo Config entities do not support entity translation hence we need to
    //   move the TranslatableInterface implementation to EntityNG. See
    //   http://drupal.org/node/2004244
  }

  /**
   * {@inheritdoc}
   */
  public function initTranslation($langcode) {
    // @todo Config entities do not support entity translation hence we need to
    //   move the TranslatableInterface implementation to EntityNG. See
    //   http://drupal.org/node/2004244
  }

636 637 638 639 640 641 642
  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions($entity_type) {
    return array();
  }

643
}