Skip to content

Commit

Permalink
Support Geometry class extension (#125)
Browse files Browse the repository at this point in the history
* add ability to extend the geometry classes with custom geometry classes

* added tests

* fix phpstan issues

* updated readme

* updated readme with custom class for casts

* fixed pint issues

* added migration for L10

* fixes

* fixes

---------

Co-authored-by: Matan Yadaev <[email protected]>
  • Loading branch information
jobverplanke and MatanYadaev authored Jul 24, 2024
1 parent a6bae05 commit 862f599
Show file tree
Hide file tree
Showing 28 changed files with 802 additions and 42 deletions.
4 changes: 3 additions & 1 deletion .run/Test - MySQL 8.0.run.xml → .run/Test - MySQL.run.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Test - MySQL 8.0" type="PestRunConfigurationType">
<configuration default="false" name="Test - MySQL" type="PestRunConfigurationType">
<CommandLine>
<envs>
<env name="DB_COLLATION" value="utf8mb4_unicode_ci"/>
<env name="DB_CONNECTION" value="mysql"/>
<env name="DB_PORT" value="3307"/>
</envs>
</CommandLine>
Expand Down
12 changes: 0 additions & 12 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,6 @@

All notable changes to `laravel-eloquent-spatial` will be documented in this file.

## v4.2.1 - 2024-04-02

### What's Changed

* `Geometry::fromArray` added optional`$srid` paramter by @ju-gow in https://github.com/MatanYadaev/laravel-eloquent-spatial/pull/118

### New Contributors

* @ju-gow made their first contribution in https://github.com/MatanYadaev/laravel-eloquent-spatial/pull/118

**Full Changelog**: https://github.com/MatanYadaev/laravel-eloquent-spatial/compare/4.2.0...4.2.1

## v4.2.0 - 2024-03-13

### What's Changed
Expand Down
79 changes: 79 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ For more comprehensive documentation on the API, please refer to the [API](API.m
## Extension
### Extend Geometry class with macros
You can add new methods to the `Geometry` class through macros.
Here's an example of how to register a macro in your service provider's `boot` method:
Expand All @@ -177,6 +179,83 @@ $londonEyePoint = new Point(51.5032973, -0.1217424);
echo $londonEyePoint->getName(); // Point
```
### Extend with custom geometry classes
You can extend the geometry classes by creating custom geometry classes and add functionality. You can also override existing methods, although it is not recommended, as it may lead to unexpected behavior.
1. Create a custom geometry class that extends the base geometry class.
```php
use MatanYadaev\EloquentSpatial\Objects\Point;
class ExtendedPoint extends Point
{
public function toCustomArray(): array
{
return 'coordinates' => [
'latitude' => $this->latitude,
'longitude' => $this->longitude
]
}
}
```
2. Update the geometry class mapping in a service provider file.
```php
use App\ValueObjects\ExtendedPoint;
use Illuminate\Support\ServiceProvider;
use MatanYadaev\EloquentSpatial\EloquentSpatial;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
EloquentSpatial::usePoint(ExtendedPoint::class);
}
}
```
3. Update your model to use the custom geometry class in the `$casts` property or `casts()` method.
```php
use App\ValueObjects\ExtendedPoint;
use Illuminate\Database\Eloquent\Model;
use MatanYadaev\EloquentSpatial\Traits\HasSpatial;
class Place extends Model
{
use HasSpatial;
protected $casts = [
'coordinates' => ExtendedPoint::class,
];
// Or:
protected function casts(): array
{
return [
'coordinates' => ExtendedPoint::class,
];
}
}
```
4. Use the custom geometry class in your code.
```php
use App\Models\Location;
use App\ValueObjects\ExtendedPoint;
$place = Place::create([
'name' => 'London Eye',
'coordinates' => new ExtendedPoint(51.5032973, -0.1217424),
]);
echo $place->coordinates->toCustomArray(); // ['longitude' => -0.1217424, 'latitude' => 51.5032973]
```
## Development
Here are some useful commands for development:
Expand Down
3 changes: 2 additions & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ parameters:
-
message: '#Call to an undefined method Pest\\Expectation\<.+\>\:\:toBe(InstanceOf)?On(Postgres|Mysql)\(\)#'
path: tests/*.php
-
identifier: missingType.generics

level: max
checkMissingIterableValueType: true
checkGenericClassInNonGenericObjectType: false
107 changes: 107 additions & 0 deletions src/EloquentSpatial.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

declare(strict_types=1);

namespace MatanYadaev\EloquentSpatial;

use MatanYadaev\EloquentSpatial\Objects\GeometryCollection;
use MatanYadaev\EloquentSpatial\Objects\LineString;
use MatanYadaev\EloquentSpatial\Objects\MultiLineString;
use MatanYadaev\EloquentSpatial\Objects\MultiPoint;
use MatanYadaev\EloquentSpatial\Objects\MultiPolygon;
use MatanYadaev\EloquentSpatial\Objects\Point;
use MatanYadaev\EloquentSpatial\Objects\Polygon;

class EloquentSpatial
{
/** @var class-string<GeometryCollection> */
public static string $geometryCollection = GeometryCollection::class;

/** @var class-string<LineString> */
public static string $lineString = LineString::class;

/** @var class-string<MultiLineString> */
public static string $multiLineString = MultiLineString::class;

/** @var class-string<MultiPoint> */
public static string $multiPoint = MultiPoint::class;

/** @var class-string<MultiPolygon> */
public static string $multiPolygon = MultiPolygon::class;

/** @var class-string<Point> */
public static string $point = Point::class;

/** @var class-string<Polygon> */
public static string $polygon = Polygon::class;

/**
* @param class-string<GeometryCollection> $class
*/
public static function useGeometryCollection(string $class): string
{
static::$geometryCollection = $class;

return static::$geometryCollection;
}

/**
* @param class-string<LineString> $class
*/
public static function useLineString(string $class): string
{
static::$lineString = $class;

return static::$lineString;
}

/**
* @param class-string<MultiLineString> $class
*/
public static function useMultiLineString(string $class): string
{
static::$multiLineString = $class;

return static::$multiLineString;
}

/**
* @param class-string<MultiPoint> $class
*/
public static function useMultiPoint(string $class): string
{
static::$multiPoint = $class;

return static::$multiPoint;
}

/**
* @param class-string<MultiPolygon> $class
*/
public static function useMultiPolygon(string $class): string
{
static::$multiPolygon = $class;

return static::$multiPolygon;
}

/**
* @param class-string<Point> $class
*/
public static function usePoint(string $class): string
{
static::$point = $class;

return static::$point;
}

/**
* @param class-string<Polygon> $class
*/
public static function usePolygon(string $class): string
{
static::$polygon = $class;

return static::$polygon;
}
}
21 changes: 7 additions & 14 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,6 @@
use InvalidArgumentException;
use LineString as geoPHPLineString;
use MatanYadaev\EloquentSpatial\Objects\Geometry;
use MatanYadaev\EloquentSpatial\Objects\GeometryCollection;
use MatanYadaev\EloquentSpatial\Objects\LineString;
use MatanYadaev\EloquentSpatial\Objects\MultiLineString;
use MatanYadaev\EloquentSpatial\Objects\MultiPoint;
use MatanYadaev\EloquentSpatial\Objects\MultiPolygon;
use MatanYadaev\EloquentSpatial\Objects\Point;
use MatanYadaev\EloquentSpatial\Objects\Polygon;
use MultiLineString as geoPHPMultiLineString;
use MultiPoint as geoPHPMultiPoint;
use MultiPolygon as geoPHPMultiPolygon;
Expand Down Expand Up @@ -48,7 +41,7 @@ protected static function createFromGeometry(geoPHPGeometry $geometry): Geometry
throw new InvalidArgumentException('Invalid spatial value');
}

return new Point($geometry->coords[1], $geometry->coords[0], $srid);
return new EloquentSpatial::$point($geometry->coords[1], $geometry->coords[0], $srid);
}

/** @var geoPHPGeometryCollection $geometry */
Expand All @@ -58,25 +51,25 @@ protected static function createFromGeometry(geoPHPGeometry $geometry): Geometry
});

if ($geometry::class === geoPHPMultiPoint::class) {
return new MultiPoint($components, $srid);
return new EloquentSpatial::$multiPoint($components, $srid);
}

if ($geometry::class === geoPHPLineString::class) {
return new LineString($components, $srid);
return new EloquentSpatial::$lineString($components, $srid);
}

if ($geometry::class === geoPHPPolygon::class) {
return new Polygon($components, $srid);
return new EloquentSpatial::$polygon($components, $srid);
}

if ($geometry::class === geoPHPMultiLineString::class) {
return new MultiLineString($components, $srid);
return new EloquentSpatial::$multiLineString($components, $srid);
}

if ($geometry::class === geoPHPMultiPolygon::class) {
return new MultiPolygon($components, $srid);
return new EloquentSpatial::$multiPolygon($components, $srid);
}

return new GeometryCollection($components, $srid);
return new EloquentSpatial::$geometryCollection($components, $srid);
}
}
2 changes: 1 addition & 1 deletion src/GeometryCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function set($model, string $key, $value, array $attributes): ?Expression
return $value;
}

if (! ($value instanceof $this->className)) {
if (! ($value instanceof $this->className) || get_class($value) !== $this->className) {
$geometryType = is_object($value) ? $value::class : gettype($value);
throw new InvalidArgumentException(
sprintf('Expected %s, %s given.', $this->className, $geometryType)
Expand Down
4 changes: 1 addition & 3 deletions src/GeometryExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
/** @codeCoverageIgnore */
class GeometryExpression
{
public function __construct(readonly private string $expression)
{
}
public function __construct(readonly private string $expression) {}

public function normalize(ConnectionInterface $connection): string
{
Expand Down
6 changes: 3 additions & 3 deletions tests/HasSpatialTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@
});

it('toExpressionString can handle a Expression input', function (): void {
$spatialBuilder = new TestPlace();
$spatialBuilder = new TestPlace;
$toExpressionStringMethod = (new ReflectionClass($spatialBuilder))->getMethod('toExpressionString');

$result = $toExpressionStringMethod->invoke($spatialBuilder, DB::raw('POINT(longitude, latitude)'));
Expand All @@ -456,7 +456,7 @@
});

it('toExpressionString can handle a Geometry input', function (): void {
$testPlace = new TestPlace();
$testPlace = new TestPlace;
$toExpressionStringMethod = (new ReflectionClass($testPlace))->getMethod('toExpressionString');
$polygon = Polygon::fromJson('{"type":"Polygon","coordinates":[[[-1,-1],[1,-1],[1,1],[-1,1],[-1,-1]]]}');

Expand All @@ -469,7 +469,7 @@
});

it('toExpressionString can handle a string input', function (): void {
$spatialBuilder = new TestPlace();
$spatialBuilder = new TestPlace;
$toExpressionStringMethod = (new ReflectionClass($spatialBuilder))->getMethod('toExpressionString');

$result = $toExpressionStringMethod->invoke($spatialBuilder, 'test_places.point');
Expand Down
Loading

0 comments on commit 862f599

Please sign in to comment.