Skip to content

Commit d622be2

Browse files
atymicjbelien
andauthored
feat: support api key on here provider (#1042)
* feat: support api key on here provider * fix: add main test env * fix: style * chore: cache files * Update cached responses * Update HereTest.php Use HERE_API_KEY for tests. Update getCacheDir() function. * Update cached responses * Update phpunit.xml.dist Formatting. * chore: add docs * chore: style fixes Co-authored-by: Jonathan Beliën <dev@jbelien.be>
1 parent 59dcc8d commit d622be2

File tree

60 files changed

+260
-211
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+260
-211
lines changed

phpunit.xml.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<server name="GRAPHHOPPER_API_KEY" value="YOUR_API_KEY" />
2727
<server name="HERE_APP_CODE" value="YOUR_APP_CODE" />
2828
<server name="HERE_APP_ID" value="YOUR_APP_ID" />
29+
<server name="HERE_API_KEY" value="YOUR_API_KEY" />
2930
<server name="IPINFODB_API_KEY" value="YOUR_API_KEY" />
3031
<server name="IP2LOCATION_API_KEY" value="YOUR_API_KEY" />
3132
<server name="IPSTACK_API_KEY" value="YOUR_API_KEY" />

src/Provider/Here/Here.php

Lines changed: 154 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@
1717
use Geocoder\Exception\InvalidCredentials;
1818
use Geocoder\Exception\QuotaExceeded;
1919
use Geocoder\Exception\UnsupportedOperation;
20+
use Geocoder\Http\Provider\AbstractHttpProvider;
2021
use Geocoder\Model\AddressBuilder;
2122
use Geocoder\Model\AddressCollection;
23+
use Geocoder\Provider\Here\Model\HereAddress;
24+
use Geocoder\Provider\Provider;
25+
use Geocoder\Query\Query;
2226
use Geocoder\Query\GeocodeQuery;
2327
use Geocoder\Query\ReverseQuery;
24-
use Geocoder\Http\Provider\AbstractHttpProvider;
25-
use Geocoder\Provider\Provider;
26-
use Geocoder\Provider\Here\Model\HereAddress;
2728
use Http\Client\HttpClient;
2829

2930
/**
@@ -34,22 +35,65 @@ final class Here extends AbstractHttpProvider implements Provider
3435
/**
3536
* @var string
3637
*/
37-
const GEOCODE_ENDPOINT_URL = 'https://geocoder.api.here.com/6.2/geocode.json?app_id=%s&app_code=%s&searchtext=%s&gen=9';
38+
const GEOCODE_ENDPOINT_URL_API_KEY = 'https://geocoder.ls.hereapi.com/6.2/geocode.json';
39+
40+
/**
41+
* @var string
42+
*/
43+
const GEOCODE_ENDPOINT_URL_APP_CODE = 'https://geocoder.api.here.com/6.2/geocode.json';
44+
45+
/**
46+
* @var string
47+
*/
48+
const GEOCODE_CIT_ENDPOINT_API_KEY = 'https:/geocoder.sit.ls.hereapi.com/6.2/geocode.json';
3849

3950
/**
4051
* @var string
4152
*/
42-
const REVERSE_ENDPOINT_URL = 'https://reverse.geocoder.api.here.com/6.2/reversegeocode.json?prox=%F,%F,250&app_id=%s&app_code=%s&mode=retrieveAddresses&gen=9&maxresults=%d';
53+
const GEOCODE_CIT_ENDPOINT_APP_CODE = 'https://geocoder.cit.api.here.com/6.2/geocode.json';
4354

4455
/**
4556
* @var string
4657
*/
47-
const GEOCODE_CIT_ENDPOINT_URL = 'https://geocoder.cit.api.here.com/6.2/geocode.json?app_id=%s&app_code=%s&searchtext=%s&gen=9';
58+
const REVERSE_ENDPOINT_URL_API_KEY = 'https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json';
4859

4960
/**
5061
* @var string
5162
*/
52-
const REVERSE_CIT_ENDPOINT_URL = 'https://reverse.geocoder.cit.api.here.com/6.2/reversegeocode.json?prox=%F,%F,250&app_id=%s&app_code=%s&mode=retrieveAddresses&gen=9&maxresults=%d';
63+
const REVERSE_ENDPOINT_URL_APP_CODE = 'https://reverse.geocoder.api.here.com/6.2/reversegeocode.json';
64+
65+
/**
66+
* @var string
67+
*/
68+
const REVERSE_CIT_ENDPOINT_URL_API_KEY = 'https://reverse.geocoder.sit.ls.hereapi.com/6.2/reversegeocode.json';
69+
70+
/**
71+
* @var string
72+
*/
73+
const REVERSE_CIT_ENDPOINT_URL_APP_CODE = 'https://reverse.geocoder.cit.api.here.com/6.2/reversegeocode.json';
74+
75+
/**
76+
* @var array
77+
*/
78+
const GEOCODE_ADDITIONAL_DATA_PARAMS = [
79+
'CrossingStreets',
80+
'PreserveUnitDesignators',
81+
'Country2',
82+
'IncludeChildPOIs',
83+
'IncludeRoutingInformation',
84+
'AdditionalAddressProvider',
85+
'HouseNumberMode',
86+
'FlexibleAdminValues',
87+
'IntersectionSnapTolerance',
88+
'AddressRangeSqueezeOffset',
89+
'AddressRangeSqueezeFactor',
90+
'AddressRangeSqueezeOffset',
91+
'IncludeShapeLevel',
92+
'RestrictLevel',
93+
'SuppressStreetType',
94+
'NormalizeNames',
95+
'IncludeMicroPointAddresses',
96+
];
5397

5498
/**
5599
* @var string
@@ -66,24 +110,34 @@ final class Here extends AbstractHttpProvider implements Provider
66110
*/
67111
private $useCIT;
68112

113+
/**
114+
* @var string
115+
*/
116+
private $apiKey;
117+
69118
/**
70119
* @param HttpClient $client an HTTP adapter
71120
* @param string $appId an App ID
72121
* @param string $appCode an App code
73122
* @param bool $useCIT use Customer Integration Testing environment (CIT) instead of production
74123
*/
75-
public function __construct(HttpClient $client, string $appId, string $appCode, bool $useCIT = false)
124+
public function __construct(HttpClient $client, string $appId = null, string $appCode = null, bool $useCIT = false)
76125
{
77-
if (empty($appId) || empty($appCode)) {
78-
throw new InvalidCredentials('Invalid or missing api key.');
79-
}
80126
$this->appId = $appId;
81127
$this->appCode = $appCode;
82128
$this->useCIT = $useCIT;
83129

84130
parent::__construct($client);
85131
}
86132

133+
public static function createUsingApiKey(HttpClient $client, string $apiKey, bool $useCIT = false): self
134+
{
135+
$client = new self($client, null, null, $useCIT);
136+
$client->apiKey = $apiKey;
137+
138+
return $client;
139+
}
140+
87141
/**
88142
* {@inheritdoc}
89143
*/
@@ -94,100 +148,33 @@ public function geocodeQuery(GeocodeQuery $query): Collection
94148
throw new UnsupportedOperation('The Here provider does not support IP addresses, only street addresses.');
95149
}
96150

