diff --git a/Civi/Api4/Service/Spec/Provider/AddressGetSpecProvider.php b/Civi/Api4/Service/Spec/Provider/AddressGetSpecProvider.php new file mode 100644 index 0000000000000000000000000000000000000000..f3a5048caa26d68f2e6038cba0c223336a9b5abb --- /dev/null +++ b/Civi/Api4/Service/Spec/Provider/AddressGetSpecProvider.php @@ -0,0 +1,83 @@ +<?php + +/* + +--------------------------------------------------------------------+ + | Copyright CiviCRM LLC. All rights reserved. | + | | + | This work is published under the GNU AGPLv3 license with some | + | permitted exceptions and without any warranty. For full license | + | and copyright information, see https://civicrm.org/licensing | + +--------------------------------------------------------------------+ + */ + +namespace Civi\Api4\Service\Spec\Provider; + +use Civi\Api4\Query\Api4SelectQuery; +use Civi\Api4\Service\Spec\FieldSpec; +use Civi\Api4\Service\Spec\RequestSpec; + +class AddressGetSpecProvider implements Generic\SpecProviderInterface { + + /** + * @param \Civi\Api4\Service\Spec\RequestSpec $spec + */ + public function modifySpec(RequestSpec $spec) { + // Groups field + $field = new FieldSpec('proximity', 'Address', 'Boolean'); + $field->setLabel(ts('Address Proximity')) + ->setTitle(ts('Address Proximity')) + ->setColumnName('geo_code_1') + ->setDescription(ts('Address is within a given distance to a location')) + ->setType('Filter') + ->setOperators(['<=']) + ->addSqlFilter([__CLASS__, 'getProximitySql']); + $spec->addFieldSpec($field); + } + + /** + * @param string $entity + * @param string $action + * + * @return bool + */ + public function applies($entity, $action) { + return $entity === 'Address' && $action === 'get'; + } + + /** + * @param array $field + * @param string $fieldAlias + * @param string $operator + * @param mixed $value + * @param \Civi\Api4\Query\Api4SelectQuery $query + * @param int $depth + * return string + */ + public static function getProximitySql(array $field, string $fieldAlias, string $operator, $value, Api4SelectQuery $query, int $depth): string { + $unit = $value['distance_unit'] ?? 'km'; + $distance = $value['distance'] ?? 0; + + if ($unit === 'miles') { + $distance = $distance * 1609.344; + } + else { + $distance = $distance * 1000.00; + } + + if ( + isset($value['geo_code_1']) && is_numeric($value['geo_code_1']) && + isset($value['geo_code_2']) && is_numeric($value['geo_code_2']) + ) { + return \CRM_Contact_BAO_ProximityQuery::where( + $value['geo_code_1'], + $value['geo_code_2'], + $distance, + explode('.', $fieldAlias)[0] + ); + } + // Todo: If address string given without lat/long, convert it. + + return '(0)'; + } + +} diff --git a/tests/phpunit/api/v4/Entity/AddressTest.php b/tests/phpunit/api/v4/Entity/AddressTest.php index 19c21e40447249d5b06d20afc5fc72fada0d3c6b..087bf350637cabba18517fee842268d04731f297 100644 --- a/tests/phpunit/api/v4/Entity/AddressTest.php +++ b/tests/phpunit/api/v4/Entity/AddressTest.php @@ -59,4 +59,29 @@ class AddressTest extends Api4TestBase implements TransactionalInterface { $this->assertTrue($addresses[1]['is_primary']); } + public function testSearchProximity() { + $cid = $this->createTestRecord('Contact')['id']; + $sampleData = [ + ['geo_code_1' => 20, 'geo_code_2' => 20], + ['geo_code_1' => 21, 'geo_code_2' => 21], + ['geo_code_1' => 19, 'geo_code_2' => 19], + ['geo_code_1' => 15, 'geo_code_2' => 15], + ]; + $addreses = $this->saveTestRecords('Address', [ + 'records' => $sampleData, + 'defaults' => ['contact_id' => $cid], + ])->column('id'); + + $result = Address::get(FALSE) + ->addWhere('contact_id', '=', $cid) + ->addWhere('proximity', '<=', ['distance' => 600, 'geo_code_1' => 20, 'geo_code_2' => 20]) + ->execute()->column('id'); + + $this->assertCount(3, $result); + $this->assertContains($addreses[0], $result); + $this->assertContains($addreses[1], $result); + $this->assertContains($addreses[2], $result); + $this->assertNotContains($addreses[3], $result); + } + }