I'm using haversine formula to count distance from selected post-code to desired destination.
App\Models\Business.php
public function scopeDistance($query, $latitude, $longitude, $radius)
{
$query->getQuery()->orders = [];
return $query->select('*')
->selectRaw("( 3959 * acos( cos( radians($latitude) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians($longitude) ) + sin( radians($latitude) ) * sin(radians(latitude)) ) ) AS distance")
->having('distance', '<=', $radius)
->orderBy('distance');
}
Everything seems pretty ok, except when i use paginate()
method instead of get()
I have been looking for solution, there are lots of topic describing this problem, but seems that people didn't come up with good solution for it.
Some solutions are using clone $builder
and trying to get item_count
separately, but in my case it caused incorrect values (always got number of all, not filtered by formula), maybe i did something wrong - not sure...
However i came up with mine solution and i would like to hear any feedback if it's ok in terms of speed and memory usage.
function custom_paginator($builder, $per_page)
{
$path = current_route();
$curPage = \Illuminate\Pagination\Paginator::resolveCurrentPage();
$haversine = isset($builder->getQuery()->columns[1]) ? $builder->getQuery()->columns[1]->getValue() : null;
$count = $haversine ?
\DB::select(\DB::raw(sprintf("SELECT COUNT(*) as count FROM (SELECT %s FROM `businesses` HAVING `distance` <= %d) AS harversine", $haversine, $builder->getQuery()->havings[0]['value'])))[0]->count :
$builder->count();
return new \Illuminate\Pagination\LengthAwarePaginator(
$builder->forPage($curPage, $per_page)->get()->all(),
$count, $per_page, null, compact('path')
);
}
I haven't got any more ideas how to improve it, at least it works and i dont need to call something like
count = count($results = $builder->get());
$items = $results->forPage($curPage, $perPage);
feedback really appreciated.
via rk.dev