From 677b6839a033255ef08667ef7ac682dce4b8825b Mon Sep 17 00:00:00 2001 From: mscherer Date: Sun, 25 May 2025 12:10:50 +0200 Subject: [PATCH 1/9] Explore spatial support. --- docs/Model/GeocodedAddresses.md | 2 + src/Model/Behavior/GeocoderBehavior.php | 59 ++++++++ tests/Fixture/SpatialAddressesFixture.php | 38 +++++ .../src/Model/Table/SpatialAddressesTable.php | 130 ++++++++++++++++++ .../Model/Table/SpatialAddressesTableTest.php | 129 +++++++++++++++++ 5 files changed, 358 insertions(+) create mode 100644 tests/Fixture/SpatialAddressesFixture.php create mode 100644 tests/TestApp/src/Model/Table/SpatialAddressesTable.php create mode 100644 tests/TestCase/Model/Table/SpatialAddressesTableTest.php diff --git a/docs/Model/GeocodedAddresses.md b/docs/Model/GeocodedAddresses.md index 57016f7..8a31350 100644 --- a/docs/Model/GeocodedAddresses.md +++ b/docs/Model/GeocodedAddresses.md @@ -16,6 +16,8 @@ You need to enable `Geo.GeocodedAddresses` Table. Just make sure you added the t bin/cake migrations migrate -p Geo ``` +Note: Configure `Geo.spatial` enables spatial functionality for this lookup table. + Then instead of using the Geocoder class directly, you go through this Table: ```php $GeocodedAddresses = TableRegistry::getTableLocator()->get('Geo.GeocodedAddresses'); diff --git a/src/Model/Behavior/GeocoderBehavior.php b/src/Model/Behavior/GeocoderBehavior.php index 5c45d03..0ee98ff 100644 --- a/src/Model/Behavior/GeocoderBehavior.php +++ b/src/Model/Behavior/GeocoderBehavior.php @@ -76,6 +76,7 @@ class GeocoderBehavior extends Behavior { 'unit' => Calculator::UNIT_KM, 'implementedFinders' => [ 'distance' => 'findDistance', + 'spatial' => 'findSpatial', ], 'validationError' => null, 'cache' => false, // Enable only if you got a GeocodedAddresses table running @@ -401,6 +402,60 @@ public function findDistance(SelectQuery $query, ?float $lat = null, ?float $lng return $query; } + /** + * @param \Cake\ORM\Query\SelectQuery $query + * @param float|null $lat + * @param float|null $lng + * @param \Geocoder\Model\Coordinates|null $coordinates + * @param int|null $distance + * @param string|null $tableName + * @param bool $sort + * @return \Cake\ORM\Query\SelectQuery + */ + public function findSpatial(SelectQuery $query, ?float $lat = null, ?float $lng = null, ?Coordinates $coordinates = null, ?int $distance = null, ?string $tableName = null, bool $sort = true): SelectQuery { + $options = [ + 'tableName' => $tableName, + 'sort' => $sort, + 'lat' => $lat, + 'lng' => $lng, + 'distance' => $distance, + 'coordinates' => $coordinates, + ]; + $options = $this->assertCoordinates($options); + + if ($query->isAutoFieldsEnabled() === null) { + $query->enableAutoFields(true); + } + + $lat = $options[static::OPTION_LAT]; + $lng = $options[static::OPTION_LNG]; + + // Add distance calculation as a virtual field + $query->select([ + 'distance' => new QueryExpression( + "ST_Distance_Sphere(coordinates, ST_GeomFromText('POINT($lng $lat)')) / 1000", + ), + ]); + + // Filter by max distance if limit is provided + if (isset($options['distance'])) { + $distance = (float)$options['distance']; + $query->where(function (QueryExpression $exp) use ($lat, $lng, $distance) { + return $exp->lte( + new QueryExpression("ST_Distance_Sphere(coordinates, ST_GeomFromText('POINT($lng $lat)')) / 1000"), + $distance, + ); + }); + } + + if ($options['sort']) { + $sort = $options['sort'] === true ? 'ASC' : $options['sort']; + $query->orderBy(['distance' => $sort]); + } + + return $query; + } + /** * Forms a sql snippet for distance calculation on db level using two lat/lng points. * @@ -653,6 +708,10 @@ protected function assertCoordinates(array $options): array { throw new InvalidArgumentException($error); } + if ($options[static::OPTION_LAT] < -90 || $options[static::OPTION_LAT] > 90 || $options[static::OPTION_LNG] < -180 || $options[static::OPTION_LNG] > 180) { + throw new InvalidArgumentException('Invalid latitude or longitude in (' . $options[static::OPTION_LAT] . '/' . $options[static::OPTION_LNG] . ').'); + } + return $options; } diff --git a/tests/Fixture/SpatialAddressesFixture.php b/tests/Fixture/SpatialAddressesFixture.php new file mode 100644 index 0000000..b1bb6bc --- /dev/null +++ b/tests/Fixture/SpatialAddressesFixture.php @@ -0,0 +1,38 @@ + ['type' => 'integer'], + 'address' => ['type' => 'string', 'null' => false, 'default' => '', 'length' => 190, 'comment' => 'street address and street numbe'], + 'lat' => ['type' => 'float', 'null' => false, 'default' => null, 'comment' => 'maps.google.de latitude'], + 'lng' => ['type' => 'float', 'null' => false, 'default' => null, 'comment' => 'maps.google.de longitude'], + 'coordinates' => ['type' => 'point', 'null' => false], + 'created' => ['type' => 'datetime', 'null' => true, 'default' => null], + 'modified' => ['type' => 'datetime', 'null' => true, 'default' => null], + '_constraints' => [ + 'primary' => ['type' => 'primary', 'columns' => ['id']], + ], + '_indexes' => [ + //'coordinates_spatial' => ['type' => 'spatial', 'columns' => ['coordinates'], 'length' => []], + ], + ]; + + /** + * Records + * + * @var array + */ + public array $records = [ + ]; + +} diff --git a/tests/TestApp/src/Model/Table/SpatialAddressesTable.php b/tests/TestApp/src/Model/Table/SpatialAddressesTable.php new file mode 100644 index 0000000..d15d4d3 --- /dev/null +++ b/tests/TestApp/src/Model/Table/SpatialAddressesTable.php @@ -0,0 +1,130 @@ + newEntities(array $data, array $options = []) + * @method \Geo\Model\Entity\GeocodedAddress|false save(\Cake\Datasource\EntityInterface $entity, array $options = []) + * @method \Geo\Model\Entity\GeocodedAddress patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = []) + * @method array<\Geo\Model\Entity\GeocodedAddress> patchEntities(iterable $entities, array $data, array $options = []) + * @method \Geo\Model\Entity\GeocodedAddress findOrCreate($search, ?callable $callback = null, array $options = []) + * @method \Geo\Model\Entity\GeocodedAddress saveOrFail(\Cake\Datasource\EntityInterface $entity, array $options = []) + * @method \Cake\Datasource\ResultSetInterface<\Geo\Model\Entity\GeocodedAddress>|false saveMany(iterable $entities, array $options = []) + * @mixin \Cake\ORM\Behavior\TimestampBehavior + * @method \Geo\Model\Entity\GeocodedAddress newEmptyEntity() + * @method \Cake\Datasource\ResultSetInterface<\Geo\Model\Entity\GeocodedAddress> saveManyOrFail(iterable $entities, array $options = []) + * @method \Cake\Datasource\ResultSetInterface<\Geo\Model\Entity\GeocodedAddress>|false deleteMany(iterable $entities, array $options = []) + * @method \Cake\Datasource\ResultSetInterface<\Geo\Model\Entity\GeocodedAddress> deleteManyOrFail(iterable $entities, array $options = []) + */ +class SpatialAddressesTable extends Table { + + /** + * @var \Geo\Geocoder\Geocoder + */ + protected $_Geocoder; + + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void { + parent::initialize($config); + + $this->setTable('spatial_addresses'); + $this->setDisplayField('address'); + $this->setPrimaryKey('id'); + + //$this->getSchema()->setColumnType('data', 'object'); + + $this->addBehavior('Timestamp'); + } + + /** + * Add formatter aka afterFind parsing geospatial values + * + * @param \Cake\Event\EventInterface $event + * @param \Cake\ORM\Query\SelectQuery $query + * @param \ArrayObject $options + * @return void + */ + public function beforeFind(EventInterface $event, SelectQuery $query, ArrayObject $options): void { + $columns = ['coordinates' => 'point']; + + // Go through each result and unpack() the binary data + $query->formatResults(function($results) use($columns) { + return $results->map(function($entity) use($columns) { + foreach ($columns as $column => $type) { + if (!isset($entity->{$column})) { + continue; + } + // [TypeError] unpack(): Argument #2 ($string) must be of type string + if (!is_string($entity->{$column})) { + continue; + } + switch ($type) { + // TODO support other types, not only POINT + case 'point': + $entity->{$column} = unpack('x/x/x/x/corder/Ltype/dx/dy', $entity->{$column}); + + break; + } + } + + return $entity; + }); + }); + } + + /** + * Once entity is marshalled, prepare geospatial values to be saved into database + * + * @param \Cake\Event\EventInterface $event + * @param \Cake\Datasource\EntityInterface $entity + * @param \ArrayObject $data + * @param \ArrayObject $options + * @return void + */ + public function afterMarshal(EventInterface $event, EntityInterface $entity, ArrayObject $data, ArrayObject $options): void { + $columns = ['coordinates' => 'point']; + + foreach ($columns as $column => $type) { + // Skip if the column is not present in $data + if (!isset($data[$column])) { + if (!empty($data['lat']) && !empty($data['lng'])) { + $data[$column] = [$data['lng'], $data['lat']]; // lng, lat order is important! + } else { + continue; + } + } + + // We expect an array like [12, 34] otherwise skip + if (!is_array($data[$column])) { + continue; + } + switch ($type) { + // TODO support other types, not only POINT + case 'point': + $value = sprintf('\'%s(%s)\'', strtoupper($type), implode(' ', $data[$column])); + + break; + } + // Set $value on $entity using ST_GeomFromText() + $entity->{$column} = $this->query()->func()->ST_GeomFromText([ + $value => 'literal', + ]); + } + } + +} diff --git a/tests/TestCase/Model/Table/SpatialAddressesTableTest.php b/tests/TestCase/Model/Table/SpatialAddressesTableTest.php new file mode 100644 index 0000000..9010fb4 --- /dev/null +++ b/tests/TestCase/Model/Table/SpatialAddressesTableTest.php @@ -0,0 +1,129 @@ +getDriver(); + $this->skipIf(!($driver instanceof Mysql || $driver instanceof Postgres), 'The virtualFields test is only compatible with Mysql/Postgres.'); + + $config = TableRegistry::getTableLocator()->exists('SpatialAddresses') ? [] : ['className' => 'TestApp\Model\Table\SpatialAddressesTable']; + $this->SpatialAddresses = TableRegistry::getTableLocator()->get('SpatialAddresses', $config); + + $entries = [ + [ + 'address' => 'Langstrasse 10, 101010 München', + 'lat' => '48.150589', + 'lng' => '11.472230', + 'created' => '2011-04-21 16:50:05', + 'modified' => '2011-10-07 17:42:27', + ], + [ + 'address' => 'Leckermannstrasse 10, 10101 München', + 'lat' => '48.133942', + 'lng' => '11.490000', + 'created' => '2011-04-21 16:51:01', + 'modified' => '2011-10-07 17:44:02', + ], + [ + 'address' => 'Krebenweg 11, 12523 Schwäbisch Hall', + 'lat' => '19.081490', + 'lng' => '19.690800', + 'created' => '2011-11-17 13:47:36', + 'modified' => '2011-11-17 13:47:36', + ], + [ + 'address' => 'hjsf', + 'lat' => '52.52', + 'lng' => '13.40', + 'created' => '2011-11-17 14:34:14', + 'modified' => '2011-11-17 14:49:21', + ], + ]; + foreach ($entries as $entry) { + $entity = $this->SpatialAddresses->newEntity($entry); + $this->SpatialAddresses->saveOrFail($entity); + } + + //debug($this->SpatialAddresses->getSchema()->getIndex('coordinates_spatial')); + } + + /** + * @return void + */ + public function tearDown(): void { + unset($this->SpatialAddresses); + + parent::tearDown(); + } + + /** + * @return void + */ + public function testSave() { + $address = $this->SpatialAddresses->newEntity([ + 'address' => 'Berlin', + 'lat' => 12, + 'lng' => 11, + ]); + $address = $this->SpatialAddresses->save($address); + $this->assertNotEmpty($address); + + $address = $this->SpatialAddresses->get($address->id); + $this->assertNotEmpty($address->coordinates); + $this->assertEquals(11, $address->coordinates['x']); + $this->assertEquals(12, $address->coordinates['y']); + } + + /** + * @return void + */ + public function testFindSpatial() { + $this->SpatialAddresses->addBehavior('Geo.Geocoder'); + $addresses = $this->SpatialAddresses->find('spatial', ...[ + 'lat' => 48.110589, + 'lng' => 11.422230, + 'distance' => 100, + ]) + ->all() + ->toArray(); + + $distances = Hash::extract($addresses, '{n}.distance'); + $expeected = [ + 5.66, + 5.79, + ]; + foreach ($distances as $key => $distance) { + $this->assertSame($expeected[$key], round($distance, 2)); + } + } + +} From 3146f9eebfa80cc0b7a42e61f04b435a1052c883 Mon Sep 17 00:00:00 2001 From: mscherer Date: Sun, 25 May 2025 12:49:06 +0200 Subject: [PATCH 2/9] Explore spatial support. --- docs/Behavior/Geocoder.md | 15 +++++++++++ docs/Model/GeocodedAddresses.md | 4 +-- .../Model/Table/SpatialAddressesTableTest.php | 27 +++++++++++++++---- tests/bootstrap.php | 11 ++++++++ 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/docs/Behavior/Geocoder.md b/docs/Behavior/Geocoder.md index e8e3b01..c568fad 100644 --- a/docs/Behavior/Geocoder.md +++ b/docs/Behavior/Geocoder.md @@ -166,3 +166,18 @@ You can test the geocoding and also remove cache data where needed. ## Providers Full list of existing providers [here](https://github.com/geocoder-php/Geocoder#providers). + +## Spatial +You can also use the "spatial" finder using coordinates as POINT instead of lat/lng. +```php +$query = $this->Addresses->find('spatial', [ + 'lat' => 13.3, + 'lng' => 19.2, + 'distance' => 100, +]); +``` +Note: This only works with PostGIS and MySQL 5.7+ (and MariaDB 10.4+) databases, as they support spatial data types. + +There can be a performance improvement when using spatial indexes, so you might want to consider using this for larger datasets. +In reality I didnt get the index to work, though. Only managed to limit down the looked up entries from full table scan to range, which +sped it up, too. diff --git a/docs/Model/GeocodedAddresses.md b/docs/Model/GeocodedAddresses.md index 8a31350..2f4e004 100644 --- a/docs/Model/GeocodedAddresses.md +++ b/docs/Model/GeocodedAddresses.md @@ -16,8 +16,6 @@ You need to enable `Geo.GeocodedAddresses` Table. Just make sure you added the t bin/cake migrations migrate -p Geo ``` -Note: Configure `Geo.spatial` enables spatial functionality for this lookup table. - Then instead of using the Geocoder class directly, you go through this Table: ```php $GeocodedAddresses = TableRegistry::getTableLocator()->get('Geo.GeocodedAddresses'); @@ -27,7 +25,7 @@ if ($address && $address->lat && $address->lng) { } ``` -Don't forget to add the Type mapping of `Geo\Database\Type\ObjectType` in your bootstrap.php. +Remember to add the Type mapping of `Geo\Database\Type\ObjectType` in your bootstrap.php. ```php TypeFactory::map('object', 'Geo\Database\Type\ObjectType'); ``` diff --git a/tests/TestCase/Model/Table/SpatialAddressesTableTest.php b/tests/TestCase/Model/Table/SpatialAddressesTableTest.php index 9010fb4..5de4b81 100644 --- a/tests/TestCase/Model/Table/SpatialAddressesTableTest.php +++ b/tests/TestCase/Model/Table/SpatialAddressesTableTest.php @@ -33,7 +33,7 @@ public function setUp(): void { $db = ConnectionManager::get('test'); $driver = $db->getDriver(); - $this->skipIf(!($driver instanceof Mysql || $driver instanceof Postgres), 'The virtualFields test is only compatible with Mysql/Postgres.'); + $this->skipIf(!($driver instanceof Mysql), 'The functionality/test is only compatible with Mysql right now.'); $config = TableRegistry::getTableLocator()->exists('SpatialAddresses') ? [] : ['className' => 'TestApp\Model\Table\SpatialAddressesTable']; $this->SpatialAddresses = TableRegistry::getTableLocator()->get('SpatialAddresses', $config); @@ -72,8 +72,6 @@ public function setUp(): void { $entity = $this->SpatialAddresses->newEntity($entry); $this->SpatialAddresses->saveOrFail($entity); } - - //debug($this->SpatialAddresses->getSchema()->getIndex('coordinates_spatial')); } /** @@ -107,6 +105,8 @@ public function testSave() { * @return void */ public function testFindSpatial() { + $this->assertNotEmpty($this->SpatialAddresses->getSchema()->getIndex('coordinates_spatial')); + $this->SpatialAddresses->addBehavior('Geo.Geocoder'); $addresses = $this->SpatialAddresses->find('spatial', ...[ 'lat' => 48.110589, @@ -117,13 +117,30 @@ public function testFindSpatial() { ->toArray(); $distances = Hash::extract($addresses, '{n}.distance'); - $expeected = [ + $expected = [ 5.66, 5.79, ]; foreach ($distances as $key => $distance) { - $this->assertSame($expeected[$key], round($distance, 2)); + $this->assertSame($expected[$key], round($distance, 2)); } } + /** + * @return void + */ + public function testFindSpatialExplain() { + $this->assertNotEmpty($this->SpatialAddresses->getSchema()->getIndex('coordinates_spatial')); + + $this->SpatialAddresses->addBehavior('Geo.Geocoder'); + + $sql = <<SpatialAddresses->getConnection()->execute($sql)->fetchAssoc(); + + // Should be type range or index, not ALL + debug($result); + } + } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 6dd7db3..258d233 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -103,6 +103,17 @@ class_alias(View::class, 'App\View\AppView'); 'apiKey' => env('API_KEY'), // local, set through `export API_KEY=".."` in CLI ]); +/** + * @var \Cake\Database\Connection $db + */ +$db = ConnectionManager::get('test'); +if ($db->getDriver() instanceof \Cake\Database\Driver\Postgres) { + //$db->execute('CREATE EXTENSION postgis;'); +} +if ($db->getDriver() instanceof \Cake\Database\Driver\Mysql) { + $db->execute('ALTER TABLE spatial_addresses ADD SPATIAL INDEX coordinates_spatial(coordinates);'); +} + if (env('FIXTURE_SCHEMA_METADATA')) { $loader = new SchemaLoader(); $loader->loadInternalFile(env('FIXTURE_SCHEMA_METADATA')); From 551d373a7084676eec7a3768797afbf06efd86c0 Mon Sep 17 00:00:00 2001 From: mscherer Date: Sun, 25 May 2025 12:51:11 +0200 Subject: [PATCH 3/9] Explore spatial support. --- tests/TestCase/Model/Table/SpatialAddressesTableTest.php | 2 -- tests/bootstrap.php | 7 ++++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/TestCase/Model/Table/SpatialAddressesTableTest.php b/tests/TestCase/Model/Table/SpatialAddressesTableTest.php index 5de4b81..4b33cc8 100644 --- a/tests/TestCase/Model/Table/SpatialAddressesTableTest.php +++ b/tests/TestCase/Model/Table/SpatialAddressesTableTest.php @@ -105,8 +105,6 @@ public function testSave() { * @return void */ public function testFindSpatial() { - $this->assertNotEmpty($this->SpatialAddresses->getSchema()->getIndex('coordinates_spatial')); - $this->SpatialAddresses->addBehavior('Geo.Geocoder'); $addresses = $this->SpatialAddresses->find('spatial', ...[ 'lat' => 48.110589, diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 258d233..4d402f1 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -110,11 +110,12 @@ class_alias(View::class, 'App\View\AppView'); if ($db->getDriver() instanceof \Cake\Database\Driver\Postgres) { //$db->execute('CREATE EXTENSION postgis;'); } -if ($db->getDriver() instanceof \Cake\Database\Driver\Mysql) { - $db->execute('ALTER TABLE spatial_addresses ADD SPATIAL INDEX coordinates_spatial(coordinates);'); -} if (env('FIXTURE_SCHEMA_METADATA')) { $loader = new SchemaLoader(); $loader->loadInternalFile(env('FIXTURE_SCHEMA_METADATA')); } + +if ($db->getDriver() instanceof \Cake\Database\Driver\Mysql) { + $db->execute('ALTER TABLE spatial_addresses ADD SPATIAL INDEX coordinates_spatial(coordinates);'); +} From 0d8ae5e856e7fbace0f22cac9ddc5c55709ce757 Mon Sep 17 00:00:00 2001 From: mscherer Date: Sun, 25 May 2025 12:54:29 +0200 Subject: [PATCH 4/9] Explore spatial support. --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a278efe..9f17ef9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,11 @@ jobs: run: | sudo service mysql start mysql -h 127.0.0.1 -u root -proot -e 'CREATE DATABASE cakephp;' + - name: Setup Geography for PostgreSQL + if: matrix.db-type == 'pgsql' + run: | + sudo apt-get install postgis + - name: Setup PHP uses: shivammathur/setup-php@v2 with: From 6f7b788447d987cc7dec8be14ac23f4200f2463f Mon Sep 17 00:00:00 2001 From: mscherer Date: Sun, 25 May 2025 12:55:57 +0200 Subject: [PATCH 5/9] Explore spatial support. --- tests/TestCase/Model/Table/SpatialAddressesTableTest.php | 1 - tests/bootstrap.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/TestCase/Model/Table/SpatialAddressesTableTest.php b/tests/TestCase/Model/Table/SpatialAddressesTableTest.php index 4b33cc8..91a807d 100644 --- a/tests/TestCase/Model/Table/SpatialAddressesTableTest.php +++ b/tests/TestCase/Model/Table/SpatialAddressesTableTest.php @@ -3,7 +3,6 @@ namespace Geo\Test\TestCase\Model\Table; use Cake\Database\Driver\Mysql; -use Cake\Database\Driver\Postgres; use Cake\Datasource\ConnectionManager; use Cake\ORM\TableRegistry; use Cake\TestSuite\TestCase; diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 4d402f1..eb9fb02 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -108,7 +108,7 @@ class_alias(View::class, 'App\View\AppView'); */ $db = ConnectionManager::get('test'); if ($db->getDriver() instanceof \Cake\Database\Driver\Postgres) { - //$db->execute('CREATE EXTENSION postgis;'); + $db->execute('CREATE EXTENSION postgis;'); } if (env('FIXTURE_SCHEMA_METADATA')) { From 91a51eaa3634045a921046dc79fa7b8adb3904ee Mon Sep 17 00:00:00 2001 From: mscherer Date: Sun, 25 May 2025 12:58:18 +0200 Subject: [PATCH 6/9] Explore spatial support. --- tests/bootstrap.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index eb9fb02..463a14f 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -108,7 +108,9 @@ class_alias(View::class, 'App\View\AppView'); */ $db = ConnectionManager::get('test'); if ($db->getDriver() instanceof \Cake\Database\Driver\Postgres) { - $db->execute('CREATE EXTENSION postgis;'); + $db->execute('CREATE EXTENSION postgis;')->fetchAll(); + + debug($db->execute('SELECT postgis_full_version();')->fetchAssoc()); } if (env('FIXTURE_SCHEMA_METADATA')) { From 674e959d64593f8ade18e685807b85993dece0a0 Mon Sep 17 00:00:00 2001 From: mscherer Date: Sun, 25 May 2025 13:05:42 +0200 Subject: [PATCH 7/9] Explore spatial support. --- tests/bootstrap.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 463a14f..dd0a175 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -108,9 +108,8 @@ class_alias(View::class, 'App\View\AppView'); */ $db = ConnectionManager::get('test'); if ($db->getDriver() instanceof \Cake\Database\Driver\Postgres) { - $db->execute('CREATE EXTENSION postgis;')->fetchAll(); - - debug($db->execute('SELECT postgis_full_version();')->fetchAssoc()); + //$db->execute('CREATE EXTENSION postgis;')->fetchAll(); + //debug($db->execute('SELECT postgis_full_version();')->fetchAssoc()); } if (env('FIXTURE_SCHEMA_METADATA')) { From d73b2deef6c6d6916a03cb354f9e1133f60e3dfe Mon Sep 17 00:00:00 2001 From: mscherer Date: Sun, 25 May 2025 13:09:27 +0200 Subject: [PATCH 8/9] Explore spatial support. --- tests/TestCase/Model/Table/SpatialAddressesTableTest.php | 4 ++-- tests/schema.php | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/TestCase/Model/Table/SpatialAddressesTableTest.php b/tests/TestCase/Model/Table/SpatialAddressesTableTest.php index 91a807d..f18a022 100644 --- a/tests/TestCase/Model/Table/SpatialAddressesTableTest.php +++ b/tests/TestCase/Model/Table/SpatialAddressesTableTest.php @@ -28,12 +28,12 @@ class SpatialAddressesTableTest extends TestCase { * @return void */ public function setUp(): void { - parent::setUp(); - $db = ConnectionManager::get('test'); $driver = $db->getDriver(); $this->skipIf(!($driver instanceof Mysql), 'The functionality/test is only compatible with Mysql right now.'); + parent::setUp(); + $config = TableRegistry::getTableLocator()->exists('SpatialAddresses') ? [] : ['className' => 'TestApp\Model\Table\SpatialAddressesTable']; $this->SpatialAddresses = TableRegistry::getTableLocator()->get('SpatialAddresses', $config); diff --git a/tests/schema.php b/tests/schema.php index ec9d4f8..0416cdc 100644 --- a/tests/schema.php +++ b/tests/schema.php @@ -33,6 +33,10 @@ 'indexes' => $indexes, ]; $tables[$tableName] = $table; + + if (str_contains(getenv('DB_URL'), 'postgres')) { + unset($tables['spatial_addresses']); + } } return $tables; From d6fc4451e27299443e4c3af76eb6a8a67308e28f Mon Sep 17 00:00:00 2001 From: mscherer Date: Sun, 25 May 2025 13:17:07 +0200 Subject: [PATCH 9/9] Explore spatial support. --- .../Admin/GeocodedAddressesController.php | 2 +- templates/Admin/GeocodedAddresses/index.php | 7 +++-- .../src/Model/Table/SpatialAddressesTable.php | 30 +++++++++---------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/Controller/Admin/GeocodedAddressesController.php b/src/Controller/Admin/GeocodedAddressesController.php index d5f3bee..fdc4b83 100644 --- a/src/Controller/Admin/GeocodedAddressesController.php +++ b/src/Controller/Admin/GeocodedAddressesController.php @@ -9,7 +9,7 @@ * * @property \Geo\Model\Table\GeocodedAddressesTable $GeocodedAddresses * - * @method \Cake\Datasource\ResultSetInterface<\Geo\Model\Entity\GeocodedAddress> paginate($object = null, array $settings = []) + * @method \Cake\Datasource\ResultSetInterface<\Geo\Model\Entity\GeocodedAddress> paginate(\Cake\Datasource\RepositoryInterface|\Cake\Datasource\QueryInterface|string|null $object = null, array $settings = []) */ class GeocodedAddressesController extends AppController { diff --git a/templates/Admin/GeocodedAddresses/index.php b/templates/Admin/GeocodedAddresses/index.php index 61e38bd..23cdfbc 100644 --- a/templates/Admin/GeocodedAddresses/index.php +++ b/templates/Admin/GeocodedAddresses/index.php @@ -1,10 +1,13 @@ $geocodedAddresses */ +use Cake\Core\Plugin; + +?> -use Cake\Core\Plugin; ?>