97-
$url = sprintf($this->useCIT ? self::GEOCODE_CIT_ENDPOINT_URL : self::GEOCODE_ENDPOINT_URL, $this->appId, $this->appCode, rawurlencode($query->getText()));
151+
$queryParams = $this->withApiCredentials([
152+
'searchtext' => $query->getText(),
153+
'gen' => 9,
154+
'additionaldata' => $this->getAdditionalDataParam($query),
155+
]);
98156

99157
if (null !== $query->getData('country')) {
100-
$url = sprintf('%s&country=%s', $url, rawurlencode($query->getData('country')));
158+
$queryParams['country'] = $query->getData('country');
101159
}
102160

103161
if (null !== $query->getData('state')) {
104-
$url = sprintf('%s&state=%s', $url, rawurlencode($query->getData('state')));
162+
$queryParams['state'] = $query->getData('state');
105163
}
106164

107165
if (null !== $query->getData('county')) {
108-
$url = sprintf('%s&county=%s', $url, rawurlencode($query->getData('county')));
166+
$queryParams['county'] = $query->getData('county');
109167
}
110168

111169
if (null !== $query->getData('city')) {
112-
$url = sprintf('%s&city=%s', $url, rawurlencode($query->getData('city')));
170+
$queryParams['city'] = $query->getData('city');
113171
}
114172

