Skip to content

Commit f9c3f44

Browse files
committed
Support distance calculation and limits in address queries
1 parent 444c2a5 commit f9c3f44

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

src/elements/Address.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ public static function addressAttributeLabel(string $attribute, string $countryC
295295
*/
296296
public ?string $longitude = null;
297297

298+
/**
299+
* @var string|null Distance in meters
300+
*/
301+
public ?float $distance = null;
302+
298303
/**
299304
* @inheritdoc
300305
*/

src/elements/db/AddressQuery.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use craft\db\Table;
1313
use craft\elements\Address;
1414
use craft\helpers\Db;
15+
use yii\db\Expression;
1516

1617
/**
1718
* AddressQuery represents a SELECT SQL statement for categories in a way that is independent of DBMS.
@@ -316,6 +317,26 @@ class AddressQuery extends ElementQuery implements NestedElementQueryInterface
316317
*/
317318
public ?string $lastName = null;
318319

320+
/**
321+
* @var null|array Narrows the query results based on the distance to a set of coordinates.
322+
* ---
323+
* ```php
324+
* // Fetch addresses by distance to the given coordinates
325+
* $addresses = \craft\elements\Address::find()
326+
* ->distanceTo(['latitude' => 44.0473,'longitude' => -121.3338])
327+
* ->all();
328+
* ```
329+
* ```twig
330+
* {# Fetch addresses by distance to the given coordinates #}
331+
* {% set addresses = craft.addresses()
332+
* .distanceTo({ latitude: 44.0473, longitude: -121.3338 })
333+
* .all() %}
334+
* ```
335+
*
336+
* @used-by distanceTo()
337+
*/
338+
public ?array $distanceTo = null;
339+
319340
/**
320341
* Narrows the query results based on the country the addresses belong to.
321342
*
@@ -863,6 +884,31 @@ public function lastName(?string $value): static
863884
return $this;
864885
}
865886

887+
/**
888+
* Calculate the distance of the address to a given set of coordinates, and
889+
* optionally narrows the query results by minimum or maximum distance.
890+
* Excludes addresses without valid coordinates.
891+
*
892+
* The coordinates should be provided as an associative array with the following keys:
893+
*
894+
* | Key | Value
895+
* | - | -
896+
* | `latitude` | Required, the latitude of the point to measure distance to.
897+
* | `longitude` | Required, the longitude of the point to measure distance to.
898+
* | `min` | Minimum distance in meters to narrow results by.
899+
* | `max` | Maximum distance in meters to narrow results by.
900+
*
901+
* @return static self reference
902+
*
903+
* @uses $distanceTo
904+
*/
905+
public function distanceTo(array $coordinates): static
906+
{
907+
$this->distanceTo = $coordinates;
908+
909+
return $this;
910+
}
911+
866912
/**
867913
* @inheritdoc
868914
*/
@@ -965,6 +1011,34 @@ protected function beforePrepare(): bool
9651011
$this->subQuery->andWhere(Db::parseParam('addresses.fullName', $this->fullName));
9661012
}
9671013

1014+
if ($this->distanceTo) {
1015+
$latitude = $this->distanceTo['latitude'];
1016+
$longitude = $this->distanceTo['longitude'];
1017+
$min = $this->distanceTo['min'] ?? null;
1018+
$max = $this->distanceTo['max'] ?? null;
1019+
1020+
$distance = new Expression(
1021+
'ST_Distance_Sphere(
1022+
POINT([[addresses.longitude]], [[addresses.latitude]]),
1023+
POINT(:lng, :lat)
1024+
)',
1025+
[':lng' => $longitude, ':lat' => $latitude]
1026+
);
1027+
$this->subQuery->addSelect(['distance' => $distance]);
1028+
$this->query->addSelect(['distance' => $distance]);
1029+
1030+
$this->subQuery->andWhere(['not', ['[[addresses.latitude]]' => null]]);
1031+
$this->subQuery->andWhere(['not', ['[[addresses.longitude]]' => null]]);
1032+
1033+
if ($min) {
1034+
$this->query->andWhere(['>=', $distance, $min]);
1035+
}
1036+
1037+
if ($max) {
1038+
$this->query->andWhere(['<=', $distance, $max]);
1039+
}
1040+
}
1041+
9681042
return true;
9691043
}
9701044

0 commit comments

Comments
 (0)