salesforce-8.x-4.x-dev/modules/salesforce_mapping/src/Entity/SalesforceMapping.php

modules/salesforce_mapping/src/Entity/SalesforceMapping.php
<?php

namespace Drupal\salesforce_mapping\Entity;

use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Plugin\DefaultLazyPluginCollection;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\salesforce\Exception;
use Drupal\salesforce\SelectQuery;
use Drupal\salesforce_mapping\MappingConstants;

/**
 * Defines a Salesforce Mapping configuration entity class.
 *
 * @ConfigEntityType(
 *   id = "salesforce_mapping",
 *   label = @Translation("Salesforce Mapping"),
 *   module = "salesforce_mapping",
 *   handlers = {
 *     "storage" = "Drupal\salesforce_mapping\SalesforceMappingStorage",
 *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
 *     "access" = "Drupal\salesforce_mapping\SalesforceMappingAccessController",
 *   },
 *   admin_permission = "administer salesforce mapping",
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "label",
 *     "weight" = "weight",
 *   },
 *   config_export = {
 *    "id",
 *    "label",
 *    "weight",
 *    "type",
 *    "key",
 *    "async",
 *    "push_standalone",
 *    "pull_standalone",
 *    "pull_trigger_date",
 *    "pull_where_clause",
 *    "pull_record_type_filter",
 *    "sync_triggers",
 *    "salesforce_object_type",
 *    "drupal_entity_type",
 *    "drupal_bundle",
 *    "field_mappings",
 *    "push_limit",
 *    "push_retries",
 *    "push_frequency",
 *    "pull_frequency",
 *    "always_upsert"
 *   },
 *   lookup_keys = {
 *     "drupal_entity_type",
 *     "drupal_bundle",
 *     "salesforce_object_type"
 *   }
 * )
 */
class SalesforceMapping extends ConfigEntityBase implements SalesforceMappingInterface {

  use StringTranslationTrait;

  /**
   * Only one bundle type for now.
   *
   * @var string
   */
  protected $type = 'salesforce_mapping';

  /**
   * ID (machine name) of the Mapping.
   *
   * @var string
   *
   * @note numeric id was removed
   */
  protected $id;

  /**
   * Label of the Mapping.
   *
   * @var string
   */
  protected $label;

  /**
   * The UUID for this entity.
   *
   * @var string
   */
  protected $uuid;

  /**
   * A default weight for the mapping.
   *
   * @var int
   */
  protected $weight = 0;

  /**
   * Whether to push asynchronous.
   *
   *   - If true, disable real-time push.
   *   - If false (default), attempt real-time push and enqueue failures for
   *     async push.
   *
   * Note this is different behavior compared to D7.
   *
   * @var bool
   */
  protected $async = FALSE;

  /**
   * Whether a standalone push endpoint is enabled for this mapping.
   *
   * @var bool
   */
  protected $push_standalone = FALSE;

  /**
   * Whether a standalone push endpoint is enabled for this mapping.
   *
   * @var bool
   */
  protected $pull_standalone = FALSE;

  /**
   * The Salesforce field to use for determining whether or not to pull.
   *
   * @var string
   */
  protected $pull_trigger_date = 'LastModifiedDate';

  /**
   * Additional "where" logic to append to pull-polling query.
   *
   * @var string
   */
  protected $pull_where_clause = '';

  /**
   * Pull query record type filter.
   *
   * @var array
   */
  protected $pull_record_type_filter = [];

  /**
   * The drupal entity type to which this mapping points.
   *
   * @var string
   */
  protected $drupal_entity_type;

  /**
   * The drupal entity bundle to which this mapping points.
   *
   * @var string
   */
  protected $drupal_bundle;

  /**
   * The salesforce object type to which this mapping points.
   *
   * @var string
   */
  protected $salesforce_object_type;

  /**
   * Salesforce field name for upsert key, if set. Otherwise FALSE.
   *
   * @var string
   */
  protected $key;

  /**
   * If TRUE, always use "upsert" to push data to Salesforce.
   *
   * Otherwise use "upsert" only if upsert key is set and SFID is not available.
   *
   * @var bool
   */
  protected $always_upsert;

  /**
   * Mapped field plugins.
   *
   * @var \Drupal\salesforce_mapping\SalesforceMappingFieldPluginInterface[]
   */
  protected $field_mappings = [];

  /**
   * Active sync triggers.
   *
   * @var array
   */
  protected $sync_triggers = [];

  /**
   * Stateful push data for this mapping.
   *
   * @var array
   */
  protected $push_info;

  /**
   * Statefull pull data for this mapping.
   *
   * @var array
   */
  protected $pull_info;

