Skip to content

Commit

Permalink
Merge pull request #5 from tarfin-labs/optimizations
Browse files Browse the repository at this point in the history
Optimizations for Point class and HasSpatial trait.
  • Loading branch information
tkaratug committed Jun 24, 2022
2 parents 818edf4 + 365e337 commit 67239f3
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 79 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

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

## 1.2.0 - 2012-02-08
## 1.3.0 - 2022-06-24
- `toWkt()` method added to `Point` class for getting the coordinates as well known text.
- `toPair()` method added to `Point` class for getting the coordinates as pair.
- `toArray()` method added to `Point` class for getting the coordinates as array.

## 1.2.0 - 2022-02-08
- Laravel 9 support added.

# 1.1.2 - 2022-03-08
Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,33 @@ Address::create([
]);
```

#### Usage in Resource:
To get an array representation of a location-casted field from a resource, you can return `parent::toArray($request)`.

If you need to return a custom array from a resource, you can use the `toArray()` method of the `Point` object.

```php
class LocationResource extends JsonResource
{
public function toArray($request)
{
return [
'location' => $this->location->toArray(),
];
}
}
```

Either way, you will get the following output for the location casted field:

```json
{
"lat": 25.45634,
"lng": 35.54331,
"srid": 4326
}
```

### Testing

```bash
Expand Down
16 changes: 7 additions & 9 deletions src/Casts/LocationCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace TarfinLabs\LaravelSpatial\Casts;

use Exception;
use InvalidArgumentException;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Contracts\Database\Eloquent\SerializesCastableAttributes;
use Illuminate\Database\Query\Expression;
Expand Down Expand Up @@ -35,24 +35,22 @@ public function get($model, string $key, $value, array $attributes): ?Point
public function set($model, string $key, $value, array $attributes): Expression
{
if (!$value instanceof Point) {
throw new Exception(message: 'The '.$key.' field must be instance of '.Point::class);
throw new InvalidArgumentException(
sprintf('The %s field must be instance of %s', $key, Point::class)
);
}

if ($value->getSrid() > 0) {
return DB::raw(
value: "ST_GeomFromText('POINT({$value->getLng()} {$value->getLat()})', {$value->getSrid()}, 'axis-order=long-lat')"
value: "ST_GeomFromText('{$value->toWkt()}', {$value->getSrid()}, 'axis-order=long-lat')"
);
}

return DB::raw(value: "ST_GeomFromText('POINT({$value->getLng()} {$value->getLat()})')");
return DB::raw(value: "ST_GeomFromText('{$value->toWkt()}')");
}

public function serialize($model, string $key, $value, array $attributes): array
{
return [
'lat' => $value->getLat(),
'lng' => $value->getLng(),
'srid' => $value->getSrid(),
];
return $value->toArray();
}
}
57 changes: 30 additions & 27 deletions src/Traits/HasSpatial.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,43 +18,46 @@ public function scopeSelectDistanceTo(Builder $query, string $column, Point $poi
$query->select('*');
}

$query->selectRaw("ST_Distance(
ST_SRID({$column}, ?),
ST_SRID(Point(?, ?), ?)
) as distance", [
$point->getSrid(),
$point->getLng(),
$point->getLat(),
$point->getSrid(),
]);
$query->selectRaw(
"ST_Distance(ST_SRID({$column}, ?), ST_SRID(Point(?, ?), ?)) as distance",
[
$point->getSrid(),
$point->getLng(),
$point->getLat(),
$point->getSrid(),
]
);
}

public function scopeWithinDistanceTo(Builder $query, string $column, Point $point, int $distance): void
{
$query->whereRaw("ST_Distance(
ST_SRID({$column}, ?),
ST_SRID(Point(?, ?), ?)
) <= ?", [...[
$point->getSrid(),
$point->getLng(),
$point->getLat(),
$point->getSrid(),
], $distance]);
$query->whereRaw(
"ST_Distance(ST_SRID({$column}, ?), ST_SRID(Point(?, ?), ?)) <= ?",
[
...[
$point->getSrid(),
$point->getLng(),
$point->getLat(),
$point->getSrid(),
],
$distance,
]
);
}

