diff --git a/.travis.yml b/.travis.yml index 22ee6ce7..b5a50f0e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,9 @@ php: - '7.0' - '7.1' - '7.2' + - '7.3' env: - - MYSQL_VERSION=5.7 - MYSQL_VERSION=8.0 dist: trusty diff --git a/Makefile b/Makefile index cb716a28..62db4599 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -V=5.7 +V=8.0 DB_DIR=$(shell pwd)/_db-$(V) mV=10.3 mDB_DIR=$(shell pwd)/_db-$(mV) diff --git a/README.md b/README.md index bbfe84cc..88c2a5ac 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ Please check the documentation for your MySQL version. MySQL's Extension for Spa **Versions** - `1.x.x`: MySQL 5.6 (also supports MySQL 5.5 but not all spatial analysis functions) -- `2.x.x`: MySQL 5.7 and 8.0 +- `2.x.x`: MySQL 5.7 +- **`3.x.x`: MySQL 8.0 with SRID support (Current branch)** This package also works with MariaDB. Please refer to the [MySQL/MariaDB Spatial Support Matrix](https://mariadb.com/kb/en/library/mysqlmariadb-spatial-support-matrix/) for compatibility. @@ -21,14 +22,20 @@ This package also works with MariaDB. Please refer to the [MySQL/MariaDB Spatial Add the package using composer: +```sh +$ composer require grimzy/laravel-mysql-spatial +``` + +For MySQL 5.7: + ```shell -composer require grimzy/laravel-mysql-spatial +$ composer require grimzy/laravel-mysql-spatial:^2.0 ``` For MySQL 5.6 and 5.5: ```shell -composer require grimzy/laravel-mysql-spatial:^1.0 +$ composer require grimzy/laravel-mysql-spatial:^1.0 ``` For Laravel versions before 5.5 or if not using auto-discovery, register the service provider in `config/app.php`: @@ -80,6 +87,19 @@ class CreatePlacesTable extends Migration { $table->polygon('area')->nullable(); $table->timestamps(); }); + + // Or create the spatial fields with an SRID (e.g. 4326 WGS84 spheroid) + + // Schema::create('places', function(Blueprint $table) + // { + // $table->increments('id'); + // $table->string('name')->unique(); + // // Add a Point spatial data field named location with SRID 4326 + // $table->point('location', 4326)->nullable(); + // // Add a Polygon spatial data field named area with SRID 4326 + // $table->polygon('area', 4326)->nullable(); + // $table->timestamps(); + // }); } /** @@ -158,11 +178,37 @@ $place1->area = new Polygon([new LineString([ new Point(40.74894149554006, -73.98615270853043) ])]); $place1->save(); +``` + +Or if your database fields were created with a specific SRID: + +```php +use Grimzy\LaravelMysqlSpatial\Types\Point; +use Grimzy\LaravelMysqlSpatial\Types\Polygon; +use Grimzy\LaravelMysqlSpatial\Types\LineString; + +$place1 = new Place(); +$place1->name = 'Empire State Building'; -$place1->area = new Polygon(); +// saving a point with SRID 4326 (WGS84 spheroid) +$place1->location = new Point(40.7484404, -73.9878441, 4326); // (lat, lng, srid) +$place1->save(); +// saving a polygon with SRID 4326 (WGS84 spheroid) +$place1->area = new Polygon([new LineString([ + new Point(40.74894149554006, -73.98615270853043), + new Point(40.74848633046773, -73.98648262023926), + new Point(40.747925497790725, -73.9851602911949), + new Point(40.74837050671544, -73.98482501506805), + new Point(40.74894149554006, -73.98615270853043) +])], 4326); +$place1->save(); ``` +> **Note**: When saving collection Geometries (`LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, and `GeometryCollection`), only the top-most geometry should have an SRID set in the constructor. +> +> In the example above, when creating a `new Polygon()`, we only set the SRID on the `Polygon` and use the default for the `LineString` and the `Point` objects. + ### Retrieving a model ```php @@ -177,13 +223,13 @@ $lng = $place2->location->getLng(); // -73.9878441 | Grimzy\LaravelMysqlSpatial\Types | OpenGIS Class | | ------------------------------------------------------------ | ------------------------------------------------------------ | -| `Point($lat, $lng)` | [Point](https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html) | -| `MultiPoint(Point[])` | [MultiPoint](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multipoint.html) | -| `LineString(Point[])` | [LineString](https://dev.mysql.com/doc/refman/8.0/en/gis-class-linestring.html) | -| `MultiLineString(LineString[])` | [MultiLineString](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multilinestring.html) | -| `Polygon(LineString[])` *([exterior and interior boundaries](https://dev.mysql.com/doc/refman/8.0/en/gis-class-polygon.html))* | [Polygon](https://dev.mysql.com/doc/refman/8.0/en/gis-class-polygon.html) | -| `MultiPolygon(Polygon[])` | [MultiPolygon](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multipolygon.html) | -| `GeometryCollection(Geometry[])` | [GeometryCollection](https://dev.mysql.com/doc/refman/8.0/en/gis-class-geometrycollection.html) | +| `Point($lat, $lng, $srid = 0)` | [Point](https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html) | +| `MultiPoint(Point[], $srid = 0)` | [MultiPoint](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multipoint.html) | +| `LineString(Point[], $srid = 0)` | [LineString](https://dev.mysql.com/doc/refman/8.0/en/gis-class-linestring.html) | +| `MultiLineString(LineString[], $srid = 0)` | [MultiLineString](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multilinestring.html) | +| `Polygon(LineString[], $srid = 0)` *([exterior and interior boundaries](https://dev.mysql.com/doc/refman/8.0/en/gis-class-polygon.html))* | [Polygon](https://dev.mysql.com/doc/refman/8.0/en/gis-class-polygon.html) | +| `MultiPolygon(Polygon[], $srid = 0)` | [MultiPolygon](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multipolygon.html) | +| `GeometryCollection(Geometry[], $srid = 0)` | [GeometryCollection](https://dev.mysql.com/doc/refman/8.0/en/gis-class-geometrycollection.html) | Check out the [Class diagram](https://user-images.githubusercontent.com/1837678/30788608-a5afd894-a16c-11e7-9a51-0a08b331d4c4.png). @@ -193,7 +239,7 @@ In order for your Eloquent Model to handle the Geometry classes, it must use the #### IteratorAggregate and ArrayAccess -The "composite" Geometries (`LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, and `GeometryCollection`) implement [`IteratorAggregate`](http://php.net/manual/en/class.iteratoraggregate.php) and [`ArrayAccess`](http://php.net/manual/en/class.arrayaccess.php); making it easy to perform Iterator and Array operations. For example: +The collection Geometries (`LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, and `GeometryCollection`) implement [`IteratorAggregate`](http://php.net/manual/en/class.iteratoraggregate.php) and [`ArrayAccess`](http://php.net/manual/en/class.arrayaccess.php); making it easy to perform Iterator and Array operations. For example: ```php $polygon = $multipolygon[10]; // ArrayAccess @@ -207,10 +253,10 @@ for($polygon as $i => $linestring) { #### Helpers -##### From/To Well Known Text ([WKT](https://dev.mysql.com/doc/refman/5.7/en/gis-data-formats.html#gis-wkt-format)) +##### From/To Well Known Text ([WKT](https://dev.mysql.com/doc/refman/8.0/en/gis-data-formats.html#gis-wkt-format)) ```php -// fromWKT($wkt) +// fromWKT($wkt, $srid = 0) $point = Point::fromWKT('POINT(2 1)'); $point->toWKT(); // POINT(2 1) @@ -221,9 +267,9 @@ $polygon->toWKT(); // POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1)) ##### From/To String ```php -// fromString($wkt) +// fromString($wkt, $srid = 0) $point = new Point(1, 2); // lat, lng -(string)$point // lng, lat: 2 1 +(string)$point // lng, lat: 2 1 $polygon = Polygon::fromString('(0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1)'); (string)$polygon; // (0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1) @@ -255,9 +301,9 @@ To deserialize a GeoJSON string into a Geometry class, you can use `Geometry::fr ```php $location = Geometry::fromJson('{"type":"Point","coordinates":[3.4,1.2]}'); -$location instanceof Point::class; // true -$location->getLat(); // 1.2 -$location->getLng()); // 3.4 +$location instanceof Point::class; // true +$location->getLat(); // 1.2 +$location->getLng()); // 3.4 ``` ## Scopes: Spatial analysis functions @@ -280,10 +326,10 @@ Available scopes: - `overlaps($geometryColumn, $geometry)` - `doesTouch($geometryColumn, $geometry)` - `orderBySpatial($geometryColumn, $geometry, $orderFunction, $direction = 'asc')` -- `orderByDistance($geometryColumn, ​$geometry, ​$direction = 'asc')` -- `orderByDistanceSphere($geometryColumn, ​$geometry, ​$direction = 'asc')` +- `orderByDistance($geometryColumn, $geometry, $direction = 'asc')` +- `orderByDistanceSphere($geometryColumn, $geometry, $direction = 'asc')` -*Note that behavior and availability of MySQL spatial analysis functions differs in each MySQL version (cf. [documentation](https://dev.mysql.com/doc/refman/5.7/en/spatial-function-reference.html)).* +*Note that behavior and availability of MySQL spatial analysis functions differs in each MySQL version (cf. [documentation](https://dev.mysql.com/doc/refman/8.0/en/spatial-function-reference.html)).* ## Migrations @@ -300,16 +346,16 @@ class CreatePlacesTable extends Migration { ### Columns -Available [MySQL Spatial Types](https://dev.mysql.com/doc/refman/5.7/en/spatial-datatypes.html) migration blueprints: +Available [MySQL Spatial Types](https://dev.mysql.com/doc/refman/8.0/en/spatial-type-overview.html) migration blueprints: -- `$table->geometry('column_name')` -- `$table->point('column_name')` -- `$table->lineString('column_name')` -- `$table->polygon('column_name')` -- `$table->multiPoint('column_name')` -- `$table->multiLineString('column_name')` -- `$table->multiPolygon('column_name')` -- `$table->geometryCollection('column_name')` +- `$table->geometry(string $column_name, int $srid = 0)` +- `$table->point(string $column_name, int $srid = 0)` +- `$table->lineString(string $column_name, int $srid = 0)` +- `$table->polygon(string $column_name, int $srid = 0)` +- `$table->multiPoint(string $column_name, int $srid = 0)` +- `$table->multiLineString(string $column_name, int $srid = 0)` +- `$table->multiPolygon(string $column_name, int $srid = 0)` +- `$table->geometryCollection(string $column_name, int $srid = 0)` ### Spatial indexes @@ -318,9 +364,9 @@ You can add or drop spatial indexes in your migrations with the `spatialIndex` a - `$table->spatialIndex('column_name')` - `$table->dropSpatialIndex(['column_name'])` or `$table->dropSpatialIndex('index_name')` -Note about spatial indexes from the [MySQL documentation](https://dev.mysql.com/doc/refman/5.7/en/creating-spatial-indexes.html): +Note about spatial indexes from the [MySQL documentation](https://dev.mysql.com/doc/refman/8.0/en/creating-spatial-indexes.html): -> For [`MyISAM`](https://dev.mysql.com/doc/refman/5.7/en/myisam-storage-engine.html) and (as of MySQL 5.7.5) `InnoDB` tables, MySQL can create spatial indexes using syntax similar to that for creating regular indexes, but using the `SPATIAL` keyword. Columns in spatial indexes must be declared `NOT NULL`. +> For [`MyISAM`](https://dev.mysql.com/doc/refman/8.0/en/myisam-storage-engine.html) and (as of MySQL 5.7.5) `InnoDB` tables, MySQL can create spatial indexes using syntax similar to that for creating regular indexes, but using the `SPATIAL` keyword. Columns in spatial indexes must be declared `NOT NULL`. Also please read this [**important note**](https://laravel.com/docs/5.5/migrations#indexes) regarding Index Lengths in the Laravel 5.6 documentation. @@ -381,18 +427,18 @@ class UpdatePlacesTable extends Migration ## Tests ```shell -composer test +$ composer test # or -composer test:unit -composer test:integration +$ composer test:unit +$ composer test:integration ``` Integration tests require a running MySQL database. If you have Docker installed, you can start easily start one: ```shell -make start_db # starts MySQL 8.0 +$ make start_db # starts MySQL 8.0 # or -make start_db V=5.7 # starts a MySQL 5.7 +$ make start_db V=5.7 # starts MySQL 5.7 ``` ## Contributing diff --git a/composer.json b/composer.json index f9ac9e4f..8dfecb78 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0.x-dev" }, "laravel": { "providers": [ diff --git a/src/Eloquent/BaseBuilder.php b/src/Eloquent/BaseBuilder.php index 33c0f21c..549e3f98 100644 --- a/src/Eloquent/BaseBuilder.php +++ b/src/Eloquent/BaseBuilder.php @@ -8,10 +8,16 @@ class BaseBuilder extends QueryBuilder { protected function cleanBindings(array $bindings) { - $bindings = array_map(function ($binding) { - return $binding instanceof SpatialExpression ? $binding->getSpatialValue() : $binding; - }, $bindings); + $spatialBindings = []; + foreach ($bindings as &$binding) { + if ($binding instanceof SpatialExpression) { + $spatialBindings[] = $binding->getSpatialValue(); + $spatialBindings[] = $binding->getSrid(); + } else { + $spatialBindings[] = $binding; + } + } - return parent::cleanBindings($bindings); + return parent::cleanBindings($spatialBindings); } } diff --git a/src/Eloquent/SpatialExpression.php b/src/Eloquent/SpatialExpression.php index 7bc88178..9224af0f 100644 --- a/src/Eloquent/SpatialExpression.php +++ b/src/Eloquent/SpatialExpression.php @@ -8,11 +8,16 @@ class SpatialExpression extends Expression { public function getValue() { - return 'ST_GeomFromText(?)'; + return "ST_GeomFromText(?, ?, 'axis-order=long-lat')"; } public function getSpatialValue() { return $this->value->toWkt(); } + + public function getSrid() + { + return $this->value->getSrid(); + } } diff --git a/src/Eloquent/SpatialTrait.php b/src/Eloquent/SpatialTrait.php index da6c2a95..5cc3f4b1 100755 --- a/src/Eloquent/SpatialTrait.php +++ b/src/Eloquent/SpatialTrait.php @@ -134,8 +134,9 @@ public function scopeDistance($query, $geometryColumn, $geometry, $distance) { $this->isColumnAllowed($geometryColumn); - $query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?)) <= ?", [ + $query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) <= ?", [ $geometry->toWkt(), + $geometry->getSrid(), $distance, ]); @@ -148,8 +149,9 @@ public function scopeDistanceExcludingSelf($query, $geometryColumn, $geometry, $ $query = $this->scopeDistance($query, $geometryColumn, $geometry, $distance); - $query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?)) != 0", [ + $query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) != 0", [ $geometry->toWkt(), + $geometry->getSrid(), ]); return $query; @@ -165,8 +167,9 @@ public function scopeDistanceValue($query, $geometryColumn, $geometry) $query->select('*'); } - $query->selectRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?)) as distance", [ + $query->selectRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) as distance", [ $geometry->toWkt(), + $geometry->getSrid(), ]); } @@ -174,8 +177,9 @@ public function scopeDistanceSphere($query, $geometryColumn, $geometry, $distanc { $this->isColumnAllowed($geometryColumn); - $query->whereRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?)) <= ?", [ + $query->whereRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) <= ?", [ $geometry->toWkt(), + $geometry->getSrid(), $distance, ]); @@ -188,8 +192,9 @@ public function scopeDistanceSphereExcludingSelf($query, $geometryColumn, $geome $query = $this->scopeDistanceSphere($query, $geometryColumn, $geometry, $distance); - $query->whereRaw("st_distance_sphere($geometryColumn, ST_GeomFromText(?)) != 0", [ + $query->whereRaw("st_distance_sphere($geometryColumn, ST_GeomFromText(?, ?, 'axis-order=long-lat')) != 0", [ $geometry->toWkt(), + $geometry->getSrid(), ]); return $query; @@ -204,8 +209,9 @@ public function scopeDistanceSphereValue($query, $geometryColumn, $geometry) if (!$columns) { $query->select('*'); } - $query->selectRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?)) as distance", [ + $query->selectRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) as distance", [ $geometry->toWkt(), + $geometry->getSrid(), ]); } @@ -217,8 +223,9 @@ public function scopeComparison($query, $geometryColumn, $geometry, $relationshi throw new UnknownSpatialRelationFunction($relationship); } - $query->whereRaw("st_{$relationship}(`$geometryColumn`, ST_GeomFromText(?))", [ + $query->whereRaw("st_{$relationship}(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat'))", [ $geometry->toWkt(), + $geometry->getSrid(), ]); return $query; @@ -272,8 +279,9 @@ public function scopeOrderBySpatial($query, $geometryColumn, $geometry, $orderFu throw new UnknownSpatialFunctionException($orderFunction); } - $query->orderByRaw("st_{$orderFunction}(`$geometryColumn`, ST_GeomFromText(?)) {$direction}", [ + $query->orderByRaw("st_{$orderFunction}(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) {$direction}", [ $geometry->toWkt(), + $geometry->getSrid(), ]); return $query; diff --git a/src/Schema/Blueprint.php b/src/Schema/Blueprint.php index d061da26..0a333f06 100644 --- a/src/Schema/Blueprint.php +++ b/src/Schema/Blueprint.php @@ -9,13 +9,14 @@ class Blueprint extends IlluminateBlueprint /** * Add a geometry column on the table. * - * @param string $column + * @param string $column + * @param null|int $srid * * @return \Illuminate\Support\Fluent */ - public function geometry($column) + public function geometry($column, $srid = null) { - return $this->addColumn('geometry', $column); + return $this->addColumn('geometry', $column, compact('srid')); } /** @@ -34,73 +35,79 @@ public function point($column, $srid = null) /** * Add a linestring column on the table. * - * @param $column + * @param string $column + * @param null|int $srid * * @return \Illuminate\Support\Fluent */ - public function lineString($column) + public function lineString($column, $srid = null) { - return $this->addColumn('linestring', $column); + return $this->addColumn('linestring', $column, compact('srid')); } /** * Add a polygon column on the table. * - * @param $column + * @param string $column + * @param null|int $srid * * @return \Illuminate\Support\Fluent */ - public function polygon($column) + public function polygon($column, $srid = null) { - return $this->addColumn('polygon', $column); + return $this->addColumn('polygon', $column, compact('srid')); } /** * Add a multipoint column on the table. * - * @param $column + * @param string $column + * @param null|int $srid * * @return \Illuminate\Support\Fluent */ - public function multiPoint($column) + public function multiPoint($column, $srid = null) { - return $this->addColumn('multipoint', $column); + return $this->addColumn('multipoint', $column, compact('srid')); } /** * Add a multilinestring column on the table. * - * @param $column + * @param string $column + * @param null|int $srid * * @return \Illuminate\Support\Fluent */ - public function multiLineString($column) + public function multiLineString($column, $srid = null) { - return $this->addColumn('multilinestring', $column); + return $this->addColumn('multilinestring', $column, compact('srid')); } /** * Add a multipolygon column on the table. * - * @param $column + * @param string $column + * @param null|int $srid * * @return \Illuminate\Support\Fluent */ - public function multiPolygon($column) + public function multiPolygon($column, $srid = null) { - return $this->addColumn('multipolygon', $column); + return $this->addColumn('multipolygon', $column, compact('srid')); } /** * Add a geometrycollection column on the table. * - * @param $column + * @param string $column + * @param null|int $srid * * @return \Illuminate\Support\Fluent */ - public function geometryCollection($column) + public function geometryCollection($column, $srid = null) { - return $this->addColumn('geometrycollection', $column); + return $this->addColumn('geometrycollection', $column, compact('srid')); } /** diff --git a/src/Schema/Grammars/MySqlGrammar.php b/src/Schema/Grammars/MySqlGrammar.php index 61f015d1..9afe4513 100644 --- a/src/Schema/Grammars/MySqlGrammar.php +++ b/src/Schema/Grammars/MySqlGrammar.php @@ -8,10 +8,20 @@ class MySqlGrammar extends IlluminateMySqlGrammar { + const COLUMN_MODIFIER_SRID = 'Srid'; + + public function __construct() + { + // Enable SRID as a column modifier + if (!in_array(self::COLUMN_MODIFIER_SRID, $this->modifiers)) { + $this->modifiers[] = self::COLUMN_MODIFIER_SRID; + } + } + /** * Adds a statement to add a geometry column. * - * @param \Illuminate\Support\Fluent $column + * @param Fluent $column * * @return string */ @@ -23,7 +33,7 @@ public function typeGeometry(Fluent $column) /** * Adds a statement to add a point column. * - * @param \Illuminate\Support\Fluent $column + * @param Fluent $column * * @return string */ @@ -35,7 +45,7 @@ public function typePoint(Fluent $column) /** * Adds a statement to add a linestring column. * - * @param \Illuminate\Support\Fluent $column + * @param Fluent $column * * @return string */ @@ -47,7 +57,7 @@ public function typeLinestring(Fluent $column) /** * Adds a statement to add a polygon column. * - * @param \Illuminate\Support\Fluent $column + * @param Fluent $column * * @return string */ @@ -59,7 +69,7 @@ public function typePolygon(Fluent $column) /** * Adds a statement to add a multipoint column. * - * @param \Illuminate\Support\Fluent $column + * @param Fluent $column * * @return string */ @@ -71,7 +81,7 @@ public function typeMultipoint(Fluent $column) /** * Adds a statement to add a multilinestring column. * - * @param \Illuminate\Support\Fluent $column + * @param Fluent $column * * @return string */ @@ -83,7 +93,7 @@ public function typeMultilinestring(Fluent $column) /** * Adds a statement to add a multipolygon column. * - * @param \Illuminate\Support\Fluent $column + * @param Fluent $column * * @return string */ @@ -95,7 +105,7 @@ public function typeMultipolygon(Fluent $column) /** * Adds a statement to add a geometrycollection column. * - * @param \Illuminate\Support\Fluent $column + * @param Fluent $column * * @return string */ @@ -107,8 +117,8 @@ public function typeGeometrycollection(Fluent $column) /** * Compile a spatial index key command. * - * @param \Grimzy\LaravelMysqlSpatial\Schema\Blueprint $blueprint - * @param \Illuminate\Support\Fluent $command + * @param Blueprint $blueprint + * @param Fluent $command * * @return string */ @@ -116,4 +126,19 @@ public function compileSpatial(Blueprint $blueprint, Fluent $command) { return $this->compileKey($blueprint, $command, 'spatial'); } + + /** + * Get the SQL for a SRID column modifier. + * + * @param \Illuminate\Database\Schema\Blueprint $blueprint + * @param Fluent $column + * + * @return string|null + */ + protected function modifySrid(\Illuminate\Database\Schema\Blueprint $blueprint, Fluent $column) + { + if (!is_null($column->srid) && is_int($column->srid) && $column->srid > 0) { + return ' srid '.$column->srid; + } + } } diff --git a/src/Types/Factory.php b/src/Types/Factory.php index 087348fa..ed04ac2d 100755 --- a/src/Types/Factory.php +++ b/src/Types/Factory.php @@ -6,41 +6,41 @@ class Factory implements \GeoIO\Factory { public function createPoint($dimension, array $coordinates, $srid = null) { - return new Point($coordinates['y'], $coordinates['x']); + return new Point($coordinates['y'], $coordinates['x'], $srid); } public function createLineString($dimension, array $points, $srid = null) { - return new LineString($points); + return new LineString($points, $srid); } public function createLinearRing($dimension, array $points, $srid = null) { - return new LineString($points); + return new LineString($points, $srid); } public function createPolygon($dimension, array $lineStrings, $srid = null) { - return new Polygon($lineStrings); + return new Polygon($lineStrings, $srid); } public function createMultiPoint($dimension, array $points, $srid = null) { - return new MultiPoint($points); + return new MultiPoint($points, $srid); } public function createMultiLineString($dimension, array $lineStrings, $srid = null) { - return new MultiLineString($lineStrings); + return new MultiLineString($lineStrings, $srid); } public function createMultiPolygon($dimension, array $polygons, $srid = null) { - return new MultiPolygon($polygons); + return new MultiPolygon($polygons, $srid); } public function createGeometryCollection($dimension, array $geometries, $srid = null) { - return new GeometryCollection($geometries); + return new GeometryCollection($geometries, $srid); } } diff --git a/src/Types/Geometry.php b/src/Types/Geometry.php index c0df8ec3..f840874c 100644 --- a/src/Types/Geometry.php +++ b/src/Types/Geometry.php @@ -19,6 +19,23 @@ abstract class Geometry implements GeometryInterface, Jsonable, \JsonSerializabl 7 => GeometryCollection::class, ]; + protected $srid; + + public function __construct($srid = 0) + { + $this->srid = (int) $srid; + } + + public function getSrid() + { + return $this->srid; + } + + public function setSrid($srid) + { + $this->srid = (int) $srid; + } + public static function getWKTArgument($value) { $left = strpos($value, '('); @@ -54,18 +71,27 @@ public static function getWKTClass($value) public static function fromWKB($wkb) { - // mysql adds 4 NUL bytes at the start of the binary + $srid = substr($wkb, 0, 4); + $srid = unpack('L', $srid)[1]; + $wkb = substr($wkb, 4); $parser = new Parser(new Factory()); - return $parser->parse($wkb); + /** @var Geometry $parsed */ + $parsed = $parser->parse($wkb); + + if ($srid > 0) { + $parsed->setSrid($srid); + } + + return $parsed; } - public static function fromWKT($wkt) + public static function fromWKT($wkt, $srid = null) { $wktArgument = static::getWKTArgument($wkt); - return static::fromString($wktArgument); + return static::fromString($wktArgument, $srid); } public static function fromJson($geoJson) diff --git a/src/Types/GeometryCollection.php b/src/Types/GeometryCollection.php index 4311685a..35f093f7 100755 --- a/src/Types/GeometryCollection.php +++ b/src/Types/GeometryCollection.php @@ -37,11 +37,14 @@ class GeometryCollection extends Geometry implements IteratorAggregate, ArrayAcc /** * @param GeometryInterface[] $geometries + * @param int $srid * * @throws InvalidArgumentException */ - public function __construct(array $geometries) + public function __construct(array $geometries, $srid = 0) { + parent::__construct($srid); + $this->validateItems($geometries); $this->items = $geometries; @@ -64,7 +67,7 @@ public function __toString() }, $this->items)); } - public static function fromString($wktArgument) + public static function fromString($wktArgument, $srid = 0) { if (empty($wktArgument)) { return new static([]); @@ -76,7 +79,7 @@ public static function fromString($wktArgument) $klass = Geometry::getWKTClass($geometry_string); return call_user_func($klass.'::fromWKT', $geometry_string); - }, $geometry_strings)); + }, $geometry_strings), $srid); } public function toArray() diff --git a/src/Types/GeometryInterface.php b/src/Types/GeometryInterface.php index aca2bfb0..4f0dd1ef 100644 --- a/src/Types/GeometryInterface.php +++ b/src/Types/GeometryInterface.php @@ -6,11 +6,11 @@ interface GeometryInterface { public function toWKT(); - public static function fromWKT($wkt); + public static function fromWKT($wkt, $srid = 0); public function __toString(); - public static function fromString($wktArgument); + public static function fromString($wktArgument, $srid = 0); public static function fromJson($geoJson); } diff --git a/src/Types/LineString.php b/src/Types/LineString.php index 8c226444..1cc4a410 100644 --- a/src/Types/LineString.php +++ b/src/Types/LineString.php @@ -20,21 +20,21 @@ public function toWKT() return sprintf('LINESTRING(%s)', $this->toPairList()); } - public static function fromWkt($wkt) + public static function fromWkt($wkt, $srid = 0) { $wktArgument = Geometry::getWKTArgument($wkt); - return static::fromString($wktArgument); + return static::fromString($wktArgument, $srid); } - public static function fromString($wktArgument) + public static function fromString($wktArgument, $srid = 0) { $pairs = explode(',', trim($wktArgument)); $points = array_map(function ($pair) { return Point::fromPair($pair); }, $pairs); - return new static($points); + return new static($points, $srid); } public function __toString() diff --git a/src/Types/MultiLineString.php b/src/Types/MultiLineString.php index dd815ebf..62c4d576 100644 --- a/src/Types/MultiLineString.php +++ b/src/Types/MultiLineString.php @@ -32,14 +32,14 @@ public function toWKT() return sprintf('MULTILINESTRING(%s)', (string) $this); } - public static function fromString($wktArgument) + public static function fromString($wktArgument, $srid = 0) { $str = preg_split('/\)\s*,\s*\(/', substr(trim($wktArgument), 1, -1)); $lineStrings = array_map(function ($data) { return LineString::fromString($data); }, $str); - return new static($lineStrings); + return new static($lineStrings, $srid); } public function __toString() diff --git a/src/Types/MultiPoint.php b/src/Types/MultiPoint.php index eafa7c0e..752967eb 100644 --- a/src/Types/MultiPoint.php +++ b/src/Types/MultiPoint.php @@ -20,14 +20,14 @@ public function toWKT() return sprintf('MULTIPOINT(%s)', (string) $this); } - public static function fromWkt($wkt) + public static function fromWkt($wkt, $srid = 0) { $wktArgument = Geometry::getWKTArgument($wkt); - return static::fromString($wktArgument); + return static::fromString($wktArgument, $srid); } - public static function fromString($wktArgument) + public static function fromString($wktArgument, $srid = 0) { $matches = []; preg_match_all('/\(\s*(\d+\s+\d+)\s*\)/', trim($wktArgument), $matches); @@ -36,7 +36,7 @@ public static function fromString($wktArgument) return Point::fromPair($pair); }, $matches[1]); - return new static($points); + return new static($points, $srid); } public function __toString() diff --git a/src/Types/MultiPolygon.php b/src/Types/MultiPolygon.php index 0a5b6784..cdea3a9c 100644 --- a/src/Types/MultiPolygon.php +++ b/src/Types/MultiPolygon.php @@ -34,14 +34,14 @@ public function __toString() }, $this->items)); } - public static function fromString($wktArgument) + public static function fromString($wktArgument, $srid = 0) { $parts = preg_split('/(\)\s*\)\s*,\s*\(\s*\()/', $wktArgument, -1, PREG_SPLIT_DELIM_CAPTURE); $polygons = static::assembleParts($parts); return new static(array_map(function ($polygonString) { return Polygon::fromString($polygonString); - }, $polygons)); + }, $polygons), $srid); } /** diff --git a/src/Types/Point.php b/src/Types/Point.php index 40719af4..d424ec5e 100644 --- a/src/Types/Point.php +++ b/src/Types/Point.php @@ -12,8 +12,10 @@ class Point extends Geometry protected $lng; - public function __construct($lat, $lng) + public function __construct($lat, $lng, $srid = 0) { + parent::__construct($srid); + $this->lat = (float) $lat; $this->lng = (float) $lng; } @@ -43,11 +45,11 @@ public function toPair() return $this->getLng().' '.$this->getLat(); } - public static function fromPair($pair) + public static function fromPair($pair, $srid = 0) { list($lng, $lat) = explode(' ', trim($pair, "\t\n\r \x0B()")); - return new static((float) $lat, (float) $lng); + return new static((float) $lat, (float) $lng, (int) $srid); } public function toWKT() @@ -55,9 +57,9 @@ public function toWKT() return sprintf('POINT(%s)', (string) $this); } - public static function fromString($wktArgument) + public static function fromString($wktArgument, $srid = 0) { - return static::fromPair($wktArgument); + return static::fromPair($wktArgument, $srid); } public function __toString() diff --git a/tests/Integration/IntegrationBaseTestCase.php b/tests/Integration/IntegrationBaseTestCase.php new file mode 100644 index 00000000..04634734 --- /dev/null +++ b/tests/Integration/IntegrationBaseTestCase.php @@ -0,0 +1,110 @@ +register(SpatialServiceProvider::class); + + $app->make('Illuminate\Contracts\Console\Kernel')->bootstrap(); + + $app['config']->set('database.default', 'mysql'); + $app['config']->set('database.connections.mysql.host', env('DB_HOST')); + $app['config']->set('database.connections.mysql.port', env('DB_PORT')); + $app['config']->set('database.connections.mysql.database', env('DB_DATABASE')); + $app['config']->set('database.connections.mysql.username', env('DB_USERNAME')); + $app['config']->set('database.connections.mysql.password', env('DB_PASSWORD')); + $app['config']->set('database.connections.mysql.modes', [ + 'ONLY_FULL_GROUP_BY', + 'STRICT_TRANS_TABLES', + 'NO_ZERO_IN_DATE', + 'NO_ZERO_DATE', + 'ERROR_FOR_DIVISION_BY_ZERO', + 'NO_ENGINE_SUBSTITUTION', + ]); + + return $app; + } + + /** + * Setup DB before each test. + * + * @return void + */ + public function setUp() + { + parent::setUp(); + + $this->after_fix = $this->isMySQL8AfterFix(); + + $this->onMigrations(function ($migrationClass) { + (new $migrationClass())->up(); + }); + + //\DB::listen(function($sql) { + // var_dump($sql); + //}); + } + + public function tearDown() + { + $this->onMigrations(function ($migrationClass) { + (new $migrationClass())->down(); + }, true); + + parent::tearDown(); + } + + // MySQL 8.0.4 fixed bug #26941370 and bug #88031 + private function isMySQL8AfterFix() + { + $results = DB::select(DB::raw('select version()')); + $mysql_version = $results[0]->{'version()'}; + + return version_compare($mysql_version, '8.0.4', '>='); + } + + protected function assertDatabaseHas($table, array $data, $connection = null) + { + if (method_exists($this, 'seeInDatabase')) { + $this->seeInDatabase($table, $data, $connection); + } else { + parent::assertDatabaseHas($table, $data, $connection); + } + } + + protected function assertException($exceptionName, $exceptionMessage = null) + { + if (method_exists(parent::class, 'expectException')) { + parent::expectException($exceptionName); + if (!is_null($exceptionMessage)) { + $this->expectExceptionMessage($exceptionMessage); + } + } else { + $this->setExpectedException($exceptionName, $exceptionMessage); + } + } + + private function onMigrations(\Closure $closure, $reverse_sort = false) + { + $migrations = $this->migrations; + $reverse_sort ? rsort($migrations, SORT_STRING) : sort($migrations, SORT_STRING); + + foreach ($migrations as $migrationClass) { + $closure($migrationClass); + } + } +} diff --git a/tests/Integration/MigrationTest.php b/tests/Integration/MigrationTest.php new file mode 100644 index 00000000..6b740d0f --- /dev/null +++ b/tests/Integration/MigrationTest.php @@ -0,0 +1,56 @@ +assertEquals('geometry', $result->Table); + $this->assertEquals($expected, $result->{'Create Table'}); + } + + public function testTableWasCreatedWithSrid() + { + $result = DB::selectOne('SHOW CREATE TABLE with_srid'); + + $expected = 'CREATE TABLE `with_srid` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `geo` geometry /*!80003 SRID 3857 */ DEFAULT NULL, + `location` point /*!80003 SRID 3857 */ DEFAULT NULL, + `line` linestring /*!80003 SRID 3857 */ DEFAULT NULL, + `shape` polygon /*!80003 SRID 3857 */ DEFAULT NULL, + `multi_locations` multipoint /*!80003 SRID 3857 */ DEFAULT NULL, + `multi_lines` multilinestring /*!80003 SRID 3857 */ DEFAULT NULL, + `multi_shapes` multipolygon /*!80003 SRID 3857 */ DEFAULT NULL, + `multi_geometries` geomcollection /*!80003 SRID 3857 */ DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'; + + $this->assertEquals('with_srid', $result->Table); + $this->assertEquals($expected, $result->{'Create Table'}); + } +} diff --git a/tests/Integration/Migrations/CreateTables.php b/tests/Integration/Migrations/CreateTables.php index 93f08704..fdff4f58 100644 --- a/tests/Integration/Migrations/CreateTables.php +++ b/tests/Integration/Migrations/CreateTables.php @@ -1,7 +1,7 @@ charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; $table->increments('id'); $table->geometry('geo')->default(null)->nullable(); $table->point('location'); // required to be not null in order to add an index @@ -30,6 +32,20 @@ public function up() $table->increments('id'); $table->geometry('geometry')->default(null)->nullable(); }); + + Schema::create('with_srid', function (Blueprint $table) { + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->increments('id'); + $table->geometry('geo', 3857)->default(null)->nullable(); + $table->point('location', 3857)->default(null)->nullable(); + $table->lineString('line', 3857)->default(null)->nullable(); + $table->polygon('shape', 3857)->default(null)->nullable(); + $table->multiPoint('multi_locations', 3857)->default(null)->nullable(); + $table->multiLineString('multi_lines', 3857)->default(null)->nullable(); + $table->multiPolygon('multi_shapes', 3857)->default(null)->nullable(); + $table->geometryCollection('multi_geometries', 3857)->default(null)->nullable(); + }); } /** @@ -41,5 +57,6 @@ public function down() { Schema::drop('geometry'); Schema::drop('no_spatial_fields'); + Schema::drop('with_srid'); } } diff --git a/tests/Integration/Migrations/UpdateTables.php b/tests/Integration/Migrations/UpdateTables.php index 46ebaccc..83915b81 100644 --- a/tests/Integration/Migrations/UpdateTables.php +++ b/tests/Integration/Migrations/UpdateTables.php @@ -1,7 +1,7 @@ exec(sprintf('DROP DATABASE IF EXISTS %s', $database)); - if ($recreate) { - $pdo->exec(sprintf( - 'CREATE DATABASE %s CHARACTER SET %s COLLATE %s;', - $database, - env('DB_CHARSET', 'utf8mb4'), - env('DB_COLLATION', 'utf8mb4_unicode_ci') - )); - } - } catch (RuntimeException $exception) { - throw $exception; - } - } - - /** - * Boots the application. - * - * @return \Illuminate\Foundation\Application - */ - public function createApplication() - { - $app = require __DIR__.'/../../vendor/laravel/laravel/bootstrap/app.php'; - $app->register(SpatialServiceProvider::class); - - $app->make('Illuminate\Contracts\Console\Kernel')->bootstrap(); - - $app['config']->set('database.default', 'mysql'); - $app['config']->set('database.connections.mysql.host', env('DB_HOST')); - $app['config']->set('database.connections.mysql.port', env('DB_PORT')); - $app['config']->set('database.connections.mysql.database', env('DB_DATABASE')); - $app['config']->set('database.connections.mysql.username', env('DB_USERNAME')); - $app['config']->set('database.connections.mysql.password', env('DB_PASSWORD')); - $app['config']->set('database.connections.mysql.modes', [ - 'ONLY_FULL_GROUP_BY', - 'STRICT_TRANS_TABLES', - 'NO_ZERO_IN_DATE', - 'NO_ZERO_DATE', - 'ERROR_FOR_DIVISION_BY_ZERO', - 'NO_ENGINE_SUBSTITUTION', - ]); - - return $app; - } - - /** - * Setup DB before each test. - * - * @return void - */ - public function setUp() - { - parent::setUp(); - - $this->after_fix = $this->isMySQL8AfterFix(); - - $this->onMigrations(function ($migrationClass) { - (new $migrationClass())->up(); - }); - -// \DB::listen(function($sql) { -// var_dump($sql); -// }); - } - - public function tearDown() - { - $this->onMigrations(function ($migrationClass) { - (new $migrationClass())->down(); - }, true); - - parent::tearDown(); - } - - // MySQL 8.0.4 fixed bug #26941370 and bug #88031 - private function isMySQL8AfterFix() - { - $results = DB::select(DB::raw('select version()')); - $mysql_version = $results[0]->{'version()'}; - - return version_compare($mysql_version, '8.0.4', '>='); - } - - protected function assertDatabaseHas($table, array $data, $connection = null) - { - if (method_exists($this, 'seeInDatabase')) { - $this->seeInDatabase($table, $data, $connection); - } else { - parent::assertDatabaseHas($table, $data, $connection); - } - } - - protected function assertException($exceptionName) - { - if (method_exists(parent::class, 'expectException')) { - parent::expectException($exceptionName); - } else { - $this->setExpectedException($exceptionName); - } - } - - private function onMigrations(\Closure $closure, $reverse_sort = false) - { - $fileSystem = new Filesystem(); - $classFinder = new Tools\ClassFinder(); - - $migrations = $fileSystem->files(__DIR__.'/Migrations'); - $reverse_sort ? rsort($migrations, SORT_STRING) : sort($migrations, SORT_STRING); - - foreach ($migrations as $file) { - $fileSystem->requireOnce($file); - $migrationClass = $classFinder->findClass($file); - - $closure($migrationClass); - } - } + protected $migrations = [ + CreateLocationTable::class, + UpdateLocationTable::class, + ]; public function testSpatialFieldsNotDefinedException() { diff --git a/tests/Integration/SridSpatialTest.php b/tests/Integration/SridSpatialTest.php new file mode 100644 index 00000000..f57c6cb2 --- /dev/null +++ b/tests/Integration/SridSpatialTest.php @@ -0,0 +1,136 @@ +location = new Point(1, 2, 3857); + $geo->save(); + $this->assertDatabaseHas('with_srid', ['id' => $geo->id]); + } + + public function testInsertLineStringWithSrid() + { + $geo = new WithSridModel(); + + $geo->location = new Point(1, 2, 3857); + $geo->line = new LineString([new Point(1, 1), new Point(2, 2)], 3857); + $geo->save(); + $this->assertDatabaseHas('with_srid', ['id' => $geo->id]); + } + + public function testInsertPolygonWithSrid() + { + $geo = new WithSridModel(); + + $geo->location = new Point(1, 2, 3857); + $geo->shape = Polygon::fromWKT('POLYGON((0 10,10 10,10 0,0 0,0 10))', 3857); + $geo->save(); + $this->assertDatabaseHas('with_srid', ['id' => $geo->id]); + } + + public function testInsertMultiPointWithSrid() + { + $geo = new WithSridModel(); + + $geo->location = new Point(1, 2, 3857); + $geo->multi_locations = new MultiPoint([new Point(1, 1), new Point(2, 2)], 3857); + $geo->save(); + $this->assertDatabaseHas('with_srid', ['id' => $geo->id]); + } + + public function testInsertMultiPolygonWithSrid() + { + $geo = new WithSridModel(); + + $geo->location = new Point(1, 2, 3857); + + $geo->multi_shapes = new MultiPolygon([ + Polygon::fromWKT('POLYGON((0 10,10 10,10 0,0 0,0 10))'), + Polygon::fromWKT('POLYGON((0 0,0 5,5 5,5 0,0 0))'), + ], 3857); + $geo->save(); + $this->assertDatabaseHas('with_srid', ['id' => $geo->id]); + } + + public function testInsertGeometryCollectionWithSrid() + { + $geo = new WithSridModel(); + + $geo->location = new Point(1, 2, 3857); + + $geo->multi_geometries = new GeometryCollection([ + Polygon::fromWKT('POLYGON((0 10,10 10,10 0,0 0,0 10))'), + Polygon::fromWKT('POLYGON((0 0,0 5,5 5,5 0,0 0))'), + new Point(0, 0), + ], 3857); + $geo->save(); + $this->assertDatabaseHas('with_srid', ['id' => $geo->id]); + } + + public function testUpdateWithSrid() + { + $geo = new WithSridModel(); + $geo->location = new Point(1, 2, 3857); + $geo->save(); + + $to_update = WithSridModel::all()->first(); + $to_update->location = new Point(2, 3, 3857); + $to_update->save(); + + $this->assertDatabaseHas('with_srid', ['id' => $to_update->id]); + + $all = WithSridModel::all(); + $this->assertCount(1, $all); + + $updated = $all->first(); + $this->assertInstanceOf(Point::class, $updated->location); + $this->assertEquals(2, $updated->location->getLat()); + $this->assertEquals(3, $updated->location->getLng()); + } + + public function testInsertPointWithWrongSrid() + { + $geo = new WithSridModel(); + $geo->location = new Point(1, 2); + + $this->assertException( + Illuminate\Database\QueryException::class, + 'SQLSTATE[HY000]: General error: 3643 The SRID of the geometry '. + 'does not match the SRID of the column \'location\'. The SRID '. + 'of the geometry is 0, but the SRID of the column is 3857. '. + 'Consider changing the SRID of the geometry or the SRID property '. + 'of the column. (SQL: insert into `with_srid` (`location`) values '. + '(ST_GeomFromText(POINT(2 1), 0, \'axis-order=long-lat\')))' + ); + $geo->save(); + } + + public function testGeometryInsertedHasRightSrid() + { + $geo = new WithSridModel(); + $geo->location = new Point(1, 2, 3857); + $geo->save(); + + $srid = \DB::selectOne('select ST_SRID(location) as srid from with_srid'); + $this->assertEquals(3857, $srid->srid); + + $result = WithSridModel::first(); + + $this->assertEquals($geo->location->getSrid(), $result->location->getSrid()); + $a = 1; + } +} diff --git a/tests/Integration/Tools/ClassFinder.php b/tests/Integration/Tools/ClassFinder.php deleted file mode 100644 index 13a8263b..00000000 --- a/tests/Integration/Tools/ClassFinder.php +++ /dev/null @@ -1,154 +0,0 @@ -in($directory)->name('*.php') as $file) { - $classes[] = $this->findClass($file->getRealPath()); - } - - return array_filter($classes); - } - - /** - * Extract the class name from the file at the given path. - * - * @param string $path - * - * @return string|null - */ - public function findClass($path) - { - $namespace = null; - - $tokens = token_get_all(file_get_contents($path)); - - foreach ($tokens as $key => $token) { - if ($this->tokenIsNamespace($token)) { - $namespace = $this->getNamespace($key + 2, $tokens); - } elseif ($this->tokenIsClassOrInterface($token)) { - return ltrim($namespace.'\\'.$this->getClass($key + 2, $tokens), '\\'); - } - } - } - - /** - * Find the namespace in the tokens starting at a given key. - * - * @param int $key - * @param array $tokens - * - * @return string - */ - protected function getNamespace($key, array $tokens) - { - $namespace = null; - - $tokenCount = count($tokens); - - for ($i = $key; $i < $tokenCount; $i++) { - if ($this->isPartOfNamespace($tokens[$i])) { - $namespace .= $tokens[$i][1]; - } elseif ($tokens[$i] == ';') { - return $namespace; - } - } - } - - /** - * Find the class in the tokens starting at a given key. - * - * @param int $key - * @param array $tokens - * - * @return string - */ - protected function getClass($key, array $tokens) - { - $class = null; - - $tokenCount = count($tokens); - - for ($i = $key; $i < $tokenCount; $i++) { - if ($this->isPartOfClass($tokens[$i])) { - $class .= $tokens[$i][1]; - } elseif ($this->isWhitespace($tokens[$i])) { - return $class; - } - } - } - - /** - * Determine if the given token is a namespace keyword. - * - * @param array|string $token - * - * @return bool - */ - protected function tokenIsNamespace($token) - { - return is_array($token) && $token[0] == T_NAMESPACE; - } - - /** - * Determine if the given token is a class or interface keyword. - * - * @param array|string $token - * - * @return bool - */ - protected function tokenIsClassOrInterface($token) - { - return is_array($token) && ($token[0] == T_CLASS || $token[0] == T_INTERFACE); - } - - /** - * Determine if the given token is part of the namespace. - * - * @param array|string $token - * - * @return bool - */ - protected function isPartOfNamespace($token) - { - return is_array($token) && ($token[0] == T_STRING || $token[0] == T_NS_SEPARATOR); - } - - /** - * Determine if the given token is part of the class. - * - * @param array|string $token - * - * @return bool - */ - protected function isPartOfClass($token) - { - return is_array($token) && $token[0] == T_STRING; - } - - /** - * Determine if the given token is whitespace. - * - * @param array|string $token - * - * @return bool - */ - protected function isWhitespace($token) - { - return is_array($token) && $token[0] == T_WHITESPACE; - } -} diff --git a/tests/Unit/Eloquent/BuilderTest.php b/tests/Unit/Eloquent/BuilderTest.php index b77c6c4e..ed730f5a 100644 --- a/tests/Unit/Eloquent/BuilderTest.php +++ b/tests/Unit/Eloquent/BuilderTest.php @@ -72,6 +72,44 @@ public function testUpdatePolygon() $this->builder->update(['polygon' => $polygon]); } + + public function testUpdatePointWithSrid() + { + $point = new Point(1, 2, 4326); + $this->queryBuilder + ->shouldReceive('update') + ->with(['point' => new SpatialExpression($point)]) + ->once(); + + $this->builder->update(['point' => $point]); + } + + public function testUpdateLinestringWithSrid() + { + $linestring = new LineString([new Point(0, 0), new Point(1, 1), new Point(2, 2)], 4326); + + $this->queryBuilder + ->shouldReceive('update') + ->with(['linestring' => new SpatialExpression($linestring)]) + ->once(); + + $this->builder->update(['linestring' => $linestring]); + } + + public function testUpdatePolygonWithSrid() + { + $linestrings[] = new LineString([new Point(0, 0), new Point(0, 1)]); + $linestrings[] = new LineString([new Point(0, 1), new Point(1, 1)]); + $linestrings[] = new LineString([new Point(1, 1), new Point(0, 0)]); + $polygon = new Polygon($linestrings, 4326); + + $this->queryBuilder + ->shouldReceive('update') + ->with(['polygon' => new SpatialExpression($polygon)]) + ->once(); + + $this->builder->update(['polygon' => $polygon]); + } } class TestBuilderModel extends Model diff --git a/tests/Unit/Eloquent/SpatialTraitTest.php b/tests/Unit/Eloquent/SpatialTraitTest.php index f59913f3..785c78a7 100644 --- a/tests/Unit/Eloquent/SpatialTraitTest.php +++ b/tests/Unit/Eloquent/SpatialTraitTest.php @@ -37,7 +37,7 @@ public function testInsertUpdatePointHasCorrectSql() $this->model->save(); $this->assertStringStartsWith('insert', $this->queries[0]); - $this->assertContains('insert into `test_models` (`point`) values (ST_GeomFromText(?))', $this->queries[0]); + $this->assertContains('insert into `test_models` (`point`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); // TODO: assert bindings in query $this->assertTrue($this->model->exists); @@ -45,7 +45,7 @@ public function testInsertUpdatePointHasCorrectSql() $this->model->save(); $this->assertStringStartsWith('update', $this->queries[1]); - $this->assertContains('update `test_models` set `point` = ST_GeomFromText(?) where `id` = ?', $this->queries[1]); + $this->assertContains('update `test_models` set `point` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); // TODO: assert bindings in query } @@ -60,7 +60,7 @@ public function testInsertUpdateLineStringHasCorrectSql() $this->model->save(); $this->assertStringStartsWith('insert', $this->queries[0]); - $this->assertContains('insert into `test_models` (`linestring`) values (ST_GeomFromText(?))', $this->queries[0]); + $this->assertContains('insert into `test_models` (`linestring`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); // TODO: assert bindings in query $this->assertTrue($this->model->exists); @@ -68,7 +68,7 @@ public function testInsertUpdateLineStringHasCorrectSql() $this->model->save(); $this->assertStringStartsWith('update', $this->queries[1]); - $this->assertContains('update `test_models` set `linestring` = ST_GeomFromText(?) where `id` = ?', $this->queries[1]); + $this->assertContains('update `test_models` set `linestring` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); // TODO: assert bindings in query } @@ -87,14 +87,14 @@ public function testInsertUpdatePolygonHasCorrectSql() $this->model->save(); $this->assertStringStartsWith('insert', $this->queries[0]); - $this->assertContains('insert into `test_models` (`polygon`) values (ST_GeomFromText(?))', $this->queries[0]); + $this->assertContains('insert into `test_models` (`polygon`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); // TODO: assert bindings in query $this->assertTrue($this->model->exists); $this->model->polygon = new \Grimzy\LaravelMysqlSpatial\Types\Polygon([$linestring1, $linestring2]); $this->model->save(); $this->assertStringStartsWith('update', $this->queries[1]); - $this->assertContains('update `test_models` set `polygon` = ST_GeomFromText(?) where `id` = ?', $this->queries[1]); + $this->assertContains('update `test_models` set `polygon` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); // TODO: assert bindings in query } @@ -109,7 +109,7 @@ public function testInsertUpdateMultiPointHasCorrectSql() $this->model->save(); $this->assertStringStartsWith('insert', $this->queries[0]); - $this->assertContains('insert into `test_models` (`multipoint`) values (ST_GeomFromText(?))', $this->queries[0]); + $this->assertContains('insert into `test_models` (`multipoint`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); // TODO: assert bindings in query $this->assertTrue($this->model->exists); @@ -117,7 +117,7 @@ public function testInsertUpdateMultiPointHasCorrectSql() $this->model->save(); $this->assertStringStartsWith('update', $this->queries[1]); - $this->assertContains('update `test_models` set `multipoint` = ST_GeomFromText(?) where `id` = ?', $this->queries[1]); + $this->assertContains('update `test_models` set `multipoint` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); // TODO: assert bindings in query } @@ -136,14 +136,14 @@ public function testInsertUpdateMultiLineStringHasCorrectSql() $this->model->save(); $this->assertStringStartsWith('insert', $this->queries[0]); - $this->assertContains('insert into `test_models` (`multilinestring`) values (ST_GeomFromText(?))', $this->queries[0]); + $this->assertContains('insert into `test_models` (`multilinestring`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); // TODO: assert bindings in query $this->assertTrue($this->model->exists); $this->model->multilinestring = new \Grimzy\LaravelMysqlSpatial\Types\MultiLineString([$linestring1, $linestring2]); $this->model->save(); $this->assertStringStartsWith('update', $this->queries[1]); - $this->assertContains('update `test_models` set `multilinestring` = ST_GeomFromText(?) where `id` = ?', $this->queries[1]); + $this->assertContains('update `test_models` set `multilinestring` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); // TODO: assert bindings in query } @@ -171,14 +171,14 @@ public function testInsertUpdateMultiPolygonHasCorrectSql() $this->model->save(); $this->assertStringStartsWith('insert', $this->queries[0]); - $this->assertContains('insert into `test_models` (`multipolygon`) values (ST_GeomFromText(?))', $this->queries[0]); + $this->assertContains('insert into `test_models` (`multipolygon`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); // TODO: assert bindings in query $this->assertTrue($this->model->exists); $this->model->multipolygon = new \Grimzy\LaravelMysqlSpatial\Types\MultiPolygon([$polygon1, $polygon2]); $this->model->save(); $this->assertStringStartsWith('update', $this->queries[1]); - $this->assertContains('update `test_models` set `multipolygon` = ST_GeomFromText(?) where `id` = ?', $this->queries[1]); + $this->assertContains('update `test_models` set `multipolygon` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); // TODO: assert bindings in query } @@ -195,14 +195,14 @@ public function testInsertUpdateGeometryCollectionHasCorrectSql() $this->model->save(); $this->assertStringStartsWith('insert', $this->queries[0]); - $this->assertContains('insert into `test_models` (`geometrycollection`) values (ST_GeomFromText(?))', $this->queries[0]); + $this->assertContains('insert into `test_models` (`geometrycollection`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); // TODO: assert bindings in query $this->assertTrue($this->model->exists); $this->model->geometrycollection = new \Grimzy\LaravelMysqlSpatial\Types\GeometryCollection([$point1, $linestring1]); $this->model->save(); $this->assertStringStartsWith('update', $this->queries[1]); - $this->assertContains('update `test_models` set `geometrycollection` = ST_GeomFromText(?) where `id` = ?', $this->queries[1]); + $this->assertContains('update `test_models` set `geometrycollection` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); // TODO: assert bindings in query } @@ -231,9 +231,9 @@ public function testScopeDistance() $this->assertNotEmpty($q->wheres); $bindings = $q->getRawBindings()['where']; $this->assertNotEmpty($bindings); - $this->assertEquals('st_distance(`point`, ST_GeomFromText(?)) <= ?', $q->wheres[0]['sql']); + $this->assertEquals('st_distance(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) <= ?', $q->wheres[0]['sql']); $this->assertEquals('POINT(2 1)', $bindings[0]); - $this->assertEquals(10, $bindings[1]); + $this->assertEquals(10, $bindings[2]); } public function testScopeDistanceExcludingSelf() @@ -246,11 +246,11 @@ public function testScopeDistanceExcludingSelf() $this->assertNotEmpty($q->wheres); $bindings = $q->getRawBindings()['where']; $this->assertNotEmpty($bindings); - $this->assertEquals('st_distance(`point`, ST_GeomFromText(?)) <= ?', $q->wheres[0]['sql']); - $this->assertEquals('st_distance(`point`, ST_GeomFromText(?)) != 0', $q->wheres[1]['sql']); + $this->assertEquals('st_distance(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) <= ?', $q->wheres[0]['sql']); + $this->assertEquals('st_distance(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) != 0', $q->wheres[1]['sql']); $this->assertEquals('POINT(2 1)', $bindings[0]); - $this->assertEquals(10, $bindings[1]); - $this->assertEquals('POINT(2 1)', $bindings[2]); + $this->assertEquals(10, $bindings[2]); + $this->assertEquals('POINT(2 1)', $bindings[3]); } public function testScopeDistanceSphere() @@ -263,9 +263,9 @@ public function testScopeDistanceSphere() $this->assertNotEmpty($q->wheres); $bindings = $q->getRawBindings()['where']; $this->assertNotEmpty($bindings); - $this->assertEquals('st_distance_sphere(`point`, ST_GeomFromText(?)) <= ?', $q->wheres[0]['sql']); + $this->assertEquals('st_distance_sphere(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) <= ?', $q->wheres[0]['sql']); $this->assertEquals('POINT(2 1)', $bindings[0]); - $this->assertEquals(10, $bindings[1]); + $this->assertEquals(10, $bindings[2]); } public function testScopeDistanceSphereExcludingSelf() @@ -278,11 +278,11 @@ public function testScopeDistanceSphereExcludingSelf() $this->assertNotEmpty($q->wheres); $bindings = $q->getRawBindings()['where']; $this->assertNotEmpty($bindings); - $this->assertEquals('st_distance_sphere(`point`, ST_GeomFromText(?)) <= ?', $q->wheres[0]['sql']); - $this->assertEquals('st_distance_sphere(point, ST_GeomFromText(?)) != 0', $q->wheres[1]['sql']); + $this->assertEquals('st_distance_sphere(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) <= ?', $q->wheres[0]['sql']); + $this->assertEquals('st_distance_sphere(point, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) != 0', $q->wheres[1]['sql']); $this->assertEquals('POINT(2 1)', $bindings[0]); - $this->assertEquals(10, $bindings[1]); - $this->assertEquals('POINT(2 1)', $bindings[2]); + $this->assertEquals(10, $bindings[2]); + $this->assertEquals('POINT(2 1)', $bindings[3]); } public function testScopeDistanceValue() @@ -297,7 +297,7 @@ public function testScopeDistanceValue() $this->assertNotEmpty($bindings); $this->assertEquals('*', $q->columns[0]); $this->assertInstanceOf(\Illuminate\Database\Query\Expression::class, $q->columns[1]); - $this->assertEquals('st_distance(`point`, ST_GeomFromText(?)) as distance', $q->columns[1]->getValue()); + $this->assertEquals('st_distance(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) as distance', $q->columns[1]->getValue()); $this->assertEquals('POINT(2 1)', $bindings[0]); } @@ -313,7 +313,7 @@ public function testScopeDistanceValueWithSelect() $this->assertNotEmpty($bindings); $this->assertEquals('some_column', $q->columns[0]); $this->assertInstanceOf(\Illuminate\Database\Query\Expression::class, $q->columns[1]); - $this->assertEquals('st_distance(`point`, ST_GeomFromText(?)) as distance', $q->columns[1]->getValue()); + $this->assertEquals('st_distance(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) as distance', $q->columns[1]->getValue()); $this->assertEquals('POINT(2 1)', $bindings[0]); } @@ -329,7 +329,7 @@ public function testScopeDistanceSphereValue() $this->assertNotEmpty($bindings); $this->assertEquals('*', $q->columns[0]); $this->assertInstanceOf(\Illuminate\Database\Query\Expression::class, $q->columns[1]); - $this->assertEquals('st_distance_sphere(`point`, ST_GeomFromText(?)) as distance', $q->columns[1]->getValue()); + $this->assertEquals('st_distance_sphere(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) as distance', $q->columns[1]->getValue()); $this->assertEquals('POINT(2 1)', $bindings[0]); } @@ -345,7 +345,7 @@ public function testScopeDistanceSphereValueWithSelect() $this->assertNotEmpty($bindings); $this->assertEquals('some_column', $q->columns[0]); $this->assertInstanceOf(\Illuminate\Database\Query\Expression::class, $q->columns[1]); - $this->assertEquals('st_distance_sphere(`point`, ST_GeomFromText(?)) as distance', $q->columns[1]->getValue()); + $this->assertEquals('st_distance_sphere(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) as distance', $q->columns[1]->getValue()); $this->assertEquals('POINT(2 1)', $bindings[0]); } @@ -373,7 +373,7 @@ public function testScopeComparison() $this->assertNotEmpty($q->wheres); $bindings = $q->getRawBindings()['where']; $this->assertNotEmpty($bindings); - $this->assertContains('st_within(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']); + $this->assertContains('st_within(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); } @@ -386,7 +386,7 @@ public function testScopeWithin() $this->assertNotEmpty($q->wheres); $bindings = $q->getRawBindings()['where']; $this->assertNotEmpty($bindings); - $this->assertContains('st_within(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']); + $this->assertContains('st_within(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); } @@ -399,7 +399,7 @@ public function testScopeCrosses() $this->assertNotEmpty($q->wheres); $bindings = $q->getRawBindings()['where']; $this->assertNotEmpty($bindings); - $this->assertContains('st_crosses(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']); + $this->assertContains('st_crosses(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); } @@ -412,7 +412,7 @@ public function testScopeContains() $this->assertNotEmpty($q->wheres); $bindings = $q->getRawBindings()['where']; $this->assertNotEmpty($bindings); - $this->assertContains('st_contains(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']); + $this->assertContains('st_contains(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); } @@ -425,7 +425,7 @@ public function testScopeDisjoint() $this->assertNotEmpty($q->wheres); $bindings = $q->getRawBindings()['where']; $this->assertNotEmpty($bindings); - $this->assertContains('st_disjoint(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']); + $this->assertContains('st_disjoint(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); } @@ -438,7 +438,7 @@ public function testScopeEquals() $this->assertNotEmpty($q->wheres); $bindings = $q->getRawBindings()['where']; $this->assertNotEmpty($bindings); - $this->assertContains('st_equals(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']); + $this->assertContains('st_equals(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); } @@ -451,7 +451,7 @@ public function testScopeIntersects() $this->assertNotEmpty($q->wheres); $bindings = $q->getRawBindings()['where']; $this->assertNotEmpty($bindings); - $this->assertContains('st_intersects(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']); + $this->assertContains('st_intersects(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); } @@ -464,7 +464,7 @@ public function testScopeOverlaps() $this->assertNotEmpty($q->wheres); $bindings = $q->getRawBindings()['where']; $this->assertNotEmpty($bindings); - $this->assertContains('st_overlaps(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']); + $this->assertContains('st_overlaps(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); } @@ -477,7 +477,7 @@ public function testScopeDoesTouch() $this->assertNotEmpty($q->wheres); $bindings = $q->getRawBindings()['where']; $this->assertNotEmpty($bindings); - $this->assertContains('st_touches(`point`, ST_GeomFromText(?))', $q->wheres[0]['sql']); + $this->assertContains('st_touches(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); } @@ -501,7 +501,7 @@ public function testScopeOrderByDistance() $this->assertNotEmpty($q->orders); $bindings = $q->getRawBindings()['order']; $this->assertNotEmpty($bindings); - $this->assertContains('st_distance(`point`, ST_GeomFromText(?)) asc', $q->orders[0]['sql']); + $this->assertContains('st_distance(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) asc', $q->orders[0]['sql']); $this->assertEquals('POINT(2 1)', $bindings[0]); } @@ -515,7 +515,7 @@ public function testScopeOrderByDistanceSphere() $this->assertNotEmpty($q->orders); $bindings = $q->getRawBindings()['order']; $this->assertNotEmpty($bindings); - $this->assertContains('st_distance_sphere(`point`, ST_GeomFromText(?)) asc', $q->orders[0]['sql']); + $this->assertContains('st_distance_sphere(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) asc', $q->orders[0]['sql']); $this->assertEquals('POINT(2 1)', $bindings[0]); } } diff --git a/tests/Unit/Schema/BlueprintTest.php b/tests/Unit/Schema/BlueprintTest.php index da335520..8ceda39b 100644 --- a/tests/Unit/Schema/BlueprintTest.php +++ b/tests/Unit/Schema/BlueprintTest.php @@ -8,6 +8,9 @@ class BlueprintTest extends BaseTestCase { + /** + * @var \Grimzy\LaravelMysqlSpatial\Schema\Blueprint + */ protected $blueprint; public function setUp() @@ -22,7 +25,7 @@ public function testGeometry() { $this->blueprint ->shouldReceive('addColumn') - ->with('geometry', 'col') + ->with('geometry', 'col', ['srid' => null]) ->once(); $this->blueprint->geometry('col'); @@ -42,7 +45,7 @@ public function testLinestring() { $this->blueprint ->shouldReceive('addColumn') - ->with('linestring', 'col') + ->with('linestring', 'col', ['srid' => null]) ->once(); $this->blueprint->linestring('col'); @@ -52,7 +55,7 @@ public function testPolygon() { $this->blueprint ->shouldReceive('addColumn') - ->with('polygon', 'col') + ->with('polygon', 'col', ['srid' => null]) ->once(); $this->blueprint->polygon('col'); @@ -62,7 +65,7 @@ public function testMultiPoint() { $this->blueprint ->shouldReceive('addColumn') - ->with('multipoint', 'col') + ->with('multipoint', 'col', ['srid' => null]) ->once(); $this->blueprint->multipoint('col'); @@ -72,17 +75,17 @@ public function testMultiLineString() { $this->blueprint ->shouldReceive('addColumn') - ->with('multilinestring', 'col') + ->with('multilinestring', 'col', ['srid' => null]) ->once(); $this->blueprint->multilinestring('col'); } - public function testMulltiPolygon() + public function testMultiPolygon() { $this->blueprint ->shouldReceive('addColumn') - ->with('multipolygon', 'col') + ->with('multipolygon', 'col', ['srid' => null]) ->once(); $this->blueprint->multipolygon('col'); @@ -92,9 +95,89 @@ public function testGeometryCollection() { $this->blueprint ->shouldReceive('addColumn') - ->with('geometrycollection', 'col') + ->with('geometrycollection', 'col', ['srid' => null]) ->once(); $this->blueprint->geometrycollection('col'); } + + public function testGeometryWithSrid() + { + $this->blueprint + ->shouldReceive('addColumn') + ->with('geometry', 'col', ['srid' => 4326]) + ->once(); + + $this->blueprint->geometry('col', 4326); + } + + public function testPointWithSrid() + { + $this->blueprint + ->shouldReceive('addColumn') + ->with('point', 'col', ['srid' => 4326]) + ->once(); + + $this->blueprint->point('col', 4326); + } + + public function testLinestringWithSrid() + { + $this->blueprint + ->shouldReceive('addColumn') + ->with('linestring', 'col', ['srid' => 4326]) + ->once(); + + $this->blueprint->linestring('col', 4326); + } + + public function testPolygonWithSrid() + { + $this->blueprint + ->shouldReceive('addColumn') + ->with('polygon', 'col', ['srid' => 4326]) + ->once(); + + $this->blueprint->polygon('col', 4326); + } + + public function testMultiPointWithSrid() + { + $this->blueprint + ->shouldReceive('addColumn') + ->with('multipoint', 'col', ['srid' => 4326]) + ->once(); + + $this->blueprint->multipoint('col', 4326); + } + + public function testMultiLineStringWithSrid() + { + $this->blueprint + ->shouldReceive('addColumn') + ->with('multilinestring', 'col', ['srid' => 4326]) + ->once(); + + $this->blueprint->multilinestring('col', 4326); + } + + public function testMultiPolygonWithSrid() + { + $this->blueprint + ->shouldReceive('addColumn') + ->with('multipolygon', 'col', ['srid' => 4326]) + ->once(); + + $this->blueprint->multipolygon('col', 4326); + } + + public function testGeometryCollectionWithSrid() + { + $this->blueprint + ->shouldReceive('addColumn') + ->with('geometrycollection', 'col', ['srid' => 4326]) + ->once(); + + $this->blueprint->geometrycollection('col', 4326); + } } diff --git a/tests/Unit/Schema/Grammars/MySqlGrammarTest.php b/tests/Unit/Schema/Grammars/MySqlGrammarTest.php index 4773a2ea..66ba7641 100644 --- a/tests/Unit/Schema/Grammars/MySqlGrammarTest.php +++ b/tests/Unit/Schema/Grammars/MySqlGrammarTest.php @@ -13,7 +13,7 @@ public function testAddingGeometry() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertEquals(1, count($statements)); - $this->assertContains('GEOMETRY', $statements[0]); + $this->assertEquals('alter table `test` add `foo` GEOMETRY not null', $statements[0]); } public function testAddingPoint() @@ -23,7 +23,7 @@ public function testAddingPoint() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertEquals(1, count($statements)); - $this->assertContains('POINT', $statements[0]); + $this->assertEquals('alter table `test` add `foo` POINT not null', $statements[0]); } public function testAddingLinestring() @@ -33,7 +33,7 @@ public function testAddingLinestring() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertEquals(1, count($statements)); - $this->assertContains('LINESTRING', $statements[0]); + $this->assertEquals('alter table `test` add `foo` LINESTRING not null', $statements[0]); } public function testAddingPolygon() @@ -43,7 +43,7 @@ public function testAddingPolygon() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertEquals(1, count($statements)); - $this->assertContains('POLYGON', $statements[0]); + $this->assertEquals('alter table `test` add `foo` POLYGON not null', $statements[0]); } public function testAddingMultipoint() @@ -53,7 +53,7 @@ public function testAddingMultipoint() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertEquals(1, count($statements)); - $this->assertContains('MULTIPOINT', $statements[0]); + $this->assertEquals('alter table `test` add `foo` MULTIPOINT not null', $statements[0]); } public function testAddingMultiLinestring() @@ -63,7 +63,7 @@ public function testAddingMultiLinestring() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertEquals(1, count($statements)); - $this->assertContains('MULTILINESTRING', $statements[0]); + $this->assertEquals('alter table `test` add `foo` MULTILINESTRING not null', $statements[0]); } public function testAddingMultiPolygon() @@ -73,7 +73,7 @@ public function testAddingMultiPolygon() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertEquals(1, count($statements)); - $this->assertContains('MULTIPOLYGON', $statements[0]); + $this->assertEquals('alter table `test` add `foo` MULTIPOLYGON not null', $statements[0]); } public function testAddingGeometryCollection() @@ -83,7 +83,87 @@ public function testAddingGeometryCollection() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertEquals(1, count($statements)); - $this->assertContains('GEOMETRYCOLLECTION', $statements[0]); + $this->assertEquals('alter table `test` add `foo` GEOMETRYCOLLECTION not null', $statements[0]); + } + + public function testAddingGeometryWithSrid() + { + $blueprint = new Blueprint('test'); + $blueprint->geometry('foo', 4326); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertEquals(1, count($statements)); + $this->assertEquals('alter table `test` add `foo` GEOMETRY not null srid 4326', $statements[0]); + } + + public function testAddingPointWithSrid() + { + $blueprint = new Blueprint('test'); + $blueprint->point('foo', 4326); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertEquals(1, count($statements)); + $this->assertEquals('alter table `test` add `foo` POINT not null srid 4326', $statements[0]); + } + + public function testAddingLinestringWithSrid() + { + $blueprint = new Blueprint('test'); + $blueprint->linestring('foo', 4326); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertEquals(1, count($statements)); + $this->assertEquals('alter table `test` add `foo` LINESTRING not null srid 4326', $statements[0]); + } + + public function testAddingPolygonWithSrid() + { + $blueprint = new Blueprint('test'); + $blueprint->polygon('foo', 4326); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertEquals(1, count($statements)); + $this->assertEquals('alter table `test` add `foo` POLYGON not null srid 4326', $statements[0]); + } + + public function testAddingMultipointWithSrid() + { + $blueprint = new Blueprint('test'); + $blueprint->multipoint('foo', 4326); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertEquals(1, count($statements)); + $this->assertEquals('alter table `test` add `foo` MULTIPOINT not null srid 4326', $statements[0]); + } + + public function testAddingMultiLinestringWithSrid() + { + $blueprint = new Blueprint('test'); + $blueprint->multilinestring('foo', 4326); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertEquals(1, count($statements)); + $this->assertEquals('alter table `test` add `foo` MULTILINESTRING not null srid 4326', $statements[0]); + } + + public function testAddingMultiPolygonWithSrid() + { + $blueprint = new Blueprint('test'); + $blueprint->multipolygon('foo', 4326); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertEquals(1, count($statements)); + $this->assertEquals('alter table `test` add `foo` MULTIPOLYGON not null srid 4326', $statements[0]); + } + + public function testAddingGeometryCollectionWithSrid() + { + $blueprint = new Blueprint('test'); + $blueprint->geometrycollection('foo', 4326); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertEquals(1, count($statements)); + $this->assertEquals('alter table `test` add `foo` GEOMETRYCOLLECTION not null srid 4326', $statements[0]); } public function testAddRemoveSpatialIndex() @@ -94,7 +174,7 @@ public function testAddRemoveSpatialIndex() $addStatements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertEquals(2, count($addStatements)); - $this->assertContains('alter table `test` add spatial `test_foo_spatial`(`foo`)', $addStatements[1]); + $this->assertEquals('alter table `test` add spatial `test_foo_spatial`(`foo`)', $addStatements[1]); $blueprint->dropSpatialIndex(['foo']); $blueprint->dropSpatialIndex('test_foo_spatial'); @@ -102,8 +182,8 @@ public function testAddRemoveSpatialIndex() $expectedSql = 'alter table `test` drop index `test_foo_spatial`'; $this->assertEquals(5, count($dropStatements)); - $this->assertContains($expectedSql, $dropStatements[3]); - $this->assertContains($expectedSql, $dropStatements[4]); + $this->assertEquals($expectedSql, $dropStatements[3]); + $this->assertEquals($expectedSql, $dropStatements[4]); } /** diff --git a/tests/Unit/Types/PointTest.php b/tests/Unit/Types/PointTest.php index 257f6a45..518a8a56 100644 --- a/tests/Unit/Types/PointTest.php +++ b/tests/Unit/Types/PointTest.php @@ -6,18 +6,19 @@ class PointTest extends BaseTestCase { public function testFromWKT() { - $point = Point::fromWKT('POINT(1 2)'); + $point = Point::fromWKT('POINT(1 2)', 4326); $this->assertInstanceOf(Point::class, $point); - $this->assertEquals(2, $point->getLat()); - $this->assertEquals(1, $point->getLng()); + $this->assertSame(2.0, $point->getLat()); + $this->assertSame(1.0, $point->getLng()); + $this->assertSame(4326, $point->getSrid()); } public function testToWKT() { - $point = new Point(1, 2); + $point = new Point(1, 2, 4326); - $this->assertEquals('POINT(2 1)', $point->toWKT()); + $this->assertSame('POINT(2 1)', $point->toWKT()); } public function testGettersAndSetters() @@ -25,30 +26,35 @@ public function testGettersAndSetters() $point = new Point(1, 2); $this->assertSame(1.0, $point->getLat()); $this->assertSame(2.0, $point->getLng()); + $this->assertSame(0, $point->getSrid()); $point->setLat('3'); $point->setLng('4'); + $point->setSrid(100); $this->assertSame(3.0, $point->getLat()); $this->assertSame(4.0, $point->getLng()); + $this->assertSame(100, $point->getSrid()); } public function testPair() { - $point = Point::fromPair('1.5 2'); + $point = Point::fromPair('1.5 2', 4326); $this->assertSame(1.5, $point->getLng()); $this->assertSame(2.0, $point->getLat()); + $this->assertSame(4326, $point->getSrid()); $this->assertSame('1.5 2', $point->toPair()); } public function testToString() { - $point = Point::fromString('1.3 2'); + $point = Point::fromString('1.3 2', 4326); $this->assertSame(1.3, $point->getLng()); $this->assertSame(2.0, $point->getLat()); + $this->assertSame(4326, $point->getSrid()); $this->assertEquals('1.3 2', (string) $point); } @@ -57,13 +63,16 @@ public function testFromJson() { $point = Point::fromJson('{"type":"Point","coordinates":[3.4,1.2]}'); $this->assertInstanceOf(Point::class, $point); - $this->assertEquals(1.2, $point->getLat()); - $this->assertEquals(3.4, $point->getLng()); + $this->assertSame(1.2, $point->getLat()); + $this->assertSame(3.4, $point->getLng()); } public function testInvalidGeoJsonException() { - $this->setExpectedException(\Grimzy\LaravelMysqlSpatial\Exceptions\InvalidGeoJsonException::class); + $this->assertException( + \Grimzy\LaravelMysqlSpatial\Exceptions\InvalidGeoJsonException::class, + 'Expected GeoJson\Geometry\Point, got GeoJson\Geometry\LineString' + ); Point::fromJson('{"type": "LineString","coordinates":[[1,1],[2,2]]}'); } diff --git a/tests/Unit/Types/PolygonTest.php b/tests/Unit/Types/PolygonTest.php index de923c2f..aaab437b 100644 --- a/tests/Unit/Types/PolygonTest.php +++ b/tests/Unit/Types/PolygonTest.php @@ -20,12 +20,12 @@ protected function setUp() ] ); - $this->polygon = new Polygon([$collection]); + $this->polygon = new Polygon([$collection], 4326); } public function testFromWKT() { - $polygon = Polygon::fromWKT('POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))'); + $polygon = Polygon::fromWKT('POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))', 4326); $this->assertInstanceOf(Polygon::class, $polygon); $this->assertEquals(2, $polygon->count()); @@ -56,7 +56,10 @@ public function testFromJson() public function testInvalidGeoJsonException() { - $this->setExpectedException(\Grimzy\LaravelMysqlSpatial\Exceptions\InvalidGeoJsonException::class); + $this->assertException( + \Grimzy\LaravelMysqlSpatial\Exceptions\InvalidGeoJsonException::class, + 'Expected GeoJson\Geometry\Polygon, got GeoJson\Geometry\Point' + ); Polygon::fromJson('{"type":"Point","coordinates":[3.4,1.2]}'); }