  /**
   * How often (in seconds) to push with this mapping.
   *
   * @var int
   */
  protected $push_frequency = 0;

  /**
   * Maximum number of records to push during a batch.
   *
   * @var int
   */
  protected $push_limit = 0;

  /**
   * Maximum number of attempts to push a record before it's considered failed.
   *
   * @var string
   */
  protected $push_retries = 3;

  /**
   * How often (in seconds) to pull with this mapping.
   *
   * @var int
   */
  protected $pull_frequency = 0;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $values, $entity_type) {
    parent::__construct($values, $entity_type);
    $push_info = $this->state()->get('salesforce.mapping_push_info', []);
    if (empty($push_info[$this->id()])) {
      $push_info[$this->id()] = [
        'last_timestamp' => 0,
      ];
    }
    $this->push_info = $push_info[$this->id()];

    $pull_info = $this->state()->get('salesforce.mapping_pull_info', []);
    if (empty($pull_info[$this->id()])) {
      $pull_info[$this->id()] = [
        'last_pull_timestamp' => 0,
        'last_delete_timestamp' => 0,
      ];
    }
    $this->pull_info = $pull_info[$this->id()];
    foreach ($this->field_mappings as $i => &$field_mapping) {
      $field_mapping['id'] = $i;
      $field_mapping['mapping'] = $this;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function __get($key) {
    return $this->$key;
  }

  /**
   * {@inheritdoc}
   */
  public function getPluginCollections() {
    if (empty($this->field_mappings)) {
      return [];
    }
    return [
      'field_mappings' => new DefaultLazyPluginCollection($this->fieldManager(), $this->field_mappings),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function toArray() {
    // Schema API complains during save() if field_mappings' mapping property
    // exists as a reference to the parent mapping. It's redundant anyway, so
    // we can delete it safely.
    // @todo there's probably a way to do this with schema.yml, but I can't find it.
    $entity_array = parent::toArray();
    foreach ($entity_array['field_mappings'] as &$value) {
      unset($value['mapping']);
    }
    return $entity_array;
  }

  /**
   * {@inheritdoc}
   */
  public function save() {
    $this->updated = $this->getRequestTime();
    if (isset($this->is_new) && $this->is_new) {
      $this->created = $this->getRequestTime();
    }
    return parent::save();
  }

  /**
   * Testable func to return the request time server variable.
   *
   * @return int
   *   The request time.
   */
  protected function getRequestTime() {
    return \Drupal::time()->getRequestTime();
  }

  /**
   * {@inheritdoc}
   */
  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
    // Update shared pull values across other mappings to same object type.
    $pull_mappings = $storage->loadByProperties([
      'salesforce_object_type' => $this->salesforce_object_type,
    ]);
    unset($pull_mappings[$this->id()]);
    foreach ($pull_mappings as $mapping) {
      if ($this->pull_frequency != $mapping->pull_frequency) {
        $mapping->pull_frequency = $this->pull_frequency;
        $mapping->save();
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function calculateDependencies() {
    // Include config dependencies on all mapped Drupal fields.
    $this->dependencies = array_intersect_key($this->dependencies, ['enforced' => '']);
    foreach ($this->getFieldMappings() as $field) {
      // Configuration entities need to depend on the providers of any plugins
      // that they store the configuration for. Default calculateDependencies()
      // method does not work, because our field_mapping plugins are anonymous,
      // indexed by numeric id only.
      $this->calculatePluginDependencies($field);
    }

    // Add a hard dependency on the mapping entity and bundle.
    if ($entity_type = $this->entityTypeManager()->getDefinition($this->getDrupalEntityType())) {
      $dependency = $entity_type->getBundleConfigDependency($this->getDrupalBundle());
      $this->addDependency($dependency['type'], $dependency['name']);
    }
    if ($this->doesPull()) {
      $this->addDependency('module', 'salesforce_pull');
    }
    if ($this->doesPush()) {
      $this->addDependency('module', 'salesforce_push');
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function onDependencyRemoval(array $dependencies) {
    parent::onDependencyRemoval($dependencies);

    // If the mapped entity type is being removed, we'll delete this mapping.
    $entity_type = $this->entityTypeManager()->getDefinition($this->getDrupalEntityType());
    $dependency = $entity_type->getBundleConfigDependency($this->getDrupalBundle());
    if (!empty($dependencies[$dependency['type']][$dependency['name']])) {
      return FALSE;
    }

    // Otherwise, ask each field mapping plugin if wants to remove itself.
    return $this->removePluginDependencies($dependencies);
  }

  /**
   * Delegate dependency removal events to field mappings plugins.
   *
   * @param array $dependencies
   *   Dependencies.
   */
  public function removePluginDependencies(array $dependencies) {
    $changed = FALSE;
    foreach ($this->getFieldMappings() as $i => $field) {
      if ($field->checkFieldMappingDependency($dependencies)) {
        $changed = TRUE;
        // If a plugin is dependent on the configuration being deleted, remove
        // the field mapping.
        unset($this->field_mappings[$i]);
      }
    }
    return $changed;
  }

  /**
   * {@inheritdoc}
   */
  public function getPullFields() {
    // @todo This should probably be delegated to a field plugin bag?
    $fields = [];
    foreach ($this->getFieldMappings() as $i => $field_plugin) {
      // Skip fields that aren't being pulled from Salesforce.
      if (!$field_plugin->pull()) {
        continue;
      }
      $fields[$i] = $field_plugin;
    }
    return $fields;
  }

  /**
   * {@inheritdoc}
   */
  public function getPullFieldsArray() {
    return array_column($this->field_mappings, 'salesforce_field', 'salesforce_field');
  }

  /**
   * {@inheritdoc}
   */
  public function getKeyField() {
    return $this->key ? $this->key : FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function hasKey() {
    return $this->key ? TRUE : FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function getKeyValue(EntityInterface $entity) {
    if (!$this->hasKey()) {
      throw new \Exception('No key defined for this mapping.');
    }

    // @todo #fieldMappingField
    foreach ($this->getFieldMappings() as $field_plugin) {
      if ($field_plugin->get('salesforce_field') == $this->getKeyField()) {
        return $field_plugin->value($entity, $this);
      }
    }
    throw new \Exception('Key ' . $this->getKeyField() . ' not found for this mapping.');
  }

  /**
   * {@inheritdoc}
   */
  public function getSalesforceObjectType() {
    return $this->salesforce_object_type;
  }

  /**
   * {@inheritdoc}
   */
  public function getDrupalEntityType() {
    return $this->drupal_entity_type;
  }

  /**
   * {@inheritdoc}
   */
  public function getDrupalBundle() {
    return $this->drupal_bundle;
  }

  /**
   * {@inheritdoc}
   */
  public function getFieldMappings() {
    // @todo #fieldMappingField
    $fields = [];
    foreach ($this->field_mappings as $i => $field) {
      $fields[$i] = $this->fieldManager()->createInstance(
         $field['drupal_field_type'],
         $field + ['mapping' => $this]
       );
    }
    return $fields;
  }

  /**
   * {@inheritdoc}
   */
  public function getFieldMapping(array $field) {
    return $this->fieldManager()->createInstance(
      $field['drupal_field_type'],
      $field['config'] + ['mapping' => $this]
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getPullTriggerDate() {
    return $this->pull_trigger_date;
  }

  /**
   * {@inheritdoc}
   */
  public function doesPushStandalone() {
    return $this->push_standalone;
  }

  /**
   * {@inheritdoc}
   */
  public function doesPullStandalone() {
    return $this->pull_standalone;
  }

  /**
   * {@inheritdoc}
   */
  public function doesPush() {
    return $this->checkTriggers([
      MappingConstants::SALESFORCE_MAPPING_SYNC_DRUPAL_CREATE,
      MappingConstants::SALESFORCE_MAPPING_SYNC_DRUPAL_UPDATE,
      MappingConstants::SALESFORCE_MAPPING_SYNC_DRUPAL_DELETE,
    ]);
  }

  /**
   * {@inheritdoc}
   */
  public function doesPull() {
    return $this->checkTriggers([
      MappingConstants::SALESFORCE_MAPPING_SYNC_SF_CREATE,
      MappingConstants::SALESFORCE_MAPPING_SYNC_SF_UPDATE,
      MappingConstants::SALESFORCE_MAPPING_SYNC_SF_DELETE,
    ]);
  }

  /**
   * {@inheritdoc}
   */
  public function checkTriggers(array $triggers) {
    foreach ($triggers as $trigger) {
      if (!empty($this->sync_triggers[$trigger])) {
        return TRUE;
      }
    }
    return FALSE;
  }

  /**
   * Returns the name of this configuration object.
   *
   * @return string
   *   The name of the configuration object.
   */
  public function getName() {
    return $this->name;
  }

  /**
   * {@inheritdoc}
   */
  public function getLastDeleteTime() {
    return $this->pull_info['last_delete_timestamp'] ?? NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setLastDeleteTime($time) {
    return $this->setPullInfo('last_delete_timestamp', $time);
  }

  /**
   * {@inheritdoc}
   */
  public function getLastPullTime() {
    return $this->pull_info['last_pull_timestamp'] ?? NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setLastPullTime($time) {
    return $this->setPullInfo('last_pull_timestamp', $time);
  }

  /**
   * Setter for pull info.
   *
   * @param string $key
   *   The config id to set.
   * @param mixed $value
   *   The value.
   *
   * @return $this
   */
  protected function setPullInfo($key, $value) {
    $this->pull_info[$key] = $value;
    $pull_info = $this->state()->get('salesforce.mapping_pull_info');
    $pull_info[$this->id()] = $this->pull_info;
    $this->state()->set('salesforce.mapping_pull_info', $pull_info);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getNextPullTime() {
    if (!isset($this->pull_info['last_pull_timestamp'])) {
      // The last pull time is 'never'.
      // In this case, we set next pull time to 0,
      // which will not hold off any pull operation.
      return 0;
    }
    return $this->pull_info['last_pull_timestamp'] + $this->pull_frequency;
  }

  /**
   * {@inheritdoc}
   */
  public function getLastPushTime() {
    return $this->push_info['last_timestamp'];
  }

  /**
   * {@inheritdoc}
   */
  public function setLastPushTime($time) {
    return $this->setPushInfo('last_timestamp', $time);
  }

  /**
   * Setter for pull info.
   *
   * @param string $key
   *   The config id to set.
   * @param mixed $value
   *   The value.
   *
   * @return $this
   */
  protected function setPushInfo($key, $value) {
    $this->push_info[$key] = $value;
    $push_info = $this->state()->get('salesforce.mapping_push_info');
    $push_info[$this->id()] = $this->push_info;
    $this->state()->set('salesforce.mapping_push_info', $push_info);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getNextPushTime() {
    return $this->push_info['last_timestamp'] + $this->push_frequency;
  }

  /**
   * {@inheritdoc}
   */
  public function getPullQuery(array $mapped_fields = [], $start = 0, $stop = 0) {
    if (!$this->doesPull()) {
      throw new Exception('Mapping does not pull.');
    }
    $object_type = $this->getSalesforceObjectType();
    $soql = new SelectQuery($object_type);

    // Convert field mappings to SOQL.
    if (empty($mapped_fields)) {
      $mapped_fields = $this->getPullFieldsArray();
    }
    $soql->fields = $mapped_fields;
    $soql->fields['Id'] = 'Id';
    $pull_trigger_date = $this->getPullTriggerDate();
    $soql->fields[$pull_trigger_date] = $pull_trigger_date;
    if ($pull_trigger_date !== 'LastModifiedDate') {
      $soql->fields['LastModifiedDate'] = 'LastModifiedDate';
    }

    $start = $start > 0 ? $start : $this->getLastPullTime();
    // If no lastupdate and no start window provided, get all records.
    if ($start) {
      $start = gmdate('Y-m-d\TH:i:s\Z', $start);
      $soql->addCondition($this->getPullTriggerDate(), $start, '>');
    }

    if ($stop) {
      $stop = gmdate('Y-m-d\TH:i:s\Z', $stop);
      $soql->addCondition($this->getPullTriggerDate(), $stop, '<');
    }

    if (!empty($this->pull_where_clause)) {
      $soql->conditions[] = [$this->pull_where_clause];
    }
    if (!empty($this->pull_record_type_filter)) {
      $record_types = [];
      foreach ($this->pull_record_type_filter as $enabled) {
        if ($enabled) {
          $record_types[] = $enabled;
        }
      }
      if (!empty($record_types)) {
        $record_types_list = implode("', '", $record_types);
        $soql->conditions[] = ["RecordType.DeveloperName IN ('" . $record_types_list . "')"];
      }
    }
    $soql->order[$this->getPullTriggerDate()] = 'ASC';
    return $soql;
  }

  /**
   * {@inheritdoc}
   */
  public function alwaysUpsert() {
    return $this->hasKey() && !empty($this->always_upsert);
  }

  /**
   * Salesforce Mapping Field Manager service.
   *
   * @return \Drupal\salesforce_mapping\SalesforceMappingFieldPluginManager
   *   The plugin.manager.salesforce_mapping_field service.
   */
  protected function fieldManager() {
    return \Drupal::service('plugin.manager.salesforce_mapping_field');
  }

  /**
   * Salesforce API client service.
   *
   * @return \Drupal\salesforce\Rest\RestClient
   *   The salesforce.client service.
   */
  protected function client() {
    return \Drupal::service('salesforce.client');
  }

  /**
   * State service.
   *
   * @return \Drupal\Core\State\StateInterface
   *   The state service.
   */
  protected function state() {
    return \Drupal::state();
  }

}

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

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