View source
<?php
namespace Drupal\civicrm_entity\Plugin\views\filter;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\civicrm_entity\CiviCrmApiInterface;
use Drupal\views\ViewExecutable;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\Core\Database\Query\Condition;
class Proximity extends FilterPluginBase {
const COUNTRY_ID = 1228;
protected $civicrmApi;
public function __construct(array $configuration, $plugin_id, array $plugin_definition, CiviCrmApiInterface $civicrm_api) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->civicrmApi = $civicrm_api;
$this->alwaysMultiple = TRUE;
$this->no_operator = TRUE;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('civicrm_entity.api'));
}
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$this->civicrmApi
->civicrmInitialize();
\CRM_Contact_BAO_ProximityQuery::initialize();
}
public function defineOptions() {
$options = parent::defineOptions();
$options['value'] = [
'contains' => [
'value' => [
'default' => '',
],
'city' => [
'default' => '',
],
'state_province_id' => [
'default' => '',
],
'distance' => [
'default' => '',
],
'distance_unit' => [
'default' => '',
],
],
];
return $options;
}
public function adminSummary() {
if (!empty($this->options['exposed'])) {
return $this
->t('exposed');
}
return $this
->t('within @postal_code', [
'@postal_code' => $this->value['value'],
]);
}
public function valueForm(&$form, FormStateInterface $form_state) {
$form['value']['#tree'] = TRUE;
$form['value']['#type'] = 'fieldset';
$form['value']['city'] = [
'#type' => 'textfield',
'#title' => $this
->t('City'),
'#size' => 30,
'#default_value' => $this->value['city'],
];
$values = $this->civicrmApi
->get('StateProvince', [
'sequential' => 1,
'country_id' => static::COUNTRY_ID,
'options' => [
'limit' => 0,
],
]);
$form['value']['state_province_id'] = [
'#type' => 'select',
'#options' => [
'' => $this
->t('- Any -'),
] + array_combine(array_column($values, 'id'), array_column($values, 'name')),
'#title' => $this
->t('State/Province'),
'#default_value' => $this->value['state_province_id'],
];
$form['value']['value'] = [
'#type' => 'textfield',
'#title' => $this
->t('Postal code'),
'#size' => 30,
'#default_value' => $this->value['value'],
];
$form['value']['distance'] = [
'#type' => 'number',
'#title' => $this
->t('Distance'),
'#default_value' => $this->value['distance'],
];
$form['value']['distance_unit'] = [
'#type' => 'select',
'#title' => $this
->t('Distance unit'),
'#default_value' => $this->value['distance_unit'],
'#options' => [
'miles' => $this
->t('Miles'),
'kilometers' => $this
->t('Kilometers'),
],
];
}
public function query() {
if ((!empty($this->value['value']) || !empty($this->value['city']) || !empty($this->value['state_province_id'])) && !empty($this->value['distance'])) {
$distance = $this
->getCalculatedDistance($this->value['distance'], $this->value['distance_unit']);
$countries = $this->civicrmApi
->get('Country', [
'sequential' => 1,
'id' => static::COUNTRY_ID,
'return' => [
'name',
],
]);
$proximity_address = [
'postal_code' => $this->value['value'],
'state_province_id' => $this->value['state_province_id'],
'city' => $this->value['city'],
'country' => !empty($countries) ? $countries[0]['name'] : '',
'country_id' => static::COUNTRY_ID,
'distance_unit' => $this->value['distance_unit'],
];
$geocoded_address = $this
->getGeocodedAddress($proximity_address);
list($min_longitude, $max_longitude) = \CRM_Contact_BAO_ProximityQuery::earthLongitudeRange($geocoded_address['longitude'], $geocoded_address['latitude'], $distance);
list($min_latitude, $max_latitude) = \CRM_Contact_BAO_ProximityQuery::earthLatitudeRange($geocoded_address['longitude'], $geocoded_address['latitude'], $distance);
$this
->ensureMyTable();
$condition = new Condition('AND');
if (!is_nan($min_latitude)) {
$condition
->condition("{$this->tableAlias}.geo_code_1", $min_latitude, '>=');
}
if (!is_nan($max_latitude)) {
$condition
->condition("{$this->tableAlias}.geo_code_1", $max_latitude, '<=');
}
if (!is_nan($min_longitude)) {
$condition
->condition("{$this->tableAlias}.geo_code_2", $min_longitude, '>=');
}
if (!is_nan($max_longitude)) {
$condition
->condition("{$this->tableAlias}.geo_code_2", $max_longitude, '<=');
}
if ($condition
->count() > 0) {
$this->query
->addWhere($this->options['group'], $condition);
}
$expression = "\n ACOS(\n COS(RADIANS({$this->tableAlias}.geo_code_1)) *\n COS(RADIANS({$geocoded_address['latitude']})) *\n COS(RADIANS({$this->tableAlias}.geo_code_2) - RADIANS({$geocoded_address['longitude']})) +\n SIN(RADIANS({$this->tableAlias}.geo_code_1)) *\n SIN(RADIANS({$geocoded_address['latitude']}))\n ) * 6378137\n ";
$this->query
->addWhereExpression($this->options['group'], "{$expression} <= {$distance}");
}
}
protected function getCalculatedDistance($distance, $distance_unit) {
switch ($distance_unit) {
case 'miles':
$distance *= 1609.344;
break;
case 'kilometers':
default:
$distance *= 1000.0;
break;
}
return $distance;
}
protected function getGeocodedAddress(array $address) {
$address = array_filter($address);
if (!\CRM_Core_BAO_Address::addGeocoderData($address)) {
throw new \Exception('Unable to properly geocode address.');
}
return [
'latitude' => $address['geo_code_1'],
'longitude' => $address['geo_code_2'],
];
}
}