Browse Source

Fix geospatial types not being defined in TypeFactory (#17991)

- Fix geospatial types not being defined in TypeFactory
- Add tests for schema reflection of geospatial types in MySQL.

Fixes #17988
Mark Story 1 year ago
parent
commit
6afa6ec7d9

+ 4 - 0
src/Database/TypeFactory.php

@@ -53,6 +53,10 @@ class TypeFactory
         'timestampfractional' => Type\DateTimeFractionalType::class,
         'timestamptimezone' => Type\DateTimeTimezoneType::class,
         'uuid' => Type\UuidType::class,
+        'linestring' => Type\StringType::class,
+        'geometry' => Type\StringType::class,
+        'point' => Type\StringType::class,
+        'polygon' => Type\StringType::class,
     ];
 
     /**

+ 5 - 0
src/TestSuite/ConnectionHelper.php

@@ -93,6 +93,11 @@ class ConnectionHelper
         $collection = $connection->getSchemaCollection();
         $allTables = $collection->listTablesWithoutViews();
 
+        // Skip special tables.
+        // spatial_ref_sys - postgis and it is undroppable.
+        $skip = ['spatial_ref_sys'];
+        $allTables = array_diff($allTables, $skip);
+
         $tables = $tables !== null ? array_intersect($tables, $allTables) : $allTables;
         /** @var array<\Cake\Database\Schema\TableSchema> $schemas Specify type for psalm */
         $schemas = array_map(fn ($table) => $collection->describe($table), $tables);

+ 83 - 0
tests/TestCase/Database/Schema/MysqlSchemaTest.php

@@ -466,6 +466,89 @@ SQL;
     }
 
     /**
+     * Test that schema reflection works for geosptial columns.
+     *
+     * We currently cannot reflect the postgis types from postgres.
+     *
+     * @return void
+     */
+    public function testDescribeTableGeometry(): void
+    {
+        $this->_needsConnection();
+        $connection = ConnectionManager::get('test');
+        $driver = $connection->getDriver();
+
+        $hasGeometry = $driver->isMariaDb() || version_compare($driver->version(), '8.0.30', '>=');
+        $this->skipIf(!$hasGeometry, 'This test requires geometry type support');
+
+        $table = <<<SQL
+CREATE TABLE schema_geometry (
+    id INTEGER,
+    geo_line LINESTRING,
+    geo_geometry GEOMETRY,
+    geo_point POINT,
+    geo_polygon POLYGON
+)
+SQL;
+        $connection->execute($table);
+        $schema = new SchemaCollection($connection);
+        $result = $schema->describe('schema_geometry');
+        $connection->execute('DROP TABLE schema_geometry');
+
+        $expected = [
+            'id' => [
+                'type' => 'integer',
+                'null' => true,
+                'default' => null,
+                'length' => null,
+                'precision' => null,
+                'unsigned' => false,
+                'comment' => '',
+                'autoIncrement' => null,
+            ],
+            'geo_line' => [
+                'type' => 'linestring',
+                'null' => true,
+                'default' => null,
+                'precision' => null,
+                'length' => null,
+                'comment' => '',
+                'srid' => null,
+            ],
+            'geo_geometry' => [
+                'type' => 'geometry',
+                'null' => true,
+                'default' => null,
+                'precision' => null,
+                'length' => null,
+                'comment' => '',
+                'srid' => null,
+            ],
+            'geo_point' => [
+                'type' => 'point',
+                'null' => true,
+                'default' => null,
+                'precision' => null,
+                'length' => null,
+                'comment' => '',
+                'srid' => null,
+            ],
+            'geo_polygon' => [
+                'type' => 'polygon',
+                'null' => true,
+                'default' => null,
+                'precision' => null,
+                'length' => null,
+                'comment' => '',
+                'srid' => null,
+            ],
+        ];
+        foreach ($expected as $field => $definition) {
+            $this->assertEquals($definition, $result->getColumn($field), "Mismatch in {$field} column");
+        }
+    }
+
+    /**
      * Test describing a table with indexes in MySQL
      */
     public function testDescribeTableIndexes(): void

+ 24 - 0
tests/TestCase/ORM/MarshallerTest.php

@@ -225,6 +225,30 @@ class MarshallerTest extends TestCase
         $this->assertEquals($data['Articles'], $result->Articles);
     }
 
+    public function testOneWithGeospatialFields(): void
+    {
+        $articles = $this->getTableLocator()->get('Articles');
+        $articles->getSchema()
+            ->addColumn('geo_line', ['type' => 'linestring'])
+            ->addColumn('geo_geometry', ['type' => 'geometry'])
+            ->addColumn('geo_point', ['type' => 'point'])
+            ->addColumn('geo_polygon', ['type' => 'polygon']);
+
+        $data = [
+            'geo_line' => 'LINESTRING(0 0,1 1)',
+            'geo_geometry' => 'GEOMETRY(15, 25)',
+            'geo_point' => 'POINT(10, 15)',
+            'geo_polygon' => 'POLYGON(0 0,6 6,6 12,12 0)',
+        ];
+        $marshall = new Marshaller($articles);
+        $result = $marshall->one($data);
+
+        $this->assertEquals($data['geo_line'], $result->geo_line);
+        $this->assertEquals($data['geo_geometry'], $result->geo_geometry);
+        $this->assertEquals($data['geo_point'], $result->geo_point);
+        $this->assertEquals($data['geo_polygon'], $result->geo_polygon);
+    }
+
     /**
      * Ensure that marshalling casts reasonably.
      */