Skip to content

Commit 862f599

Browse files
Support Geometry class extension (#125)
* 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]>
1 parent a6bae05 commit 862f599

28 files changed

+802
-42
lines changed

.run/Test - MySQL 8.0.run.xml .run/Test - MySQL.run.xml

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
<component name="ProjectRunConfigurationManager">
2-
<configuration default="false" name="Test - MySQL 8.0" type="PestRunConfigurationType">
2+
<configuration default="false" name="Test - MySQL" type="PestRunConfigurationType">
33
<CommandLine>
44
<envs>
5+
<env name="DB_COLLATION" value="utf8mb4_unicode_ci"/>
6+
<env name="DB_CONNECTION" value="mysql"/>
57
<env name="DB_PORT" value="3307"/>
68
</envs>
79
</CommandLine>

CHANGELOG.md

-12
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,6 @@
22

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

5-
## v4.2.1 - 2024-04-02
6-
7-
### What's Changed
8-
9-
* `Geometry::fromArray` added optional`$srid` paramter by @ju-gow in https://github.com/MatanYadaev/laravel-eloquent-spatial/pull/118
10-
11-
### New Contributors
12-
13-
* @ju-gow made their first contribution in https://github.com/MatanYadaev/laravel-eloquent-spatial/pull/118
14-
15-
**Full Changelog**: https://github.com/MatanYadaev/laravel-eloquent-spatial/compare/4.2.0...4.2.1
16-
175
## v4.2.0 - 2024-03-13
186

197
### What's Changed

README.md

