entity_reference_inline-8.x-1.x-dev/src/Plugin/Field/FieldType/EntityReferenceInlineItem.php

src/Plugin/Field/FieldType/EntityReferenceInlineItem.php
<?php

/**
 * @file
 * Contains \Drupal\entity_reference_inline\Plugin\Field\FieldType\EntityReferenceInlineItem.
 */

namespace Drupal\entity_reference_inline\Plugin\Field\FieldType;

use Drupal\Core\Entity\TypedData\EntityDataDefinition;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TypedData\DataReferenceDefinition;
use Drupal\Core\TypedData\TranslatableInterface;

/**
 * Defines the 'entity_reference_inline' entity field type.
 *
 * Supported settings (below the definition's 'settings' key) are:
 * - target_type: The entity type to reference. Required.
 *
 * @FieldType(
 *   id = "entity_reference_inline",
 *   label = @Translation("Entity reference inline"),
 *   description = @Translation("An entity field containing an entity reference for inline editing."),
 *   category = @Translation("Reference inline"),
 *   default_widget = "entity_reference_inline",
 *   default_formatter = "entity_reference_label",
 *   list_class = "\Drupal\entity_reference_inline\Plugin\Field\FieldType\EntityReferenceInlineFieldItemList",
 * )
*/
class EntityReferenceInlineItem extends EntityReferenceItem implements EntityReferenceInlineItemInterface {

  use FieldItemCommonMethodsTrait;

  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties = parent::propertyDefinitions($field_definition);

    $settings = $field_definition->getSettings();
    $target_type_info = \Drupal::entityManager()->getDefinition($settings['target_type']);

    $properties['entity'] = DataReferenceDefinition::create('entity_inline')
      ->setLabel($target_type_info->getLabel())
      ->setDescription(new TranslatableMarkup('The referenced entity'))
      // The entity object is computed out of the entity ID.
      ->setComputed(TRUE)
      ->setReadOnly(FALSE)
      ->setTargetDefinition(EntityDataDefinition::create($settings['target_type']))
      // We can add a constraint for the target entity type. The list of
      // referenceable bundles is a field setting, so the corresponding
      // constraint is added dynamically in ::getConstraints().
      ->addConstraint('EntityType', $settings['target_type']);

