civicrm_entity-8.x-3.0-beta1/src/Plugin/views/field/CivicrmEntityDistance.php

src/Plugin/views/field/CivicrmEntityDistance.php
<?php

/**
 * @file
 * Issue: 3541996 - Provides a new "Distance" field for CiviCRM proximity filters.
 */

namespace Drupal\civicrm_entity\Plugin\views\field;

use Drupal\views\Attribute\ViewsField;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\ResultRow;
use Drupal\Core\Form\FormStateInterface;

/**
 * Field handler to display calculated distance from proximity filter.
 *
 * @ViewsField("civicrm_entity_distance")
 */
#[ViewsField("civicrm_entity_distance")]
class CivicrmEntityDistance extends FieldPluginBase {

  /**
   * {@inheritdoc}
   */
  public function canExpose() {
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function query() {
    // Get coordinates from proximity filter
    $center_lat = $center_lon = NULL;
    
    // Method 1: Check view storage (set by proximity filter)
    if (!empty($this->view->proximity_center)) {
      $center_lat = $this->view->proximity_center['latitude'];
      $center_lon = $this->view->proximity_center['longitude'];
    } else {
      // Method 2: Get directly from proximity filter
      $proximity_filter = $this->getProximityFilter();
      if ($proximity_filter && method_exists($proximity_filter, 'getProximityValues')) {
        $values = $proximity_filter->getProximityValues();
        if (!empty($values['latitude']) && !empty($values['longitude'])) {
          $center_lat = $values['latitude'];
          $center_lon = $values['longitude'];
        }
      }
    }

    if ($center_lat && $center_lon) {
      // Get the correct table alias for civicrm_address
      $address_table = 'contact_id_civicrm_contact';
      
      // Create distance formula with proper table alias
      $formula = $this->getDistanceFormula($center_lat, $center_lon, $address_table);
      
      // Add the field to the query
      $this->field_alias = $this->query->addField(NULL, $formula, 'distance_calculated');
      $this->addAdditionalFields();
    }
  }

  /**
   * Get the proximity filter from the current view.
   */
  protected function getProximityFilter() {
    $filters = $this->view->display_handler->getHandlers('filter');
    
    foreach ($filters as $filter_id => $filter) {
      if ($filter->getPluginId() == 'civicrm_entity_civicrm_address_proximity') {
        return $filter;
      }
    }
    
    return NULL;
  }

  /**
   * Calculate distance formula with proper table alias and units.
   */
  protected function getDistanceFormula($center_lat, $center_lon, $address_table) {
    // Make sure we have a table alias
    if (empty($address_table)) {
      $address_table = 'civicrm_address';
    }
    
    // Determine the unit to use
    $unit = $this->getDistanceUnit();
    
    // Set earth radius based on unit
    if ($unit == 'mi' || $unit == 'miles') {
      $earth_radius = 3958.8; // Earth radius in miles
    } else {
      $earth_radius = 6378.137; // Earth radius in kilometers
    }
    
    // Distance formula with proper units
    $formula = "
      (ACOS(
        COS(RADIANS({$address_table}.geo_code_1)) *
        COS(RADIANS($center_lat)) *
        COS(RADIANS({$address_table}.geo_code_2) - RADIANS($center_lon)) +
        SIN(RADIANS({$address_table}.geo_code_1)) *
        SIN(RADIANS($center_lat))
      ) * $earth_radius)
    ";
    
    return $formula;
  }

  /**
   * Get the distance unit to use for calculations.
   */
  protected function getDistanceUnit() {
    // Priority 1: Field configuration
    if (!empty($this->options['unit'])) {
      return $this->options['unit'];
    }
    
    // Priority 2: Proximity filter settings
    if (!empty($this->view->proximity_center['distance_unit'])) {
      $proximity_unit = $this->view->proximity_center['distance_unit'];
      // Convert proximity filter units to field units
      if ($proximity_unit == 'miles') {
        return 'mi';
      } elseif ($proximity_unit == 'kilometers') {
        return 'km';
      }
    }
    
    // Priority 3: Get from proximity filter directly
    $proximity_filter = $this->getProximityFilter();
    if ($proximity_filter && !empty($proximity_filter->value['distance_unit'])) {
      $proximity_unit = $proximity_filter->value['distance_unit'];
      if ($proximity_unit == 'miles') {
        return 'mi';
      } elseif ($proximity_unit == 'kilometers') {
        return 'km';
      }
    }
    
    // Default to kilometers
    return 'km';
  }

  /**
   * {@inheritdoc}
   */
  public function render(ResultRow $values) {
    $value = $this->getValue($values);
    
    if ($value !== NULL && $value !== '') {
      $distance = round((float)$value, (int)($this->options['precision'] ?? 2));
      $unit = $this->getDistanceUnit();
      
      // Display unit labels
      $unit_label = ($unit == 'mi' || $unit == 'miles') ? 'mi' : 'km';
      
      return $distance . ' ' . $unit_label;
    }
    
    return '';
  }

  /**
   * {@inheritdoc}
   */
  protected function defineOptions() {
    $options = parent::defineOptions();
    $options['unit'] = ['default' => '']; // Empty = use proximity filter unit
    $options['precision'] = ['default' => 2];
    return $options;
  }

  /**
   * {@inheritdoc}
   */
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    parent::buildOptionsForm($form, $form_state);
    
    $form['unit'] = [
      '#type' => 'select',
      '#title' => $this->t('Distance unit'),
      '#description' => $this->t('Override the unit from the proximity filter. Leave empty to use the proximity filter\'s unit setting.'),
      '#options' => [
        '' => $this->t('- Use proximity filter unit -'),
        'km' => $this->t('Kilometers'),
        'mi' => $this->t('Miles'),
      ],
      '#default_value' => $this->options['unit'],
    ];
    
    $form['precision'] = [
      '#type' => 'number',
      '#title' => $this->t('Decimal places'),
      '#description' => $this->t('Number of decimal places to display.'),
      '#default_value' => $this->options['precision'],
      '#min' => 0,
      '#max' => 5,
    ];
  }
}

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

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