+79
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ For more comprehensive documentation on the API, please refer to the [API](API.m
152152

153153
## Extension
154154

155+
### Extend Geometry class with macros
156+
155157
You can add new methods to the `Geometry` class through macros.
156158

157159
Here's an example of how to register a macro in your service provider's `boot` method:
@@ -177,6 +179,83 @@ $londonEyePoint = new Point(51.5032973, -0.1217424);
177179
echo $londonEyePoint->getName(); // Point
178180
```
179181

182+
### Extend with custom geometry classes
183+
184+
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.
185+
186+
1. Create a custom geometry class that extends the base geometry class.
187+
188+
```php
189+
use MatanYadaev\EloquentSpatial\Objects\Point;
190+
191+
class ExtendedPoint extends Point
192+
{
193+
public function toCustomArray(): array
194+
{
195+
return 'coordinates' => [
196+
'latitude' => $this->latitude,
197+
'longitude' => $this->longitude
198+
]
199+
}
200+
}
201+
```
202+
203+
2. Update the geometry class mapping in a service provider file.
204+
205+
```php
206+
use App\ValueObjects\ExtendedPoint;
207+
use Illuminate\Support\ServiceProvider;
208+
use MatanYadaev\EloquentSpatial\EloquentSpatial;
209+
210+
class AppServiceProvider extends ServiceProvider
211+
{
212+
public function boot(): void
213+
{
214+
EloquentSpatial::usePoint(ExtendedPoint::class);
215+
}
216+
}
217+
```
218+
219+
3. Update your model to use the custom geometry class in the `$casts` property or `casts()` method.
220+
221+
```php
222+
use App\ValueObjects\ExtendedPoint;
223+
use Illuminate\Database\Eloquent\Model;
224+
use MatanYadaev\EloquentSpatial\Traits\HasSpatial;
225+
226+
class Place extends Model
227+
{
228+
use HasSpatial;
229+
230+
protected $casts = [
231+
'coordinates' => ExtendedPoint::class,
232+
];
233+
234+
// Or:
235+
236+
protected function casts(): array
237+
{
238+
return [
239+
'coordinates' => ExtendedPoint::class,
240+
];
241+
}
242+
}
243+
```
244+
245+
4. Use the custom geometry class in your code.
246+
247+
```php
248+
use App\Models\Location;
249+
use App\ValueObjects\ExtendedPoint;
250+
251+
$place = Place::create([
252+
'name' => 'London Eye',
253+
'coordinates' => new ExtendedPoint(51.5032973, -0.1217424),
254+
]);
255+
256+
echo $place->coordinates->toCustomArray(); // ['longitude' => -0.1217424, 'latitude' => 51.5032973]
257+
```
258+
180259
## Development
181260

182261
Here are some useful commands for development:

phpstan.neon

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ parameters:
1818
-
1919
message: '#Call to an undefined method Pest\\Expectation\<.+\>\:\:toBe(InstanceOf)?On(Postgres|Mysql)\(\)#'
2020
path: tests/*.php
21+
-
22+
identifier: missingType.generics
2123

2224
level: max
2325
checkMissingIterableValueType: true
24-
checkGenericClassInNonGenericObjectType: false

src/EloquentSpatial.php

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MatanYadaev\EloquentSpatial;
6+
7+
use MatanYadaev\EloquentSpatial\Objects\GeometryCollection;
8+
use MatanYadaev\EloquentSpatial\Objects\LineString;
9+
use MatanYadaev\EloquentSpatial\Objects\MultiLineString;
10+
use MatanYadaev\EloquentSpatial\Objects\MultiPoint;
11+
use MatanYadaev\EloquentSpatial\Objects\MultiPolygon;
12+
use MatanYadaev\EloquentSpatial\Objects\Point;
13+
use MatanYadaev\EloquentSpatial\Objects\Polygon;
14+
15+
class EloquentSpatial
16+
{
17+
/** @var class-string<GeometryCollection> */
18+
public static string $geometryCollection = GeometryCollection::class;
19+
20+
/** @var class-string<LineString> */
21+
public static string $lineString = LineString::class;
22+
23+
/** @var class-string<MultiLineString> */
24+
public static string $multiLineString = MultiLineString::class;
25+
26+
/** @var class-string<MultiPoint> */
27+
public static string $multiPoint = MultiPoint::class;
28+
29+
/** @var class-string<MultiPolygon> */
30+
public static string $multiPolygon = MultiPolygon::class;
31+
32+
/** @var class-string<Point> */
33+
public static string $point = Point::class;
34+
35+
/** @var class-string<Polygon> */
36+
public static string $polygon = Polygon::class;
37+
38+
/**
39+
* @param class-string<GeometryCollection> $class
40+
*/
41+
public static function useGeometryCollection(string $class): string
42+
{
43+
static::$geometryCollection = $class;
44+
45+
return static::$geometryCollection;
46+
}
47+
48+
/**
49+
* @param class-string<LineString> $class
50+
*/
51+
public static function useLineString(string $class): string
52+
{
53+
static::$lineString = $class;
54+
55+
return static::$lineString;
56+
}
57+
58+
/**
59+
* @param class-string<MultiLineString> $class
60+
*/
61+
public static function useMultiLineString(string $class): string
62+
{
63+
static::$multiLineString = $class;
64+
65+
return static::$multiLineString;
66+
}
67+
68+
/**
69+
* @param class-string<MultiPoint> $class
70+
*/
71+
public static function useMultiPoint(string $class): string
72+
{
73+
static::$multiPoint = $class;
74+
75+
return static::$multiPoint;
76+
}
77+
78+
/**
79+
* @param class-string<MultiPolygon> $class
80+
*/
81+
public static function useMultiPolygon(string $class): string
82+
{
83+
static::$multiPolygon = $class;
84+
85+
return static::$multiPolygon;
86+
}
87+
88+
/**
89+
* @param class-string<Point> $class
90+
*/
91+
public static function usePoint(string $class): string
92+
{
93+
static::$point = $class;
94+
95+
return static::$point;
96+
}
97+
98+
/**
99+
* @param class-string<Polygon> $class
100+
*/
101+
public static function usePolygon(string $class): string
102+
{
103+
static::$polygon = $class;
104+
105+
return static::$polygon;
106+
}
107+
}

src/Factory.php

+7-14
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,6 @@
1010
use InvalidArgumentException;
1111
use LineString as geoPHPLineString;
1212
use MatanYadaev\EloquentSpatial\Objects\Geometry;
13-
use MatanYadaev\EloquentSpatial\Objects\GeometryCollection;
14-
use MatanYadaev\EloquentSpatial\Objects\LineString;
15-
use MatanYadaev\EloquentSpatial\Objects\MultiLineString;
16-
use MatanYadaev\EloquentSpatial\Objects\MultiPoint;
17-
use MatanYadaev\EloquentSpatial\Objects\MultiPolygon;
18-
use MatanYadaev\EloquentSpatial\Objects\Point;
19-
use MatanYadaev\EloquentSpatial\Objects\Polygon;
2013
use MultiLineString as geoPHPMultiLineString;
2114
use MultiPoint as geoPHPMultiPoint;
2215
use MultiPolygon as geoPHPMultiPolygon;
@@ -48,7 +41,7 @@ protected static function createFromGeometry(geoPHPGeometry $geometry): Geometry
4841
throw new InvalidArgumentException('Invalid spatial value');
4942
}
5043

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

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

6053
if ($geometry::class === geoPHPMultiPoint::class) {
61-
return new MultiPoint($components, $srid);
54+
return new EloquentSpatial::$multiPoint($components, $srid);
6255
}
6356

6457
if ($geometry::class === geoPHPLineString::class) {
65-
return new LineString($components, $srid);
58+
return new EloquentSpatial::$lineString($components, $srid);
6659
}
6760

6861
if ($geometry::class === geoPHPPolygon::class) {
69-
return new Polygon($components, $srid);
62+
return new EloquentSpatial::$polygon($components, $srid);
7063
}
7164

7265
if ($geometry::class === geoPHPMultiLineString::class) {
73-
return new MultiLineString($components, $srid);
66+
return new EloquentSpatial::$multiLineString($components, $srid);
7467
}
7568

7669
if ($geometry::class === geoPHPMultiPolygon::class) {
77-
return new MultiPolygon($components, $srid);
70+
return new EloquentSpatial::$multiPolygon($components, $srid);
7871
}
7972

80-
return new GeometryCollection($components, $srid);
73+
return new EloquentSpatial::$geometryCollection($components, $srid);
8174
}
8275
}

src/GeometryCast.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public function set($model, string $key, $value, array $attributes): ?Expression
6666
return $value;
6767
}
6868

69-
if (! ($value instanceof $this->className)) {
69+
if (! ($value instanceof $this->className) || get_class($value) !== $this->className) {
7070
$geometryType = is_object($value) ? $value::class : gettype($value);
7171
throw new InvalidArgumentException(
7272
sprintf('Expected %s, %s given.', $this->className, $geometryType)

src/GeometryExpression.php

+1-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010
/** @codeCoverageIgnore */
1111
class GeometryExpression
1212
{
13-
public function __construct(readonly private string $expression)
14-
{
15-
}
13+
public function __construct(readonly private string $expression) {}
1614

1715
public function normalize(ConnectionInterface $connection): string
1816
{

tests/HasSpatialTest.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@
447447
});
448448

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

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

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

@@ -469,7 +469,7 @@
469469
});
470470

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

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

0 commit comments

Comments
 (0)