115173
if (null !== $query->getLocale()) {
116-
$url = sprintf('%s&language=%s', $url, $query->getLocale());
117-
}
118-
119-
$additionalDataParam = [];
120-
if (null !== $query->getData('CrossingStreets')) {
121-
$additionalDataParam['CrossingStreets'] = $query->getData('CrossingStreets');
122-
}
123-
124-
if (null !== $query->getData('PreserveUnitDesignators')) {
125-
$additionalDataParam['PreserveUnitDesignators'] = $query->getData('PreserveUnitDesignators');
126-
}
127-
128-
if (null !== $query->getData('Country2')) {
129-
$additionalDataParam['Country2'] = $query->getData('Country2');
174+
$queryParams['language'] = $query->getLocale();
130175
}
131176

132-
if (null !== $query->getData('IncludeChildPOIs')) {
133-
$additionalDataParam['IncludeChildPOIs'] = $query->getData('IncludeChildPOIs');
134-
}
135-
136-
if (null !== $query->getData('IncludeRoutingInformation')) {
137-
$additionalDataParam['IncludeRoutingInformation'] = $query->getData('IncludeRoutingInformation');
138-
}
139-
140-
if (null !== $query->getData('AdditionalAddressProvider')) {
141-
$additionalDataParam['AdditionalAddressProvider'] = $query->getData('AdditionalAddressProvider');
142-
}
143-
144-
if (null !== $query->getData('HouseNumberMode')) {
145-
$additionalDataParam['HouseNumberMode'] = $query->getData('HouseNumberMode');
146-
}
147-
148-
if (null !== $query->getData('FlexibleAdminValues')) {
149-
$additionalDataParam['FlexibleAdminValues'] = $query->getData('FlexibleAdminValues');
150-
}
151-
152-
if (null !== $query->getData('IntersectionSnapTolerance')) {
153-
$additionalDataParam['IntersectionSnapTolerance'] = $query->getData('IntersectionSnapTolerance');
154-
}
155-
156-
if (null !== $query->getData('AddressRangeSqueezeOffset')) {
157-
$additionalDataParam['AddressRangeSqueezeOffset'] = $query->getData('AddressRangeSqueezeOffset');
158-
}
159-
160-
if (null !== $query->getData('AddressRangeSqueezeFactor')) {
161-
$additionalDataParam['AddressRangeSqueezeFactor'] = $query->getData('AddressRangeSqueezeFactor');
162-
}
163-
164-
if (null !== $query->getData('IncludeShapeLevel')) {
165-
$additionalDataParam['IncludeShapeLevel'] = $query->getData('IncludeShapeLevel');
166-
}
167-
168-
if (null !== $query->getData('RestrictLevel')) {
169-
$additionalDataParam['RestrictLevel'] = $query->getData('RestrictLevel');
170-
}
171-
172-
if (null !== $query->getData('SuppressStreetType')) {
173-
$additionalDataParam['SuppressStreetType'] = $query->getData('SuppressStreetType');
174-
}
175-
176-
if (null !== $query->getData('NormalizeNames')) {
177-
$additionalDataParam['NormalizeNames'] = $query->getData('NormalizeNames');
178-
}
179-
180-
if (null !== $query->getData('IncludeMicroPointAddresses')) {
181-
$additionalDataParam['IncludeMicroPointAddresses'] = $query->getData('IncludeMicroPointAddresses');
182-
}
183-
184-
$additionalDataParam['IncludeShapeLevel'] = 'country';
185-
186-
if (!empty($additionalDataParam)) {
187-
$url = sprintf('%s&additionaldata=%s', $url, $this->serializeComponents($additionalDataParam));
188-
}
189-
190-
return $this->executeQuery($url, $query->getLimit());
177+
return $this->executeQuery(sprintf('%s?%s', $this->getBaseUrl($query), http_build_query($queryParams)), $query->getLimit());
191178
}
192179

