From c0f77f1424b79895ac79f6ba3a5876622b4a39f7 Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 3 Jan 2025 17:05:42 +0100 Subject: [PATCH 1/2] Add ->as() helper to MagellanBaseExpression that wraps into Aliased expression --- CHANGELOG.md | 1 + README.md | 25 +++++++++---------- .../MagellanBaseExpression.php | 6 +++++ tests/Models/SpatialQueriesTest.php | 15 ++++++----- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4633409..e233547 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `Castable` to all geometries to use them as casters, instead of the `GeometryWKBCast` - Added `Aliased` Expression class as wrapper for `AS` in query selects + - Add `->as()` helper method on MagellanBaseExpression - Added `withMagellanCasts()` as EloquentBuilder macro - Added `AsGeometry` and `AsGeography` database expressions diff --git a/README.md b/README.md index 5da60ee..f083cd4 100644 --- a/README.md +++ b/README.md @@ -390,7 +390,6 @@ Most of the `ST`-prefixed functions can be accessed using the static functions o ```php use Clickbar\Magellan\Data\Geometries\Point; use Clickbar\Magellan\Database\PostgisFunctions\ST; -use Clickbar\Magellan\Database\Expressions\Aliased; ``` Assuming we have our ships current position and want to query all ports with their distance: @@ -398,7 +397,7 @@ Assuming we have our ships current position and want to query all ports with the ```php $currentShipPosition = Point::makeGeodetic(50.107471773560114, 8.679861151457937); $portsWithDistance = Port::select() // use select() because we want SELECT *, distance and not only the distance - ->addSelect(new Aliased(ST::distanceSphere($currentShipPosition, 'location'), 'distance_to_ship')) + ->addSelect(ST::distanceSphere($currentShipPosition, 'location')->as('distance_to_ship')) ->get(); ``` @@ -407,7 +406,7 @@ Since we cannot sail over the whole world, let's limit the distance to max. 50.0 ```php $currentShipPosition = Point::makeGeodetic(50.107471773560114, 8.679861151457937); $portsWithDistance = Port::select() - ->addSelect(new Aliased(ST::distanceSphere($currentShipPosition, 'location'), 'distance_to_ship')) + ->addSelect(ST::distanceSphere($currentShipPosition, 'location')->as('distance_to_ship')) ->where(ST::distanceSphere($currentShipPosition, 'location'), '<=', 50000) ->get(); ``` @@ -417,7 +416,7 @@ Now let us order them based on the distance: ```php $currentShipPosition = Point::makeGeodetic(50.107471773560114, 8.679861151457937); $portsWithDistance = Port::select() - ->addSelect(new Aliased(ST::distanceSphere($currentShipPosition, 'location'), 'distance_to_ship')) + ->addSelect(ST::distanceSphere($currentShipPosition, 'location')->as('distance_to_ship')) ->where(ST::distanceSphere($currentShipPosition, 'location'), '<=', 50000) ->orderBy(ST::distanceSphere($currentShipPosition, 'location')) ->get(); @@ -431,16 +430,16 @@ No problem: $hullsWithArea = Port::query() ->select([ 'country', - new Aliased(ST::convexHull(ST::collect('location')), 'hull'), - new Aliased(ST::area(ST::convexHull(ST::collect('location'))), 'area') + ST::convexHull(ST::collect('location'))->as('hull'), + ST::area(ST::convexHull(ST::collect('location')))->as('area') ]) ->groupBy('country') ->get(); ``` ### Alias in select -Since we use Laravel Database Expressions for a seamless integration into the default select(...), where(..) and so on, you need to add an alias using the `Aliased` expression: +Since we use Laravel Database Expressions for a seamless integration into the default select(...), where(..) and so on, you need to use the `as(string)` method on our ST::function expressions: ```php - ->select(new Aliased(ST::distanceSphere($currentShipPosition, 'location'), 'distance_to_ship')) + ->select(ST::distanceSphere($currentShipPosition, 'location')->as('distance_to_ship')) //--> leads to SELECT ST_DistanceSphere(<select(new Aliased(ST::buffer(new AsGeography('location'), 50), as: 'buffered_location')) + ->select(ST::buffer(new AsGeography('location'), 50)->as('buffered_location')) ->withCasts(['buffered_location' => Polygon::class]) ->get(); ``` @@ -474,8 +473,8 @@ Since "hull" will return a geometry we need a cast for it. Instead of adding eac $hullWithArea = Port::query() ->select([ 'country', - new Aliased(ST::convexHull(ST::collect('location')), 'hull'), - new Aliased(ST::area(ST::convexHull(ST::collect('location'))), 'area') + ST::convexHull(ST::collect('location'))->('hull'), + ST::area(ST::convexHull(ST::collect('location')))->('area') ]) ->groupBy('country') ->withMagellanCasts() /* <======= */ @@ -486,8 +485,8 @@ $hullWithArea = Port::query() $hullWithArea = Port::query() ->select([ 'country', - new Aliased(ST::convexHull(ST::collect('location')), 'hull'), - new Aliased(ST::area(ST::convexHull(ST::collect('location'))), 'area') + ST::convexHull(ST::collect('location'))->('hull'), + ST::area(ST::convexHull(ST::collect('location')))->('area') ]) ->groupBy('country') ->withCasts(['hull' => Polygon::class]) /* <======= */ diff --git a/src/Database/MagellanExpressions/MagellanBaseExpression.php b/src/Database/MagellanExpressions/MagellanBaseExpression.php index 8fe0ac5..661c576 100644 --- a/src/Database/MagellanExpressions/MagellanBaseExpression.php +++ b/src/Database/MagellanExpressions/MagellanBaseExpression.php @@ -4,6 +4,7 @@ use Clickbar\Magellan\Database\Builder\StringifiesQueryParameters; use Clickbar\Magellan\Database\Builder\ValueParameter; +use Clickbar\Magellan\Database\Expressions\Aliased; use Clickbar\Magellan\IO\Generator\BaseGenerator; use Clickbar\Magellan\IO\Generator\WKT\WKTGenerator; use Illuminate\Contracts\Database\Query\Expression; @@ -65,6 +66,11 @@ public function returnsBbox(): bool return $this instanceof MagellanBBoxExpression; } + public function as(string $name): Expression + { + return new Aliased($this, $name); + } + // ######################## Database Expression Building ######################## public function getValue(Grammar $grammar): float|int|string diff --git a/tests/Models/SpatialQueriesTest.php b/tests/Models/SpatialQueriesTest.php index c107946..8ba3224 100644 --- a/tests/Models/SpatialQueriesTest.php +++ b/tests/Models/SpatialQueriesTest.php @@ -4,7 +4,6 @@ use Clickbar\Magellan\Data\Geometries\MultiPoint; use Clickbar\Magellan\Data\Geometries\Point; use Clickbar\Magellan\Data\Geometries\Polygon; -use Clickbar\Magellan\Database\Expressions\Aliased; use Clickbar\Magellan\Database\Expressions\AsGeography; use Clickbar\Magellan\Database\PostgisFunctions\ST; use Clickbar\Magellan\IO\Parser\WKB\WKBParser; @@ -90,7 +89,7 @@ $distances = Location::query() ->select([ 'name', - new Aliased(ST::distance(new AsGeography('location'), new AsGeography($berlinPoint)), 'distance_in_meters'), + ST::distance(new AsGeography('location'), new AsGeography($berlinPoint))->as('distance_in_meters'), ]) ->orderBy('distance_in_meters') ->get(); @@ -120,7 +119,7 @@ $closestPoint = Location::query() ->where('name', 'Berlin') - ->select(new Aliased(ST::closestPoint('location', $hamburg), 'closest_point')) + ->select(ST::closestPoint('location', $hamburg)->as('closest_point')) ->withMagellanCasts() ->first(); @@ -144,7 +143,7 @@ $shortestLine = Location::query() ->where('name', 'Berlin') - ->select(new Aliased(ST::shortestLine('location', $hamburg), 'shortest_line')) + ->select(ST::shortestLine('location', $hamburg)->as('shortest_line')) ->withMagellanCasts() ->first(); @@ -161,7 +160,7 @@ // Check if the point is valid and simple $validation = Location::query() ->where('id', $berlin->id) - ->select(new Aliased(ST::isSimple('location'), 'is_simple')) + ->select(ST::isSimple('location')->as('is_simple')) ->first(); expect($validation->is_simple)->toBeTrue(); @@ -178,8 +177,8 @@ $dimensions = Location::query() ->where('id', $berlin->id) ->select([ - new Aliased(ST::coordDim('location'), 'coord_dim'), - new Aliased(ST::nDims('location'), 'n_dims'), + ST::coordDim('location')->as('coord_dim'), + ST::nDims('location')->as('n_dims'), ]) ->first(); @@ -211,7 +210,7 @@ // Calculate 3D distance $distances = $location3d::query() ->where('name', 'Berlin') - ->select(new Aliased(ST::distance3D('location', $hamburgWithAltitude), 'distance_3d')) + ->select(ST::distance3D('location', $hamburgWithAltitude)->as('distance_3d')) ->first(); expect((float) $distances->distance_3d)->toBeFloat(); From f48aaf9bf0af4ccbb2f6e576bf0b6836bbd29a80 Mon Sep 17 00:00:00 2001 From: Adrian <53199186+ahawlitschek@users.noreply.github.com> Date: Fri, 3 Jan 2025 17:36:38 +0100 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: saibotk --- CHANGELOG.md | 2 +- README.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e233547..dba1f8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `Castable` to all geometries to use them as casters, instead of the `GeometryWKBCast` - Added `Aliased` Expression class as wrapper for `AS` in query selects - - Add `->as()` helper method on MagellanBaseExpression + - Added `->as()` helper method on MagellanBaseExpression - Added `withMagellanCasts()` as EloquentBuilder macro - Added `AsGeometry` and `AsGeography` database expressions diff --git a/README.md b/README.md index f083cd4..c0c1b86 100644 --- a/README.md +++ b/README.md @@ -473,8 +473,8 @@ Since "hull" will return a geometry we need a cast for it. Instead of adding eac $hullWithArea = Port::query() ->select([ 'country', - ST::convexHull(ST::collect('location'))->('hull'), - ST::area(ST::convexHull(ST::collect('location')))->('area') + ST::convexHull(ST::collect('location'))->as('hull'), + ST::area(ST::convexHull(ST::collect('location')))->as('area') ]) ->groupBy('country') ->withMagellanCasts() /* <======= */ @@ -485,8 +485,8 @@ $hullWithArea = Port::query() $hullWithArea = Port::query() ->select([ 'country', - ST::convexHull(ST::collect('location'))->('hull'), - ST::area(ST::convexHull(ST::collect('location')))->('area') + ST::convexHull(ST::collect('location'))->as('hull'), + ST::area(ST::convexHull(ST::collect('location')))->as('area') ]) ->groupBy('country') ->withCasts(['hull' => Polygon::class]) /* <======= */