Skip to content

Commit 9913525

Browse files
authored
Merge pull request #75 from dereuromark/address-element-closure
Add closure support for elements of address
2 parents d91c76b + cde278c commit 9913525

File tree

5 files changed

+136
-4
lines changed

5 files changed

+136
-4
lines changed

docs/Behavior/Geocoder.md

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ Possible config options are:
1515
- locale (for example DE)
1616
- region (for some providers
1717
- ssl (for some providers)
18-
- address: (array|string, optional) set to the field name that contains the string from where to generate the slug, or a set of field names to concatenate for generating the slug.
18+
- address: (array|string, optional) set to the field name that contains the string from where
19+
to generate the slug, or a set of field names to concatenate for generating the slug.
1920
- overwrite: lat/lng overwrite existing coordinates, defaults to true
2021
- update: what fields to update (key=>value array pairs)
2122
- on: beforeMarshal/afterMarshal/beforeSave (defaults to beforeSave) - Set to false if you only want to use the validation rules etc
@@ -29,10 +30,10 @@ Possible config options are:
2930
Note that it is usually better to set global configs in your `app.php` using the `Geocoder` key.
3031

3132
## Configure your own Geocoder
32-
By default it will use the GoogleMaps provider.
33+
By default, it will use the GoogleMaps provider.
3334

3435
Please see [geocoder-php/Geocoder](https://github.com/geocoder-php/Geocoder) library on what other providers you can use out of the box.
35-
You can chose from
36+
You can choose from
3637
- 12+ address-based Geocoder providers
3738
- 10+ IP-based Geocoder providers
3839

@@ -105,6 +106,39 @@ $options = ['coordinates' => $coordinates, 'distance' => 200];
105106

106107
$query = $this->Addresses->find('distance', ...$options);
107108
```
109+
When using the plugin's native `GeoCoordinate` value object, use
110+
```php
111+
$geoCoordinate = new GeoCoordinate(13.3, 19.2);
112+
$coordinates = $geoCoordinate->toGeocoderCoordinates();
113+
$options = ['coordinates' => $coordinates, 'distance' => 200];
114+
```
115+
116+
### Address elements as closure
117+
Sometimes, you need to have more logic for a specific address field.
118+
In this case you can use a closure to make dynamic lookups where needed.
119+
120+
Example: Cities and their Countries when saving a city (cities/add or cities/edit/ID).
121+
122+
```php
123+
$this->addBehavior('Geo.Geocoder', [
124+
'address' => ['street', 'postal_code', 'city', function(City $entity) {
125+
// If there is a matching relation
126+
if ($entity->country && $entity->country->id && $entity->country_id) {
127+
return $entity->country->name;
128+
}
129+
// If there is a virtual or tmp field
130+
if ($entity->get('country_name')) {
131+
return $entity->get('country_name');
132+
}
133+
// Do an actual DB lookup with the ID given in the form
134+
if ($entity->country_id) {
135+
$country = $this->Countries->get($entity->country_id);
136+
return $country->name;
137+
}
138+
139+
return null;
140+
}]]);
141+
```
108142

109143
## Batch geocoding
110144

src/Geocoder/GeoCoordinate.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Geo\Geocoder;
44

5+
use Geocoder\Model\Coordinates;
56
use InvalidArgumentException;
67
use JsonSerializable;
78
use Stringable;
@@ -59,6 +60,22 @@ public static function fromArray(array $data): static {
5960
return new static($lat, $lng);
6061
}
6162

63+
/**
64+
* @param \Geocoder\Model\Coordinates $coordinates
65+
*
66+
* @return static
67+
*/
68+
public function fromGeocoderCoordinates(Coordinates $coordinates): static {
69+
return new static($coordinates->getLatitude(), $coordinates->getLongitude());
70+
}
71+
72+
/**
73+
* @return \Geocoder\Model\Coordinates
74+
*/
75+
public function toGeocoderCoordinates(): Coordinates {
76+
return new Coordinates($this->latitude, $this->longitude);
77+
}
78+
6279
/**
6380
* @param string $coordinate
6481
*

src/Model/Behavior/GeocoderBehavior.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,14 +232,28 @@ public function geocode(EntityInterface $entity) {
232232
$addressData = [];
233233
$dirty = false;
234234
foreach ($addressFields as $field) {
235-
$fieldData = $entity->get($field);
235+
$isClosure = $field instanceof \Closure;
236+
if ($isClosure) {
237+
$fieldData = $field($entity);
238+
} else {
239+
$fieldData = $entity->get($field);
240+
}
236241
if ($fieldData) {
237242
$addressData[] = $fieldData;
238243
}
244+
245+
if ($isClosure) {
246+
if ($fieldData) {
247+
$dirty = true;
248+
}
249+
250+
continue;
251+
}
239252
if ($entity->isDirty($field)) {
240253
$dirty = true;
241254
}
242255
}
256+
243257
if (!$dirty) {
244258
if (
245259
$this->_config['allowEmpty'] ||

tests/TestCase/Model/Behavior/GeocoderBehaviorTest.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,73 @@ public function testBasic() {
253253
$entity = $this->_getEntity($data);
254254
$res = $this->Addresses->save($entity);
255255

256+
$this->assertTrue(!empty($res));
257+
$this->assertSame('Bibersfeld', $res->geocoder_result['address_data']);
258+
}
259+
/**
260+
* @return void
261+
*/
262+
public function testAddressClosure() {
263+
$this->Addresses->removeBehavior('Geocoder');
264+
$this->Addresses->addBehavior('Geocoder', [
265+
'address' => [
266+
'street',
267+
'zip',
268+
'city',
269+
function($entity) {
270+
if ($entity->country && $entity->country->id && $entity->country_id) {
271+
return $entity->country->name;
272+
}
273+
if ($entity->get('country_name')) {
274+
return $entity->get('country_name');
275+
}
276+
277+
if ($entity->country_id) {
278+
// One can use this, but we fake it for simplicity of the test
279+
//$country = TableRegistry::getTableLocator('Countries')->get($entity->country_id);
280+
return 'Deutschland';
281+
}
282+
283+
return null;
284+
},
285+
],
286+
]);
287+
288+
$data = [
289+
'street' => 'Krebenweg 22',
290+
'zip' => '74523',
291+
'city' => 'Bibersfeld',
292+
'country_id' => 1,
293+
];
294+
$entity = $this->_getEntity($data);
295+
$res = $this->Addresses->save($entity);
296+
297+
$this->assertTrue(!empty($res['lat']) && !empty($res['lng']));
298+
$this->assertTrue(round($res['lat']) === 49.0 && round($res['lng']) === 10.0);
299+
300+
$this->assertSame('Deutschland', $res->geocoder_result['country']);
301+
$this->assertSame('DE', $res->geocoder_result['countryCode']);
302+
$this->assertSame('Krebenweg 22 74523 Bibersfeld Deutschland', $res->geocoder_result['address_data']);
303+
304+
// inconclusive
305+
$data = [
306+
//'street' => 'Leopoldstraße',
307+
'city' => 'München',
308+
];
309+
$entity = $this->_getEntity($data);
310+
$res = $this->Addresses->save($entity);
311+
312+
$this->assertTrue(!empty($res['lat']) && !empty($res['lng']));
313+
//FIXME
314+
$this->assertStringContainsString('München', $res['formatted_address']);
315+
//$this->assertEquals('München, Deutschland', $res['formatted_address']);
316+
317+
$data = [
318+
'city' => 'Bibersfeld',
319+
];
320+
$entity = $this->_getEntity($data);
321+
$res = $this->Addresses->save($entity);
322+
256323
$this->assertTrue(!empty($res));
257324
}
258325

Binary file not shown.

0 commit comments

Comments
 (0)