cms_content_sync-3.0.x-dev/src/Plugin/FieldHandlerBase.php

src/Plugin/FieldHandlerBase.php
<?php

namespace Drupal\cms_content_sync\Plugin;

use Drupal\cms_content_sync\Controller\LoggerProxy;
use Drupal\cms_content_sync\Helper\FieldHelper;
use Drupal\cms_content_sync\PullIntent;
use Drupal\cms_content_sync\PushIntent;
use Drupal\cms_content_sync\SyncIntent;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase;
use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
use EdgeBox\SyncCore\Interfaces\Configuration\IDefineEntityType;
use EdgeBox\SyncCore\Interfaces\Configuration\IDefineObject;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\TypedData\DataDefinitionInterface;
use EdgeBox\SyncCore\Interfaces\Configuration\IDefineObjectProperty;
use EdgeBox\SyncCore\Interfaces\Configuration\IDefineProperty;
use EdgeBox\SyncCore\V2\Raw\Model\RemoteEntityTypePropertyFormat;

use function PHPSTORM_META\map;

/**
 * Common base class for field handler plugins.
 *
 * @see \Drupal\cms_content_sync\Annotation\EntityHandler
 * @see \Drupal\cms_content_sync\Plugin\FieldHandlerInterface
 * @see plugin_api
 *
 * @ingroup third_party
 */
abstract class FieldHandlerBase extends PluginBase implements ContainerFactoryPluginInterface, FieldHandlerInterface {
  /**
   * A logger instance.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected $logger;

  /**
   * @var string
   */
  protected $entityTypeName;

  /**
   * @var string
   */
  protected $bundleName;

  /**
   * @var string
   */
  protected $fieldName;

  /**
   * @var \Drupal\Core\Field\FieldDefinitionInterface
   */
  protected $fieldDefinition;

  /**
   * @var array
   *            Additional settings as provided by
   *            {@see FieldHandlerInterface::getHandlerSettings}
   */
  protected $settings;

  /**
   * @var \Drupal\cms_content_sync\Entity\Flow
   */
  protected $flow;

  /**
   * FieldHelper service.
   *
   * @var \Drupal\cms_content_sync\Helper\FieldHelper
   */
  protected FieldHelper $fieldHelper;

  /**
   * Constructs a Drupal\rest\Plugin\ResourceBase object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   *                                 Must contain entity_type_name, bundle_name, field_name, field_definition,
   *                                 settings and sync (see above).
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Psr\Log\LoggerInterface $logger
   *   A logger instance.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerInterface $logger, FieldHelper $field_helper) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->logger = $logger;
    $this->entityTypeName = $configuration['entity_type_name'];
    $this->bundleName = $configuration['bundle_name'];
    $this->fieldName = $configuration['field_name'];
    $this->fieldDefinition = $configuration['field_definition'];
    $this->settings = $configuration['settings'];
    $this->flow = $configuration['sync'];
    $this->fieldHelper = $field_helper;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
    $configuration,
    $plugin_id,
    $plugin_definition,
    LoggerProxy::get(),
    $container->get('cms_content_sync.field_helper'),
    );
  }

  /**
   * @return string
   */
  public function getFieldName() {
    return $this->fieldName;
  }