193180
/**
@@ -196,9 +183,15 @@ public function geocodeQuery(GeocodeQuery $query): Collection
196183
public function reverseQuery(ReverseQuery $query): Collection
197184
{
198185
$coordinates = $query->getCoordinates();
199-
$url = sprintf($this->useCIT ? self::REVERSE_CIT_ENDPOINT_URL : self::REVERSE_ENDPOINT_URL, $coordinates->getLatitude(), $coordinates->getLongitude(), $this->appId, $this->appCode, $query->getLimit());
200186

201-
return $this->executeQuery($url, $query->getLimit());
187+
$queryParams = $this->withApiCredentials([
188+
'gen' => 9,
189+
'mode' => 'retrieveAddresses',
190+
'prox' => sprintf('%s,%s', $coordinates->getLatitude(), $coordinates->getLongitude()),
191+
'maxresults' => $query->getLimit(),
192+
]);
193+
194+
return $this->executeQuery(sprintf('%s?%s', $this->getBaseUrl($query), http_build_query($queryParams)), $query->getLimit());
202195
}
203196

204197
/**
@@ -210,6 +203,7 @@ public function reverseQuery(ReverseQuery $query): Collection
210203
private function executeQuery(string $url, int $limit): Collection
211204
{
212205
$content = $this->getUrlContents($url);
206+
213207
$json = json_decode($content, true);
214208

215209
if (isset($json['type'])) {
@@ -233,6 +227,8 @@ private function executeQuery(string $url, int $limit): Collection
233227

234228
$locations = $json['Response']['View'][0]['Result'];
235229

230+
$results = [];
231+
236232
foreach ($locations as $loc) {
237233
$location = $loc['Location'];
238234
$builder = new AddressBuilder($this->getName());
@@ -281,6 +277,73 @@ public function getName(): string
281277
return 'Here';
282278
}
283279

280+
/**
281+
* Get serialized additional data param.
282+
*
283+
* @param GeocodeQuery $query
284+
*
285+
* @return string
286+
*/
287+
private function getAdditionalDataParam(GeocodeQuery $query): string
288+
{
289+
$additionalDataParams = [
290+
'IncludeShapeLevel' => 'country',
291+
];
292+
293+
foreach (self::GEOCODE_ADDITIONAL_DATA_PARAMS as $paramKey) {
294+
if (null !== $query->getData($paramKey)) {
295+
$additionalDataParams[$paramKey] = $query->getData($paramKey);
296+
}
297+
}
298+
299+
return $this->serializeComponents($additionalDataParams);
300+
}
301+
302+
/**
303+
* Add API credentials to query params.
304+
*
305+
* @param array $queryParams
306+
*
307+
* @return array
308+
*/
309+
private function withApiCredentials(array $queryParams): array
310+
{
311+
if (
312+
empty($this->apiKey) &&
313+
(empty($this->appId) || empty($this->appCode))
314+
) {
315+
throw new InvalidCredentials('Invalid or missing api key.');
316+
}
317+
318+
if (null !== $this->apiKey) {
319+
$queryParams['apiKey'] = $this->apiKey;
320+
} else {
321+
$queryParams['app_id'] = $this->appId;
322+
$queryParams['app_code'] = $this->appCode;
323+
}
324+
325+
return $queryParams;
326+
}
327+
328+
public function getBaseUrl(Query $query): string
329+
{
330+
$usingApiKey = null !== $this->apiKey;
331+
332+
if ($query instanceof ReverseQuery) {
333+
if ($this->useCIT) {
334+
return $usingApiKey ? self::REVERSE_CIT_ENDPOINT_URL_API_KEY : self::REVERSE_CIT_ENDPOINT_URL_APP_CODE;
335+
}
336+
337+
return $usingApiKey ? self::REVERSE_ENDPOINT_URL_API_KEY : self::REVERSE_ENDPOINT_URL_APP_CODE;
338+
}
339+
340+
if ($this->useCIT) {
341+
return $usingApiKey ? self::GEOCODE_CIT_ENDPOINT_API_KEY : self::GEOCODE_CIT_ENDPOINT_APP_CODE;
342+
}
343+
344+
return $usingApiKey ? self::GEOCODE_ENDPOINT_URL_API_KEY : self::GEOCODE_ENDPOINT_URL_APP_CODE;
345+
}
346+
284347
/**
285348
* Serialize the component query parameter.
286349
*

0 commit comments

Comments
 (0)