public function scopeOrderByDistanceTo(Builder $query, string $column, Point $point, string $direction = 'asc'): void
{
$direction = strtolower($direction) === 'asc' ? 'asc' : 'desc';

$query->orderByRaw("ST_Distance(
ST_SRID({$column}, ?),
ST_SRID(Point(?, ?), ?)
) ".$direction, [
$point->getSrid(),
$point->getLng(),
$point->getLat(),
$point->getSrid(),
]);
$query->orderByRaw(
"ST_Distance(ST_SRID({$column}, ?), ST_SRID(Point(?, ?), ?)) " . $direction,
[
$point->getSrid(),
$point->getLng(),
$point->getLat(),
$point->getSrid(),
]
);
}

public function newQuery(): Builder
Expand Down
19 changes: 19 additions & 0 deletions src/Types/Point.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,23 @@ public function getSrid(): int
{
return $this->srid;
}

public function toWkt(): string
{
return sprintf('POINT(%s)', $this->toPair());
}

public function toPair(): string
{
return "{$this->getLng()} {$this->getLat()}";
}

public function toArray(): array
{
return [
'lat' => $this->lat,
'lng' => $this->lng,
'srid' => $this->srid,
];
}
}
45 changes: 9 additions & 36 deletions tests/HasSpatialTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@

class HasSpatialTest extends TestCase
{
/**
* @test
* @see
*/
/** @test */
public function it_generates_sql_query_for_selectDistanceTo_scope(): void
{
// Arrange
Expand All @@ -22,18 +19,12 @@ public function it_generates_sql_query_for_selectDistanceTo_scope(): void

// Assert
$this->assertEquals(
expected: "select *, CONCAT(ST_AsText(addresses.$castedAttr, 'axis-order=long-lat'), ',', ST_SRID(addresses.$castedAttr)) as $castedAttr, ST_Distance(
ST_SRID($castedAttr, ?),
ST_SRID(Point(?, ?), ?)
) as distance from `addresses`",
expected: "select *, CONCAT(ST_AsText(addresses.$castedAttr, 'axis-order=long-lat'), ',', ST_SRID(addresses.$castedAttr)) as $castedAttr, ST_Distance(ST_SRID($castedAttr, ?), ST_SRID(Point(?, ?), ?)) as distance from `addresses`",
actual: $query->toSql()
);
}

/**
* @test
* @see
*/
/** @test */
public function it_generates_sql_query_for_withinDistanceTo_scope(): void
{
// 1. Arrange
Expand All @@ -45,18 +36,12 @@ public function it_generates_sql_query_for_withinDistanceTo_scope(): void

// 3. Assert
$this->assertEquals(
expected: "select *, CONCAT(ST_AsText(addresses.$castedAttr, 'axis-order=long-lat'), ',', ST_SRID(addresses.$castedAttr)) as $castedAttr from `addresses` where ST_Distance(
ST_SRID($castedAttr, ?),
ST_SRID(Point(?, ?), ?)
) <= ?",
expected: "select *, CONCAT(ST_AsText(addresses.$castedAttr, 'axis-order=long-lat'), ',', ST_SRID(addresses.$castedAttr)) as $castedAttr from `addresses` where ST_Distance(ST_SRID($castedAttr, ?), ST_SRID(Point(?, ?), ?)) <= ?",
actual: $query->toSql()
);
}

/**
* @test
* @see
*/
/** @test */
public function it_generates_sql_query_for_orderByDistanceTo_scope(): void
{
// 1. Arrange
Expand All @@ -69,26 +54,17 @@ public function it_generates_sql_query_for_orderByDistanceTo_scope(): void

// 3. Assert
$this->assertEquals(
expected: "select *, CONCAT(ST_AsText(addresses.$castedAttr, 'axis-order=long-lat'), ',', ST_SRID(addresses.$castedAttr)) as $castedAttr from `addresses` order by ST_Distance(
ST_SRID($castedAttr, ?),
ST_SRID(Point(?, ?), ?)
) asc",
expected: "select *, CONCAT(ST_AsText(addresses.$castedAttr, 'axis-order=long-lat'), ',', ST_SRID(addresses.$castedAttr)) as $castedAttr from `addresses` order by ST_Distance(ST_SRID($castedAttr, ?), ST_SRID(Point(?, ?), ?)) asc",
actual: $queryForAsc->toSql()
);