  /**
   * {@inheritdoc}
   */
  public function getAllowedPushOptions() {
    return [
      PushIntent::PUSH_DISABLED,
      PushIntent::PUSH_AUTOMATICALLY,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getAllowedPullOptions() {
    return [
      PullIntent::PULL_DISABLED,
      PullIntent::PULL_AUTOMATICALLY,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getHandlerSettings($current_values, $type = 'both') {
    // Nothing special here.
    return [];
  }

  /**
   * {@inheritdoc}
   */
  public function validateHandlerSettings(array &$form, FormStateInterface $form_state, string $entity_type_name, string $bundle_name, string $field_name, $current_values) {
    // No settings means no validation.
  }

  /**
   * Get the target bundles for the reference field.
   *
   * @return array
   *   An array of target bundles.
   */
  protected function getFieldTargetBundles(): array {
    return $this->fieldHelper->getEntityReferenceFieldAllowedTargetBundles($this->fieldDefinition);
  }

  /**
   * {@inheritdoc}
   */
  public function pull(PullIntent $intent) {
    $action = $intent->getAction();
    $entity = $intent->getEntity();

    // Deletion doesn't require any action on field basis for static data.
    if (SyncIntent::ACTION_DELETE == $action) {
      return FALSE;
    }

    if ($intent->shouldMergeChanges() && !$this->forceMergeOverwrite()) {
      return FALSE;
    }

    if (PullIntent::PULL_AUTOMATICALLY != $this->settings['import']) {
      return FALSE;
    }

    // These fields can't be changed.
    if (!$entity->isNew()) {
      if ('default_langcode' === $this->fieldName) {
        return TRUE;
      }
    }

    $data = $intent->getProperty($this->fieldName);

    if (empty($data)) {
      if (!$this->fieldDefinition->isRequired()) {
        $entity->set($this->fieldName, NULL);
      }
    }
    else {
      $entity->set($this->fieldName, $data);
    }

    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function push(PushIntent $intent) {
    $action = $intent->getAction();
    $entity = $intent->getEntity();

    if (PushIntent::PUSH_AUTOMATICALLY != $this->settings['export']) {
      return FALSE;
    }

    // Deletion doesn't require any action on field basis for static data.
    if (SyncIntent::ACTION_DELETE == $action) {
      return FALSE;
    }

    $intent->setProperty($this->fieldName, $entity->get($this->fieldName)->getValue());

    return TRUE;
  }

  /**
   *
   */
  protected function applyPropertySettings(IDefineProperty $property) {
    $storage = $this->fieldDefinition->getFieldStorageDefinition();

    $cardinality = $storage->getCardinality();

    $constraints = $this->fieldDefinition->getConstraints();
    if (isset($constraints['Count']['min'])) {
      $property->setMinItems($constraints['Count']['min']);
    }
    if ($cardinality !== FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
      $property->setMaxItems($cardinality);
    }
    elseif (isset($constraints['Count']['max'])) {
      $property->setMaxItems($constraints['Count']['max']);
    }

    $property->setLocalized($this->fieldDefinition->isTranslatable());

    $property->setShared(!$this->fieldDefinition->getTargetBundle());

    $description = $this->fieldDefinition->getDescription();
    if ($description) {
      if (!is_string($description)) {
        $description = $description->__toString();
      }
      $property->setDescription($description);
    }
  }

  /**
   * {@inheritDoc}
   */
  public function definePropertyAtType(IDefineEntityType $type_definition) {
    $property = $type_definition->addObjectProperty($this->fieldName, $this->fieldDefinition->getLabel(), TRUE, $this->fieldDefinition->isRequired(), $this->fieldDefinition->getType());

    $storage = $this->fieldDefinition->getFieldStorageDefinition();

    $this->applyPropertySettings($property);

    $remove_properties = [
      // Remove unused properties.
      'language' => [
        'language' => 1,
      ],
      // Remove calculated values.
      'published_at' => [
        'published_at_or_now' => 1,
      ],
      'text_with_summary' => [
        'processed' => 1,
        'summary_processed' => 1,
      ],
      'text_long' => [
        'processed' => 1,
      ],
      // Remove local IDs.
      'entitygroupfield' => [
        'target_id' => 1,
      ],
      'path' => [
        'pid' => 1,
      ],
      'image' => [
        'target_id' => 1,
      ],
      'file' => [
        'target_id' => 1,
      ],
      'svg_image_field' => [
        'target_id' => 1,
      ],
      'viewfield' => [
        'target_id' => 1,
      ],
      'webform' => [
        'target_id' => 1,
      ],
    ];

    $property_names = $storage->getPropertyNames();
    // Could use getColumns on top / instead to supplement poor typing.
    $columns = $storage->getColumns();
    if (empty($property_names)) {
      foreach ($columns as $name => $column) {
        if (!empty($remove_properties[$this->fieldDefinition->getType()][$name])) {
          continue;
        }

        $this->definePropertyByDefinition($name, $property, new class($name, $column) implements DataDefinitionInterface {
          protected array $column;
          protected string $name;

          /**
           *
           */
          public function __construct(string $name, array $column) {
            $this->name = $name;
            $this->column = $column;
          }

          /**
           * @inheritDoc
           */
          public static function createFromDataType($data_type) {
            throw new \Exception("Not supported.");
          }

          /**
           * @inheritDoc
           */
          public function getDataType() {
            if (!empty($this->column['serialize'])) {
              return 'map';
            }
            return $this->column['type'];
          }

          /**
           * @inheritDoc
           */
          public function getLabel() {
            return $this->name;
          }

          /**
           * @inheritDoc
           */
          public function getDescription() {
            return NULL;
          }

          /**
           * @inheritDoc
           */
          public function isList() {
            return FALSE;
          }

          /**
           * @inheritDoc
           */
          public function isReadOnly() {
            return FALSE;
          }

          /**
           * @inheritDoc
           */
          public function isComputed() {
            return FALSE;
          }

          /**
           * @inheritDoc
           */
          public function isRequired() {
            return !empty($this->column['not null']);
          }

          /**
           * @inheritDoc
           */
          public function getClass() {
            return '\\NoClass';
          }

          /**
           * @inheritDoc
           */
          public function getSettings() {
            return [];
          }

          /**
           * @inheritDoc
           */
          public function getSetting($setting_name) {
            return NULL;
          }

          /**
           * @inheritDoc
           */
          public function getConstraints() {
            return [];
          }

          /**
           * @inheritDoc
           */
          public function getConstraint($constraint_name) {
            throw new \Exception("Not supported.");
          }

          /**
           * @inheritDoc
           */
          public function addConstraint($constraint_name, $options = NULL) {
            throw new \Exception("Not supported.");
          }

          /**
           * @inheritDoc
           */
          public function isInternal() {
            return FALSE;
          }

        }, $name === $storage->getMainPropertyName(), $column);
      }
    }
    else {
      foreach ($property_names as $name) {
        if (!empty($remove_properties[$this->fieldDefinition->getType()][$name])) {
          continue;
        }

        $definition = $storage->getPropertyDefinition($name);
        if ($definition->isComputed()) {
          continue;
        }

        $this->definePropertyByDefinition($name, $property, $definition, $name === $storage->getMainPropertyName(), $columns[$name] ?? []);
      }
    }

    return $property;
  }

  /**
   *
   */
  protected function definePropertyByDefinition(string $name, IDefineObjectProperty $property, DataDefinitionInterface $definition, $is_main_property = FALSE, array $column = []) {
    static $map = [
      'serial' => 'integer',
      'int' => 'integer',
      'integer' => 'integer',
      'float' => 'float',
      'numeric' => 'float',
      'decimal' => 'float',
      'varchar' => 'string',
      'string' => 'string',
      'language_reference' => 'string',
      'timestamp' => 'integer',
      'boolean' => 'boolean',
      'filter_format' => 'string',
      'char' => 'string',
      'text' => 'string',
      'blob' => 'string',
      'datetime' => 'string',
      'uri' => 'string',
      'entity_reference' => 'reference',
      'any' => 'string',
      'map' => 'object',
      'datetime_iso8601' => 'string',
      'email' => 'string',
      'metatag' => 'string',
      'layout_section' => 'string',
      'chart_config' => 'string',
    ];
    // For arrays: Tiny, Medium, Small, Big, Normal.
    static $stringLengths = [
      'varchar' => 0xFFFF,
      'string' => 0xFFFF,
    // 12
      'language_reference' => 0xC,
      'filter_format' => 0xFF,
      'char' => 0xFF,
      'text' => [
        0xFF,
        0xFF,
        0xFFFFFF,
        0xFFFFFFFF,
    // 16KB
        0x4000,
      ],
      // 32
      'datetime' => 0x20,
      'uri' => 0x800,
      // 32
      'datetime_iso8601' => 0x20,
      'email' => 0xFF,
      // 16KB
      'metatag' => 0x4000,
      'layout_section' => 0xFF,
      // Blob / unknown.
      'blob' => 0xFFFFFFFF,
      'any' => 0xFFFFFFFF,
    ];
    static $integerSizes = [
      1,
      2,
      3,
      8,
      4,
    ];
    static $floatSizes = [
      4,
      4,
      4,
      8,
      4,
    ];
    static $sizeIndices = [
      'tiny',
      'small',
      'medium',
      'big',
      'normal',
    ];

    $simple_type = $map[$definition->getDataType()] ?? NULL;

    if (empty($simple_type)) {
      throw new \Exception('Using unknown data definition type ' . $definition->getDataType() . ' at property ' . $this->fieldName);
    }

    $constraints = $definition->getConstraints();

    $allowed_values = NULL;
    if ($name === 'country_code' || $this->fieldDefinition->getType() === 'address_country') {
      $allowed_values = $this->fieldDefinition->getSetting('available_countries');
    }
    elseif ($is_main_property) {
      $allowed_values = $this->fieldDefinition->getSetting('allowed_values');
    }

    $any_property = NULL;

    $size = $definition->getSetting('size');
    if (!$size && !empty($column['size'])) {
      $size = $column['size'];
    }
    else {
      $size = 'normal';
    }
    $size_index = array_search($size, $sizeIndices);

    switch ($simple_type) {
      case 'boolean':
        $boolean_property = $property->addBooleanProperty($name, $definition->getLabel(), FALSE, $definition->isRequired(), $definition->getDataType());

        $any_property = $boolean_property;

        break;

      case 'integer':
        $integer_property = $property->addIntegerProperty($name, $definition->getLabel(), FALSE, $definition->isRequired(), $definition->getDataType());
        if (isset($constraints['Range']['min'])) {
          $integer_property->setMinValue($constraints['Range']['min']);
        }
        if (isset($constraints['Range']['max'])) {
          $integer_property->setMaxValue($constraints['Range']['max']);
        }

        if ($integerSizes[$size_index] > 0) {
          $integer_property->setByteSize($integerSizes[$size_index]);
        }

        $any_property = $integer_property;

        break;

      case 'float':
        $float_property = $property->addFloatProperty($name, $definition->getLabel(), FALSE, $definition->isRequired(), $definition->getDataType());
        if (isset($constraints['Range']['min'])) {
          $float_property->setMinValue($constraints['Range']['min']);
        }
        if (isset($constraints['Range']['max'])) {
          $float_property->setMaxValue($constraints['Range']['max']);
        }

        if ($floatSizes[$size_index] > 0) {
          $float_property->setByteSize($floatSizes[$size_index]);
        }

        $any_property = $float_property;

        break;

      case 'string':
        $string_property = $property->addStringProperty($name, $definition->getLabel(), FALSE, $definition->isRequired(), $definition->getDataType());

        if (isset($constraints['Length']['min'])) {
          $string_property->setMinLength($constraints['Length']['min']);
        }
        if (isset($constraints['Length']['max'])) {
          $string_property->setMaxLength($constraints['Length']['max']);
        }
        elseif (!empty($column['length'])) {
          $string_property->setMaxLength($column['length']);
        }
        elseif ($length_type = $stringLengths[$definition->getDataType()]) {
          $max_length = is_array($length_type) ? $length_type[$size_index] : $length_type;
          if ($max_length > 0) {
            $string_property->setMaxLength($max_length);
          }
        }

        if (!empty($constraints['Email']['mode'])) {
          $string_property->setFormat(RemoteEntityTypePropertyFormat::EMAIL_ADDRESS);
        }
        elseif (!empty($constraints['Uuid']['mode'])) {
          $string_property->setFormat(RemoteEntityTypePropertyFormat::UUID);
        }

        if (!empty($constraints['Regex']['pattern'])) {
          $string_property->setRegularExpressionFormat($constraints['Regex']['pattern']);
        }

        if ($definition->getDataType() === 'datetime_iso8601') {
          $string_property->setFormat(RemoteEntityTypePropertyFormat::DATE_ISO8601);
        }

        if (!empty($constraints['Country']['availableCountries'])) {
          $allowed_values = $constraints['Country']['availableCountries'];
        }

        $any_property = $string_property;

        break;

      case 'reference':
        $reference_property = $property->addReferenceProperty($name, $definition->getLabel(), FALSE, $definition->isRequired(), $definition->getDataType());

        $target_type = $definition->getSetting('target_type');
        if ($target_type) {
          $target_bundles = $this->getFieldTargetBundles();
          if (empty($target_bundles)) {
            $reference_property->addAllowedType($target_type);
          }
          else {
            foreach ($target_bundles as $target_bundle) {
              $reference_property->addAllowedType($target_type, $target_bundle);
            }
          }
        }
        else {
          if (in_array($this->fieldDefinition->getType(), ['image', 'file_uri', 'file', 'svg_image_field'])) {
            $reference_property->addAllowedType('file');
          }
          elseif ($name === 'entity') {
            if ($this->fieldDefinition->getType() === 'entitygroupfield') {
              $reference_property->addAllowedType('group');
            }
            elseif ($this->fieldDefinition->getType() === 'viewfield') {
              $reference_property->addAllowedType('view');
            }
            elseif ($this->fieldDefinition->getType() === 'webform') {
              $reference_property->addAllowedType('webform');
            }
          }
        }

        $any_property = $reference_property;

        break;

      case 'object':
        $object_property = $property->addObjectProperty($name, $definition->getLabel(), FALSE, $definition->isRequired(), $definition->getDataType());

        if ($definition instanceof ComplexDataDefinitionInterface) {
          foreach ($definition->getPropertyDefinitions() as $key => $property_definition) {
            $this->definePropertyByDefinition($key, $object_property, $property_definition);
          }

          if ($definition->getMainPropertyName()) {
            $object_property->setMainProperty($definition->getMainPropertyName());
          }
        }

        $any_property = $object_property;

        break;
    }

    if ($any_property && !empty($allowed_values)) {
      foreach ($allowed_values as $value => $name) {
        $any_property->addAllowedValue($name, $value);
      }
    }
  }

  /**
   * @return false
   */
  protected function forceMergeOverwrite() {
    return 'changed' == $this->fieldName;
  }

}

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

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