引言
laravel使用模型进行数据库操作时,并不是所有的字段或者关联关系 都能满足查询需求,有时候会有一些MySQL的函数计算等功能放在数据库 层面执行。
本文说一说自定义的参数绑定办法。
学习时间
假如有一个需求,已知经纬度手动计算两点之间的近距离。我们会使用类似下面的计算公式:
3959 * acos( cos( radians( ? ) ) * cos( radians( lat ) ) * cos( radians( lng ) – radians(?) ) + sin( radians( ? ) ) * sin( radians( lat ) ) )
其中问号位置就是我们需要填入的变量数据,也就是一个经纬度坐标,根据参照点,计算出数据库内每条记录内的该点距离参照点的距离。 那么如果写到程序里,应该如何把绑定参数按顺序传入呢。
既然是自定义的字段名,我们可以使用 DB::raw 方式传入。代码如下:
$property =
Property::select(
DB::raw(“title, lat, lng, (
3959 * acos(
cos( radians( ? ) ) *
cos( radians( lat ) ) *
cos( radians( lng ) – radians(?) ) +
sin( radians( ? ) ) *
sin( radians( lat ) )
)
) AS distance”)
)
->having(“distance”, “<“, “?”) ->orderBy(“distance”)
->take(20)
->setBindings([$lat, $lng, $lat, $radius])
->get();
这个查询语句有些复杂,可是有章可循。首先是使用select方法指定了返回字段名, 对于自定义的字段 distance 使用 having 子句进行进一步条件限定。 最重要就是 setBindings 方法了,传送的是前面自定义SQL语句的位置参数。只需一一对应上就可以了。
如果大家经常使用laravel的调试功能的话,应该会注意到,这种问号的写法是laravel本身封装SQL语句用的。 而对于写惯了PDO原生SQL语句查询的,则可以使用PDO方式的绑定方式:
$property = Property::select(DB::raw(“title, lat, lng, ( 3959 * acos( cos( radians(:lat) ) * cos( radians( lat ) ) * cos( radians( lng ) – radians(:lng) + sin(radians(:lat_i) ) * sin( radians( lat ) ) ) ) AS distance”), [“lat” => $lat, “lng” => $lng, “lat_i” => $lat]);
上面的语句只用了select方法,其他的筛选和排序,与上一节代码相同,不需要调用 setBindings 方法。
为了方便查看,我们也可以不画蛇添足地使用 DB::raw 方法。而直接使用 selectRaw ,更为简洁直观:
$select = <<<SQL
title,
lat,
lng,
(3959acos(cos(radians( ? ))cos(radians(lat))cos(radians(lng)-radians( ? ))+sin(radians( ? ))sin(radians(lat)))) AS distance
SQL;
首先拼装好SQL语句的字段,然后使用laravel模型的方法:
$property = Property::selectRaw($select, [$lat, $lng, $lat])->having(‘distance’, ‘<‘, $radius)->orderBy(‘distance’)->take(20)->get();
写在最后
本文通过三种方式实现了laravel模型进行复杂的自定义查询, 其中提供了两种参数绑定的方式,我们推荐使用 selectRaw 的方式, 更为直观。
Happy coding :-)