You are here

function geocoder_field_entity_presave in Geocoder 8.3

Same name and namespace in other branches
  1. 8.2 modules/geocoder_field/geocoder_field.module \geocoder_field_entity_presave()

Implements hook_entity_presave().

File

modules/geocoder_field/geocoder_field.module, line 159
Geocoder Field module.

Code

function geocoder_field_entity_presave(EntityInterface $entity) {

  /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
  if (!$entity instanceof ContentEntityInterface) {
    return;
  }
  $geocoder_config = \Drupal::configFactory()
    ->get('geocoder.settings');

  // Check geocoder_presave_disabled setting and do nothing if enabled.
  if ($geocoder_config
    ->get('geocoder_presave_disabled')) {
    return;
  }

  /** @var \Drupal\geocoder_field\PreprocessorPluginManager $preprocessor_manager */
  $preprocessor_manager = \Drupal::service('plugin.manager.geocoder.preprocessor');

  /** @var \Drupal\geocoder\DumperPluginManager $dumper_manager */
  $dumper_manager = \Drupal::service('plugin.manager.geocoder.dumper');

  // Reorder the list of fields to be Geocoded | Reverse Geocoded.
  $order_geocoder_fields = $preprocessor_manager
    ->getOrderedGeocodeFields($entity);
  foreach ($order_geocoder_fields as $field_name => $field) {

    /** @var \Drupal\Core\Field\FieldConfigInterface $field_config */
    if (!($field_config = $field
      ->getFieldDefinition()) instanceof FieldConfigInterface) {

      // Only configurable fields are subject of geocoding.
      continue;
    }
    $geocoder = $field_config
      ->getThirdPartySettings('geocoder_field');
    if (empty($geocoder['method']) || $geocoder['method'] === 'none') {

      // This field was not configured to geocode/reverse_geocode from/to
      // other field.
      continue;
    }
    $remote_field = isset($geocoder['field']) ? $entity
      ->get($geocoder['field']) : NULL;
    if ($remote_field === NULL) {
      continue;
    }
    $original_field = NULL;
    if (isset($entity->original) && isset($geocoder['field'])) {

      /* @var Drupal\Core\Field\FieldItemListInterface $original_field */
      $original_field = $entity->original
        ->get($geocoder['field']);
    }

    // Skip any action if:
    // remote value is null and the field value should be preserved
    // geofield has value and remote field value has not changed.
    if (empty($remote_field
      ->getValue()) && $geocoder['failure']['handling'] == 'preserve' || NULL !== $original_field && !$entity
      ->get($field_name)
      ->isEmpty() && $preprocessor_manager
      ->sourceFieldIsSameOfOriginal($remote_field, $original_field)) {
      continue;
    }

    // First we need to Pre-process field.
    // Note: in case of Address module integration this creates the
    // value as formatted address.
    $preprocessor_manager
      ->preprocess($remote_field);
    try {

      /** @var \Drupal\geocoder\DumperInterface|\Drupal\Component\Plugin\PluginInspectionInterface $dumper */
      $dumper = $dumper_manager
        ->createInstance($geocoder['dumper']);
    } catch (PluginException $e) {
      \Drupal::logger('geocoder')
        ->error('No Dumper has been set');
    }
    $default_values = $entity
      ->get($field_name);
    $result = [];
    $failure_status_message = NULL;
    $providers = GeocoderProvider::loadMultiple($geocoder['providers']);

    // Skip Geocode/Reverse Geocode op is so requested for not empty value.
    if (isset($geocoder['skip_not_empty_value']) && $geocoder['skip_not_empty_value'] && isset($default_values) && !$default_values
      ->isEmpty()) {
      return;
    }
    foreach ($remote_field
      ->getValue() as $delta => $value) {
      if ($remote_field
        ->getFieldDefinition()
        ->getType() === 'address_country') {

        /** @var \Drupal\Core\Locale\CountryManager $country_manager */
        $country_manager = \Drupal::service('country_manager');
        $value['value'] = $country_manager
          ->getList()[$value['value']];
      }

      /* @var \Geocoder\Model\AddressCollection|\Geometry $geo_collection */
      switch ($geocoder['method']) {
        case 'geocode':

          // Allow others modules to adjust the address string.
          Drupal::service('module_handler')
            ->alter('geocode_entity_field_address_string', $value['value'], $field);
          $failure_status_message = t("Unable to geocode '@text'.", [
            '@text' => isset($value['value']) ? $value['value'] : '',
          ]);
          $geo_collection = isset($value['value']) ? \Drupal::service('geocoder')
            ->geocode($value['value'], $providers) : NULL;
          break;
        case 'reverse_geocode':

          // Allow others modules to adjust the Coordinates to Reverse Geocode.
          Drupal::service('module_handler')
            ->alter('reverse_geocode_entity_field_coordinates', $value['lat'], $value['lon'], $field);
          $failure_status_message = t('Unable to reverse geocode the <em>@field_name</em> value.', [
            '@field_name' => $field_name,
          ]);
          $geo_collection = isset($value['lat']) && isset($value['lon']) ? \Drupal::service('geocoder')
            ->reverse($value['lat'], $value['lon'], $providers) : NULL;
          break;
        default:
      }
      if (isset($dumper) && isset($geo_collection) && !$geo_collection
        ->isEmpty()) {

        // Normally each geocode/reverse op would return an AddressCollection.
        if ($geo_collection instanceof AddressCollection) {
          switch ($geocoder['delta_handling']) {

            // Single-to-multiple handling - if we can, explode out the
            // component geometries.
            case 's_to_m':
              foreach ($geo_collection
                ->all() as $collection_delta => $address) {
                $this_result = $dumper
                  ->dump($address);

                // Check|Fix some incompatibility between Dumper output and
                // Field Config.
                $dumper_manager
                  ->fixDumperFieldIncompatibility($this_result, $dumper, $field_config);
                $result[] = $this_result;
              }
              break;

            // Default delta handling: just pass one delta to the next.
            default:
              $result[$delta] = $dumper
                ->dump($geo_collection
                ->first());

              // Check|Fix some incompatibility between Dumper output and
              // Field Config.
              $dumper_manager
                ->fixDumperFieldIncompatibility($result[$delta], $dumper, $field_config);

              // If an Address field is being processed, transform its Dumper
              // result into array to comply to Address entity->set Api.
              if ($field_config
                ->getType() == 'address' && $dumper
                ->getPluginId() == 'geojson') {
                $result[$delta] = $dumper_manager
                  ->setAddressFieldFromGeojson($result[$delta]);
              }

              // Or an Address Country field is being processed ...
              if ($field_config
                ->getType() === 'address_country' && $dumper
                ->getPluginId() === 'geojson') {
                $result[$delta] = $dumper_manager
                  ->setCountryFromGeojson($result[$delta]);
              }
          }
          continue;
        }
        elseif ($geo_collection instanceof Geometry && array_key_exists($dumper
          ->getPluginId(), \Drupal::service('geofield.geophp')
          ->getAdapterMap())) {

          // Single-to-multiple handling - if we can, explode out the
          // component geometries.
          if ($geo_collection instanceof GeometryCollection && $geocoder['delta_handling'] == 's_to_m') {

            /* @var \GeometryCollection $geo_collection */
            if ($geo_collection instanceof GeometryCollection) {
              $components = $geo_collection
                ->getComponents();

              /* @var \Geometry $component */
              foreach ($components as $component) {
                $this_result = $component
                  ->out($dumper
                  ->getPluginId());

                // Check|Fix some incompatibility between Dumper output and
                // Field Config.
                $dumper_manager
                  ->fixDumperFieldIncompatibility($this_result, $dumper, $field_config);
                $result[] = $this_result;
              }
            }
          }
          else {
            $result[] = $geo_collection
              ->out($dumper
              ->getPluginId());

            // Check|Fix some incompatibility between result and Field Config.
            $dumper_manager
              ->fixDumperFieldIncompatibility($result[$delta], $dumper, $field_config);
          }
          continue;
        }
      }
      switch ($geocoder['failure']['handling']) {
        case 'preserve':
          $result[$delta] = isset($default_values[$delta]) ? $default_values[$delta]
            ->getValue() : NULL;
          break;
        case 'empty':
          $result[$delta] = NULL;
          break;
      }

      // Display a status message.
      if ($failure_status_message !== NULL && $geocoder['failure']['status_message']) {
        \Drupal::messenger()
          ->addWarning($failure_status_message);
      }

      // Log the failure.
      if ($failure_status_message !== NULL && $geocoder['failure']['log']) {
        \Drupal::logger('geocoder')
          ->warning($failure_status_message);
      }
    }
    $entity
      ->set($field_name, $result);
  }
}