Skip to content

Commit f7ff5dc

Browse files
author
HEUTE Damien
committed
* EVOL Use of NotFoundException + Storage of place names and postal codes + Tests added
1 parent 465c14a commit f7ff5dc

File tree

5 files changed

+160
-49
lines changed

5 files changed

+160
-49
lines changed

composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
"prefer-stable": true,
1515
"require": {
1616
"php": ">=7.0.0",
17+
"phpfacile/openstreetmap": "^1.0.1",
18+
"crazycodr/standard-exceptions": "^2.3",
1719
"zendframework/zend-db": "^2.5"
1820
},
1921
"require-dev": {

docs/CHANGELOG.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
* EVOL Use of NotFoundException + Storage of place names and postal codes + Tests added
2+
13
1.0.2 (2018-11-13)
24
-------
35
* EVOL Locations table is now splitted into geocoder_locations (non localized data from geocoder), places (official names retrieved from openstreetmap), place_names and place_postal_codes (theses 2 last tables are not populated in this release)* EVOL Locations table is now splitted into geocoder_locations (non localized data from geocoder), places (official names retrieved from openstreetmap), place_names and place_postal_codes (theses 2 last tables are not populated in this release)

src/Service/LocationService.php

Lines changed: 79 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
<?php
22
namespace PHPFacile\Geocoding\Db\Service;
33

4+
use PHPFacile\Openstreetmap\Service\OpenstreetmapServiceInterface;
5+
46
use Zend\Db\Adapter\AdapterInterface;
57
use Zend\Db\Sql\Sql;
68

9+
use Exceptions\Data\NotFoundException;
10+
use Exceptions\Data\FoundTooManyException;
11+
712
class LocationService
813
{
914
/**
10-
* @var Adapater
15+
* @var Adapter
1116
*/
1217
protected $adapter;
1318

@@ -25,7 +30,7 @@ public function __construct(AdapterInterface $adapter)
2530
$this->adapter = $adapter;
2631
}
2732

28-
public function setOpenstreetmapService($openstreetmapService)
33+
public function setOpenstreetmapService(OpenstreetmapServiceInterface $openstreetmapService)
2934
{
3035
$this->openstreetmapService = $openstreetmapService;
3136
}
@@ -35,14 +40,15 @@ public function setOpenstreetmapService($openstreetmapService)
3540
*
3641
* @param StdClass $geocodedLocation Geocoded location as returned by phpfacile/geocoding
3742
*
38-
* @throws Exception In case the geocoded location is not found or if there are several matches
43+
* @throws NotFoundException In case the geocoded location is not found
44+
* @throws FoundTooManyException If there are several matches
3945
*
4046
* @return string Id in the database
4147
*/
4248
public function getGeocoderLocationIdFromGeocodedPlaceStdClass($geocodedLocation)
4349
{
4450
$where = [
45-
'geocoding_provider' => $geocodedLocation->geocoding->provider,
51+
'geocoding_provider' => $geocodedLocation->geocoding->provider,
4652
'geocoder_object_id' => $geocodedLocation->geocoding->idProvider,
4753
];
4854

@@ -55,10 +61,10 @@ public function getGeocoderLocationIdFromGeocodedPlaceStdClass($geocodedLocation
5561
$rows = $stmt->execute();
5662
if (false === ($row = $rows->current())) {
5763
// TODO replace with a NotFoundException
58-
throw new \Exception('Not found');
64+
throw new NotFoundException('Geocoder location ['. $geocodedLocation->geocoding->provider.':'.$geocodedLocation->geocoding->idProvider.'] not found');
5965
} else if (false !== $rows->next()) {
6066
// TODO replace with a TooManyHitsException
61-
throw new \Exception('Too many hits');
67+
throw new FoundTooManyException('Too many geocoder locations found for ['.$geocodedLocation->geocoding->provider.':'.$geocodedLocation->geocoding->idProvider.']');
6268
}
6369

6470
return $row['id'];
@@ -79,13 +85,13 @@ public function insertGeocoderLocationOfGeocodedPlaceStdClass($geocodedLocation)
7985
1st step - Actually store the geocoder location data
8086
*/
8187
$geocoding = $geocodedLocation->geocoding;
82-
$values['geocoding_datetime_utc'] = $geocoding->geocodingDateTimeUTC;
83-
$values['geocoding_provider'] = $geocoding->provider;
84-
$values['geocoder_object_id'] = $geocoding->idProvider;
85-
$values['geocoded_latitude'] = $geocoding->coordinates->latitude;
86-
$values['geocoded_longitude'] = $geocoding->coordinates->longitude;
87-
$values['geocoded_country_code'] = $geocoding->country->isoCode;
88-
$values['geocoded_timezone'] = $geocoding->timezone;
88+
$values['geocoding_datetime_utc'] = $geocoding->geocodingDateTimeUTC;
89+
$values['geocoding_provider'] = $geocoding->provider;
90+
$values['geocoder_object_id'] = $geocoding->idProvider;
91+
$values['geocoded_latitude'] = $geocoding->coordinates->latitude;
92+
$values['geocoded_longitude'] = $geocoding->coordinates->longitude;
93+
$values['geocoded_country_code'] = $geocoding->country->isoCode;
94+
$values['geocoded_timezone'] = $geocoding->timezone;
8995

9096
$sql = new Sql($this->adapter);
9197
$query = $sql
@@ -101,16 +107,18 @@ public function insertGeocoderLocationOfGeocodedPlaceStdClass($geocodedLocation)
101107
in future. So as to be able to geocode places with (almost) no more
102108
external geocoder API call.
103109
*/
110+
$placeNames = [];
111+
$postalCodes = [];
104112
switch ($geocodedLocation->geocoding->provider) {
105113
case 'nominatim':
106-
$relation = $this->openstreetmapService->getRelationById($geocodedLocation->geocoding->idProvider);
107-
$officialName = $relation->getOfficialName();
108-
$placeNames = $relation->getNames();
109-
$placePostalCodes = $relation->getPostalCodes();
110-
// TODO Not sure this is the best way to retrieve the country code
114+
$relation = $this->openstreetmapService->getRelationById($geocodedLocation->geocoding->idProvider);
115+
$officialName = $relation->getOfficialName();
116+
$placeNames = $relation->getNames();
117+
$postalCodes = $relation->getPostalCodes();
118+
// FIXME Not sure this is the best way to retrieve the country code
111119
$countryCode = $geocodedLocation->country->code;
112-
if (1 === count($placePostalCodes)) {
113-
$keptPostalCode = $placePostalCodes[0];
120+
if (1 === count($postalCodes)) {
121+
$keptPostalCode = $postalCodes[0];
114122
} else {
115123
// probably several postal codes for the same area
116124
$keptPostalCode = null;
@@ -124,32 +132,59 @@ public function insertGeocoderLocationOfGeocodedPlaceStdClass($geocodedLocation)
124132
// or with same name and postal code in the same country?
125133
try {
126134
$placeId = $this->getIdOfPlaceByNamePostalCodeCountryCodeEtc($officialName, $keptPostalCode, $countryCode, $geocodedLocation->geocoding->provider, $geocodedLocation->geocoding->idProvider);
127-
} catch (\Exception $e) {
128-
// TODO replace with a NotFoundException
129-
if ('Not found' === $e->getMessage()) {
130-
// Ok insert
131-
$values = [];
132-
$values['name'] = $officialName;
133-
$values['postal_code'] = $keptPostalCode;
134-
$values['country_code'] = $countryCode;
135-
$values['best_geocoder_location_id'] = $geocoderDataId;
136-
137-
$sql = new Sql($this->adapter);
135+
} catch (NotFoundException $e) {
136+
// Ok insert
137+
$values = [];
138+
$values['name'] = $officialName;
139+
$values['postal_code'] = $keptPostalCode;
140+
$values['country_code'] = $countryCode;
141+
$values['best_geocoder_location_id'] = $geocoderDataId;
142+
143+
$sql = new Sql($this->adapter);
144+
$query = $sql
145+
->insert('places')
146+
->values($values);
147+
$stmt = $sql->prepareStatementForSqlObject($query);
148+
$stmt->execute();
149+
150+
$placeId = $this->adapter->getDriver()->getLastGeneratedValue();
151+
152+
// Store alternative names
153+
foreach ($placeNames as $locale => $name)
154+
{
155+
if (2 === strlen($locale)) {
156+
// Huho... I really need to find the right way to use prepared statements
157+
$query = $sql
158+
->insert('place_names')
159+
->values(
160+
[
161+
'place_id' => $placeId,
162+
'locale' => $locale,
163+
'name' => $name,
164+
]
165+
);
166+
$stmt = $sql->prepareStatementForSqlObject($query);
167+
$stmt->execute();
168+
}
169+
}
170+
171+
// Store all postal codes
172+
foreach ($postalCodes as $postalCode)
173+
{
174+
// Huho... I really need to find the right way to use prepared statements
138175
$query = $sql
139-
->insert('places')
140-
->values($values);
176+
->insert('place_postal_codes')
177+
->values(
178+
[
179+
'place_id' => $placeId,
180+
'postal_code' => $postalCode,
181+
]
182+
);
141183
$stmt = $sql->prepareStatementForSqlObject($query);
142184
$stmt->execute();
143-
144-
$placeId = $this->adapter->getDriver()->getLastGeneratedValue();
145-
} else {
146-
throw new \Exception('Failure', 0, $e);
147185
}
148186
}
149187

150-
// TODO Store alternative names
151-
// TODO Store all zipcodes
152-
153188
$sql = new Sql($this->adapter);
154189
$query = $sql
155190
->update('geocoder_locations')
@@ -177,14 +212,9 @@ public function getGeocoderLocationIdFromGeocodedPlaceStdClassAfterInsertIfNeede
177212
{
178213
try {
179214
$id = $this->getGeocoderLocationIdFromGeocodedPlaceStdClass($geocodedLocation);
180-
} catch (\Exception $e) {
181-
// TODO replace with a NotFoundException
182-
if ('Not found' === $e->getMessage()) {
183-
// Ok insert
184-
$id = $this->insertGeocoderLocationOfGeocodedPlaceStdClass($geocodedLocation);
185-
} else {
186-
throw new \Exception('Failure', 0, $e);
187-
}
215+
} catch (NotFoundException $e) {
216+
// Ok insert
217+
$id = $this->insertGeocoderLocationOfGeocodedPlaceStdClass($geocodedLocation);
188218
}
189219

190220
return $id;
@@ -230,6 +260,6 @@ public function getIdOfPlaceByNamePostalCodeCountryCodeEtc($officialName, $keptP
230260
return $row['place_id'];
231261
}
232262

233-
throw new \Exception('Not found');
263+
throw new NotFoundException('Place ['.$officialName.'] not found');
234264
}
235265
}

tests/LocationServiceTest.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
use PHPUnit\Framework\TestCase;
5+
6+
use PHPFacile\Geocoding\Db\Service\LocationService;
7+
use PHPFacile\Openstreetmap\Service\OpenstreetmapService;
8+
use Zend\Db\Adapter\Adapter;
9+
10+
use Exceptions\Data\NotFoundException;
11+
12+
final class LocationServiceTest extends TestCase
13+
{
14+
protected $locationService;
15+
protected $geocodedLocations = [];
16+
17+
protected function setUp()
18+
{
19+
$adapterCfg = [
20+
'driver' => 'pdo_sqlite',
21+
'database' => __DIR__.'/locations.tests.sqlite3'
22+
];
23+
24+
$adapter = new Adapter($adapterCfg);
25+
26+
$locationService = new LocationService($adapter);
27+
$openstreetmapService = new OpenstreetmapService();
28+
$locationService->setOpenstreetmapService($openstreetmapService);
29+
30+
$geocodedLocation = new \StdClass();
31+
$geocodedLocation->place = new \StdClass();
32+
$geocodedLocation->place->name = 'Paris';
33+
$geocodedLocation->place->country = new \StdClass();
34+
$geocodedLocation->place->country->code = 'FR';
35+
$geocodedLocation->place->geocoding = new \StdClass();
36+
$geocodedLocation->place->geocoding->geocodingDateTimeUTC = '2018-11-08 12:00:00';
37+
$geocodedLocation->place->geocoding->provider = 'nominatim';
38+
$geocodedLocation->place->geocoding->idProvider = '7444';
39+
$geocodedLocation->place->geocoding->coordinates = new \StdClass();
40+
$geocodedLocation->place->geocoding->coordinates->latitude = 45;
41+
$geocodedLocation->place->geocoding->coordinates->longitude = 0.5;
42+
$geocodedLocation->place->geocoding->country = new \StdClass();
43+
$geocodedLocation->place->geocoding->country->isoCode = 'FR';
44+
$geocodedLocation->place->geocoding->timezone = 'Europe/Paris';
45+
46+
$this->geocodedLocations[0] = $geocodedLocation;
47+
48+
$this->locationService = $locationService;
49+
}
50+
51+
/**
52+
* @expectedException Exceptions\Data\NotFoundException
53+
*/
54+
public function testNotFoundException()
55+
{
56+
$geocodedLocation = $this->geocodedLocations[0];
57+
$this->locationService->getGeocoderLocationIdFromGeocodedPlaceStdClass($geocodedLocation->place);
58+
}
59+
60+
public function testInsert()
61+
{
62+
$geocodedLocation = $this->geocodedLocations[0];
63+
$geocoderLocationId = $this->locationService->insertGeocoderLocationOfGeocodedPlaceStdClass($geocodedLocation->place);
64+
$this->assertEquals('1', $geocoderLocationId);
65+
66+
// TODO Check nb of place_names, postal_codes, etc.
67+
}
68+
69+
public function testGetOrInsert()
70+
{
71+
$geocodedLocation = $this->geocodedLocations[0];
72+
$geocoderLocationId = $this->locationService->getGeocoderLocationIdFromGeocodedPlaceStdClassAfterInsertIfNeeded($geocodedLocation->place);
73+
$this->assertEquals('1', $geocoderLocationId);
74+
75+
// TODO Check nb of place_names, postal_codes, etc.
76+
}
77+
}

tests/locations.sqlite3

15 KB
Binary file not shown.

0 commit comments

Comments
 (0)