$this->assertEquals(
expected: "select *, CONCAT(ST_AsText(addresses.$castedAttr, 'axis-order=long-lat'), ',', ST_SRID(addresses.$castedAttr)) as $castedAttr from `addresses` order by ST_Distance(
ST_SRID($castedAttr, ?),
ST_SRID(Point(?, ?), ?)
) desc",
expected: "select *, CONCAT(ST_AsText(addresses.$castedAttr, 'axis-order=long-lat'), ',', ST_SRID(addresses.$castedAttr)) as $castedAttr from `addresses` order by ST_Distance(ST_SRID($castedAttr, ?), ST_SRID(Point(?, ?), ?)) desc",
actual: $queryForDesc->toSql()
);
}

/**
* @test
* @see
*/
/** @test */
public function it_generates_sql_query_for_location_casted_attributes(): void
{
// 1. Arrange
Expand All @@ -102,10 +78,7 @@ public function it_generates_sql_query_for_location_casted_attributes(): void
);
}

/**
* @test
* @see
*/
/** @test */
public function it_returns_location_casted_attributes(): void
{
// 1. Arrange
Expand Down
12 changes: 6 additions & 6 deletions tests/LocationCastTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace TarfinLabs\LaravelSpatial\Tests;

use Exception;
use InvalidArgumentException;
use Illuminate\Support\Facades\DB;
use TarfinLabs\LaravelSpatial\Casts\LocationCast;
use TarfinLabs\LaravelSpatial\Tests\TestModels\Address;
Expand All @@ -16,10 +16,10 @@ public function it_throws_an_exception_if_casted_attribute_set_to_a_non_point_va
// 1. Arrange
$address = new Address();

// 3. Expect
$this->expectException(Exception::class);
// 2. Expect
$this->expectException(InvalidArgumentException::class);

// 2. Act
// 3. Act
$address->location = 'dummy';
}

Expand All @@ -36,7 +36,7 @@ public function it_can_set_the_casted_attribute_to_a_point(): void
$response = $cast->set($address, 'location', $point, $address->getAttributes());

// 3. Assert
$this->assertEquals(DB::raw("ST_GeomFromText('POINT({$point->getLng()} {$point->getLat()})')"), $response);
$this->assertEquals(DB::raw("ST_GeomFromText('{$point->toWkt()}')"), $response);
}

/** @test */
Expand All @@ -52,7 +52,7 @@ public function it_can_set_the_casted_attribute_to_a_point_with_srid(): void
$response = $cast->set($address, 'location', $point, $address->getAttributes());

// 3. Assert
$this->assertEquals(DB::raw("ST_GeomFromText('POINT({$point->getLng()} {$point->getLat()})', {$point->getSrid()}, 'axis-order=long-lat')"), $response);
$this->assertEquals(DB::raw("ST_GeomFromText('{$point->toWkt()}', {$point->getSrid()}, 'axis-order=long-lat')"), $response);
}

/** @test */
Expand Down
45 changes: 45 additions & 0 deletions tests/PointTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,49 @@ public function it_returns_default_srid_in_config_if_it_is_not_null(): void
$this->assertSame(expected: 0.0, actual: $point->getLng());
$this->assertSame(expected: 4326, actual: $point->getSrid());
}

/** @test */
public function it_returns_point_as_wkt(): void
{
// 1. Arrange
$point = new Point(25.1515, 36.1212, 4326);

// 2. Act
$wkt = $point->toWkt();

// 3. Assert
$this->assertSame("POINT({$point->getLng()} {$point->getLat()})", $wkt);
}

/** @test */
public function it_returns_point_as_pair(): void
{
// 1. Arrange
$point = new Point(25.1515, 36.1212, 4326);

// 2. Act
$pair = $point->toPair();

// 3. Assert
$this->assertSame("{$point->getLng()} {$point->getLat()}", $pair);
}

/** @test */
public function it_returns_points_as_array(): void
{
// 1. Arrange
$point = new Point(25.1515, 36.1212, 4326);

// 2. Act
$array = $point->toArray();

$expected = [
'lat' => $point->getLat(),
'lng' => $point->getLng(),
'srid' => $point->getSrid(),
];

// 3. Assert
$this->assertSame($expected, $array);
}
}

0 comments on commit 67239f3

Please sign in to comment.