    return $properties;
  }

  /**
   * {@inheritdoc}
   */
  public function getValue($include_computed = FALSE) {
    $values = FieldItemBase::getValue();
    if ($include_computed || $this->hasNewEntity()) {
      $values['entity'] = $this->entity;
    }
    return $values;
  }

  /**
   * {@inheritdoc}
   */
  public function preSave() {
    if ($this->skipPreSave) {
      return;
    }

    /** @var \Drupal\Core\Entity\EntityInterface $referenced_entity */
    $referenced_entity = $this->entity;

    $needs_save = FALSE;
    if ($this->referencedEntityNeedsSave()) {
      if ($referenced_entity instanceof TranslatableInterface) {
        $referenced_entity = $this->getFieldDefinition()->isTranslatable() && $referenced_entity->hasTranslation($this->getLangcode()) ? $referenced_entity->getTranslation($this->getLangcode()) : $referenced_entity;
      }

      $needs_save = TRUE;
    }
    // If we reference an entity, which has different ID or revision ID, then we
    // assume that this entity has been already saved and therefore we only need
    // to update the referenced metadata.
    // @todo in order to cover all the possible cases we need additionally an
    // entity method checking if the entity has unsaved changes.
    elseif (!$this->itemNeedsPropertiesUpdate()) {
      if (isset($referenced_entity->original) && ($unchanged = $referenced_entity->original) && isset($unchanged->loadedUnchanged)) {
        $unchanged = $referenced_entity->original;
      }
      else {
        $storage = \Drupal::entityTypeManager()->getStorage($referenced_entity->getEntityTypeId());
        // Get the entity id from the entity itself instead from the target_id
        // property, as if the entity set on the field item was new target_id
        // has been set to NULL, but meanwhile the entity might have been saved
        // elsewhere, but the target_id property will still not be set.
        /** @var \Drupal\Core\Entity\ContentEntityInterface $unchanged */
        $unchanged = $storage->loadUnchanged($referenced_entity->id());

        // Flag the entity object we are comparing that the entity has been
        // loaded through loadUnchanged in order for the referenced entities hold
        // in an inline entity field type to be loaded unchanged as well.
        // @see \Drupal\entity_reference_inline\Plugin\DataType\EntityReferenceInline::getTarget()
        $unchanged->loadedUnchanged = TRUE;

        // As we've already loaded the unchanged entity we attach it to the
        // current referenced entity for performance reasons, because otherwise
        // it will be loaded again in EntityStorageBase::doPreSave or in
        // ContentEntityBase::hasTranslationChanges.
        $referenced_entity->original = $unchanged;
      }

      // We save the entity only in case it has changed in order to speed up
      // the save process of big structures.
      //
      // A fast check for addition or removal of an entity translation.
      $current_translation_langcodes = array_keys($referenced_entity->getTranslationLanguages());
      $previous_translation_langcodes = array_keys($unchanged->getTranslationLanguages());
      sort($current_translation_langcodes);
      sort($previous_translation_langcodes);
      if ($current_translation_langcodes != $previous_translation_langcodes) {
        if ($this->getFieldDefinition()->isTranslatable() && $referenced_entity->hasTranslation($this->getLangcode())) {
          $referenced_entity = $referenced_entity->getTranslation($this->getLangcode());
        }
        $referenced_entity->inlineEditedHasTranslationChanges = TRUE;
        $needs_save = TRUE;
      }
      else {
        // If the entity has been edited through our inline widget then it
        // might have been flagged with the translation language in which it
        // has been edited, so we have to check only this translation for
        // changes.
        // @see \Drupal\entity_reference_inline\Plugin\Field\FieldWidget\EntityReferenceInlineWidget::buildEntity()
        if (isset($referenced_entity->inlineEditedLangcode) && ($inline_edited_langcode = $referenced_entity->inlineEditedLangcode) && $referenced_entity->hasTranslation($inline_edited_langcode)) {
          $referenced_entity = $referenced_entity->getTranslation($inline_edited_langcode);
          if ($referenced_entity->hasTranslationChanges()) {
            $referenced_entity->inlineEditedHasTranslationChanges = TRUE;
            $needs_save = TRUE;
          }
        }
        else {
          foreach ($current_translation_langcodes as $langcode) {
            /** @var \Drupal\Core\Entity\ContentEntityInterface $translation */
            $translation = $referenced_entity->getTranslation($langcode);
            if ($translation->hasTranslationChanges()) {
              if ($this->getFieldDefinition()->isTranslatable() && $referenced_entity->hasTranslation($this->getLangcode())) {
                $referenced_entity = $referenced_entity->getTranslation($this->getLangcode());
              }
              elseif ($translation->isNewTranslation()) {
                $referenced_entity = $translation;
              }
              $referenced_entity->inlineEditedHasTranslationChanges = TRUE;
              $needs_save = TRUE;
              break;
            }
          }
        }
      }
    }

    // In case we haven't found any changes we still have to check the
    // underlying structure for inconsistencies which require save of the
    // current entity.
    if ($needs_save || $this->needsSave(FALSE)) {
      // Flag the entity as being in a saving process. If there are inline
      // references on the entity as well then when calling
      // hasTranslationChanges on their item list we will append the original
      // entity and in order to not unset it and so reset the cache of the
      // Entity::hasTranslationChanges and also to not load later again
      // additionally the original again we will check if the parent is in a
      // saving process and if so do not unset the original entity, otherwise
      // we do unset it.
      // @see EntityReferenceRevisionsInlineFieldItemList::hasTranslationChanges
      $referenced_entity->inlineSaving = TRUE;

      $referenced_entity->save();
    }

    $this->entity = $referenced_entity;
  }

  /**
   * Checks whether the item needs property updates.
   *
   * This method checks if the property holding the entity ID matches the entity
   * ID on the entity object i.e. if the property is up to date. This might
   * happen if somehow the ID of the entity object is changed.
   *
   * @return bool
   */
  protected function itemNeedsPropertiesUpdate() {
    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
    $entity = $this->entity;
    if (!$entity) {
      return isset($this->target_id);
    }

    return $this->target_id != $entity->id();
  }

  /**
   * Checks whether the referenced entity needs save.
   *
   * @return bool
   */
  protected function referencedEntityNeedsSave() {
    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
    $entity = $this->entity;
    if (!$entity) {
      return FALSE;
    }

    $needs_save = $entity->isNew() || $entity->isNewTranslation() || !empty($entity->needsSave);
    return $needs_save;
  }

  /**
   * {@inheritdoc}
   */
  public function needsSave($include_current_item = TRUE) {
    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
    $entity = $this->entity;
    if (!$entity) {
      return FALSE;
    }

    $needs_save = $include_current_item ? ($this->needsSave || $this->referencedEntityNeedsSave() || $this->itemNeedsPropertiesUpdate()) : FALSE;
    if (!$needs_save) {
      foreach (array_keys($entity->getTranslationLanguages()) as $langcode) {
        $translation = $entity->getTranslation($langcode);
        if ($translation->isNewTranslation()) {
          // For performance reasons we set the flag on the item, so that in
          // case of a big structure not to iterate down the whole structure
          // till the bottom each time we go one level deeper.
          $this->needsSave = TRUE;
          return TRUE;
        }

        // We have to go down the structure and check if an entity down the
        // structure needs a save.
        $fields = $translation->isDefaultTranslation() ? $translation->getFields() : $translation->getTranslatableFields();
        foreach ($fields as $items) {
          if ($items instanceof EntityReferenceInlineFieldItemListInterface) {
            if ($items->needsSave()) {
              // For performance reasons we set the flag on the item, so that in
              // case of a big structure not to iterate down the whole structure
              // till the bottom each time we go one level deeper.
              $this->needsSave = TRUE;
              return TRUE;
            }
          }
        }
      }
    }
    elseif (!$this->needsSave) {
      // For performance reasons we set the flag on the item, so that in case of
      // a big structure not to iterate down the whole structure till the bottom
      // each time we go one level deeper.
      $this->needsSave = TRUE;
    }

    return $needs_save;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc