我有一些使用地理空间字段的模型,例如
POINT
、POLYGON
或 MULTIPOLYGON
。我想告诉我的模型以一种特殊的方式处理这些属性,以便我获得所需的模型属性集。
例子: 每个常规
Model::find()
或其他 Eloquent 方法都应该在存储之前或检索数据库值之后应用一些自定义代码。
$area->surface
是 MySQL 中的一个 POLYGON
字段,但在我的模型类中,我想将 $area->surfare
作为点数组处理。
On
SELECT
因此,我想 1) 使用原始表达式获取值以获取值的文本表示,以及 2) 通过一些自定义 PHP 代码将 WKT 字符串转换为数组。
On
INSERT/UPDATE
我想获取属性值(数组)和 1) 将其转换为 WKT 字符串,然后 2) 使用存储该值的 DB 原始语句将其写入数据库。
我想在现场设置它,而不是为每个字段设置特殊的获取/设置功能,而不是在控制器中 - 因为我有很多地理空间。
有没有办法在 Laravel 中实现这个?
(同一问题的更抽象版本,是我如何创建代码来操纵实际 SQL 查询的属性值,而不仅仅是通过修改器和访问器进行一些基于值的操纵)
更新: 深入查看 Laravel Doc 和 API,我发现也许
Eloquent::newQuery()
方法是我需要操作的?无论是SELECT
,INSERT
还是UPDATE
,它都可以用于任何查询吗?
我们现在已经通过使用以下功能扩展我们的基本模型来为所有模型解决这个问题:
这是我们现在使用的基本模型的摘录:
/**
* The attributes that hold geometrical data.
*
* @var array
*/
protected $geometry = array();
/**
* Select geometrical attributes as text from database.
*
* @var bool
*/
protected $geometryAsText = false;
/**
* Get a new query builder for the model's table.
* Manipulate in case we need to convert geometrical fields to text.
*
* @param bool $excludeDeleted
* @return \Illuminate\Database\Eloquent\Builder
*/
public function newQuery($excludeDeleted = true)
{
if (!empty($this->geometry) && $this->geometryAsText === true)
{
$raw = '';
foreach ($this->geometry as $column)
{
$raw .= 'AsText(`' . $this->table . '`.`' . $column . '`) as `' . $column . '`, ';
}
$raw = substr($raw, 0, -2);
return parent::newQuery($excludeDeleted)->addSelect('*', DB::raw($raw));
}
return parent::newQuery($excludeDeleted);
}
如果修改器和访问器不符合您的需求,您可以使用 Model Events 操作这些属性。
然后您可以在一些 Eloquent 事件被触发时执行代码:创建、创建、更新、更新、保存、保存、删除、删除、恢复、恢复。
积分可以用很酷的包git包
安装
composer require matanyadaev/laravel-eloquent-spatial
例子:
创建迁移
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePlacesTable extends Migration
{
public function up(): void
{
Schema::create('places', static function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->point('location')->nullable();
$table->polygon('area')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('places');
}
}
运行迁移
php artisan migrate
创建场所模型
填充
$fillable
和 $casts
数组并在新模型中使用 HasSpatial 特征:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use MatanYadaev\EloquentSpatial\SpatialBuilder;
use MatanYadaev\EloquentSpatial\Objects\Point;
use MatanYadaev\EloquentSpatial\Objects\Polygon;
use MatanYadaev\EloquentSpatial\Traits\HasSpatial;
/**
* @property Point $location
* @property Polygon $area
* @method static SpatialBuilder query()
*/
class Place extends Model
{
use HasSpatial;
protected $fillable = [
'name',
'location',
'area',
];
protected $casts = [
'location' => Point::class,
'area' => Polygon::class,
];
}
创建对象
use App\Models\Place;
use MatanYadaev\EloquentSpatial\Objects\Polygon;
use MatanYadaev\EloquentSpatial\Objects\LineString;
use MatanYadaev\EloquentSpatial\Objects\Point;
use MatanYadaev\EloquentSpatial\Enums\Srid;
$londonEye = Place::create([
'name' => 'London Eye',
'location' => new Point(51.5032973, -0.1217424),
]);
$whiteHouse = Place::create([
'name' => 'White House',
'location' => new Point(38.8976763, -77.0365298, Srid::WGS84->value), // with SRID
]);
$vaticanCity = Place::create([
'name' => 'Vatican City',
'area' => new Polygon([
new LineString([
new Point(12.455363273620605, 41.90746728266806),
new Point(12.450309991836548, 41.906636872349075),
new Point(12.445632219314575, 41.90197359839437),
new Point(12.447413206100464, 41.90027269624499),
new Point(12.457906007766724, 41.90000118654431),
new Point(12.458517551422117, 41.90281205461268),
new Point(12.457584142684937, 41.903107507989986),
new Point(12.457734346389769, 41.905918239316286),
new Point(12.45572805404663, 41.90637337450963),
new Point(12.455363273620605, 41.90746728266806),
]),
]),
])
包也有不同的有用方法和例子
whereDistance
orderByDistance
whereDistanceSphere
orderByDistanceSphere
whereWithin
whereNotWithin
whereContains
whereNotContains
whereEquals
对于所有使用 postgres 数据库的人,laravel-postgis 可以用于几何数据