Как использовать paginate () с предложением having (), когда столбец не существует в таблице

У меня сложный случай …

Следующий запрос базы данных не работает:

DB::table('posts') ->select('posts.*', DB::raw($haversineSQL . ' as distance')) ->having('distance', 'paginate(10); 

Он не работает с сообщением: расстояние столбца не существует.

Ошибка возникает, когда paginate () пытается подсчитать записи с помощью

 select count(*) as aggregate from {query without the column names} 

Когда имена столбцов лишены, расстояние неизвестно и возникает исключение.

Есть ли у кого-нибудь работа, чтобы иметь возможность использовать разбиение на страницы?

благодаря

Это несколько проблема с построителем запросов, поскольку все выбранные элементы отбрасываются при выполнении совокупного вызова (например, count(*) ). Решение make-do на данный момент состоит в том, чтобы вручную создать pagniator.

 $query = DB::table('posts') ->select(DB::raw('(c1 - c2) as distance')) ->having('distance', '<=', 5); $perPage = 10; $curPage = Paginator::getCurrentPage(); // reads the query string, defaults to 1 // clone the query to make 100% sure we don't have any overwriting $itemQuery = clone $query; $itemQuery->addSelect('posts.*'); // this does the sql limit/offset needed to get the correct subset of items $items = $itemQuery->forPage($curPage, $perPage)->get(); // manually run a query to select the total item count // use addSelect instead of select to append $totalResult = $query->addSelect(DB::raw('count(*) as count'))->get(); $totalItems = $totalResult[0]->count; // make the paginator, which is the same as returned from paginate() // all() will return an array of models from the collection. $paginatedItems = Paginator::make($items->all(), $totalItems, $perPage); 

Протестировано со следующей схемой с использованием MySQL:

 Schema::create('posts', function($t) { $t->increments('id'); $t->integer('c1'); $t->integer('c2'); }); for ($i=0; $i < 100; $i++) { DB::table('posts')->insert([ 'c1' => rand(0, 10), 'c2' => rand(0, 10), ]); } 

Используя Eloquent, я знаю, что вы можете передавать столбцы в paginator, что-то вроде этого:

 Post::having('distance','<=', $distance) ->paginate(10, array('*', DB::raw($haversineSQL . ' as distance'))); 

Не уверен, что он работает без эвкалипта, но вы можете попробовать.

Вы можете использовать ручную разбивку на страницы как having своеобразное поведение с classом разбиения на страницы.

 $posts = DB::table('posts') ->select('posts.*', DB::raw($haversineSQL . ' as distance')) ->having('distance', '<=', $distance) ->get(); // Items per page $perPage = 10; $totalItems = count($posts); $totalPages = ceil($totalItems / $perPage); $page = Input::get('page', 1); if ($page > $totalPages or $page < 1) { $page = 1; } $offset = ($page * $perPage) - $perPage; $posts = array_slice($posts, $offset, $perPage); $posts = Paginator::make($posts, $totalItems, $perPage); dd($posts); 

Вы можете рассчитать расстояние в части WHERE :

 DB::table('posts') ->whereRaw($haversineSQL . '<= ?', [$distance]) ->paginate(10); 

Если вам нужно значение distance в приложении, вам придется рассчитать его дважды:

 DB::table('posts') ->select('posts.*', DB::raw($haversineSQL . ' as distance')) ->whereRaw($haversineSQL . '<= ?', [$distance]) ->paginate(10); 

Это область реализует поиск формулы Haversine , с дополнительной оптимизацией скорости, которая описана здесь .

Мне жаль, что не было чистого способа получить исходный SQL из объекта запроса, но, к сожалению, toSql() возвращает SQL перед заменой заполнителей, поэтому я полагался на несколько *Raw вызовов. Это не так уж плохо, но мне жаль, что это было чище.

Код предполагает, что в вашей таблице есть столбцы lat и lng .

 const DISTANCE_UNIT_KILOMETERS = 111.045; const DISTANCE_UNIT_MILES = 69.0; /** * @param $query * @param $lat * @param $lng * @param $radius numeric * @param $units string|['K', 'M'] */ public function scopeNearLatLng($query, $lat, $lng, $radius = 10, $units = 'K') { $distanceUnit = $this->distanceUnit($units); if (!(is_numeric($lat) && $lat >= -90 && $lat <= 90)) { throw new Exception("Latitude must be between -90 and 90 degrees."); } if (!(is_numeric($lng) && $lng >= -180 && $lng <= 180)) { throw new Exception("Longitude must be between -180 and 180 degrees."); } $haversine = sprintf('*, (%f * DEGREES(ACOS(COS(RADIANS(%f)) * COS(RADIANS(lat)) * COS(RADIANS(%f - lng)) + SIN(RADIANS(%f)) * SIN(RADIANS(lat))))) AS distance', $distanceUnit, $lat, $lng, $lat ); $subselect = clone $query; $subselect ->selectRaw(DB::raw($haversine)); // Optimize the query, see details here: // http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/ $latDistance = $radius / $distanceUnit; $latNorthBoundary = $lat - $latDistance; $latSouthBoundary = $lat + $latDistance; $subselect->whereRaw(sprintf("lat BETWEEN %f AND %f", $latNorthBoundary, $latSouthBoundary)); $lngDistance = $radius / ($distanceUnit * cos(deg2rad($lat))); $lngEastBoundary = $lng - $lngDistance; $lngWestBoundary = $lng + $lngDistance; $subselect->whereRaw(sprintf("lng BETWEEN %f AND %f", $lngEastBoundary, $lngWestBoundary)); $query ->from(DB::raw('(' . $subselect->toSql() . ') as d')) ->where('distance', '<=', $radius); } /** * @param $units */ private function distanceUnit($units = 'K') { if ($units == 'K') { return static::DISTANCE_UNIT_KILOMETERS; } elseif ($units == 'M') { return static::DISTANCE_UNIT_MILES; } else { throw new Exception("Unknown distance unit measure '$units'."); } } 

Это может быть использовано как таковое:

  $places->NearLatLng($lat, $lng, $radius, $units); $places->orderBy('distance'); 

Сгенерированный SQL будет выглядеть примерно так:

 select * from ( select *, ( '111.045' * DEGREES( ACOS( COS( RADIANS('45.5088') ) * COS( RADIANS(lat) ) * COS( RADIANS('-73.5878' - lng) ) + SIN( RADIANS('45.5088') ) * SIN( RADIANS(lat) ) ) ) ) AS distance from `places` where lat BETWEEN 45.418746 AND 45.598854 and lng BETWEEN -73.716301 AND -73.459299 ) as d where `distance` <= 10 order by `distance` asc 

Это не удовлетворительный ответ, но если вам нужно отображать простые «Следующие» и «Предыдущие» ссылки в вашем представлении с simplePaginate по simplePaginate , вы можете использовать метод simplePaginate . Он будет выполнять более эффективный запрос, и он не будет сбой, если вы используете его.

 DB::table('posts') ->select('posts.*', DB::raw($haversineSQL . ' as distance')) ->having('distance', '<=', $distance) ->simplePaginate(10); 
  • Laravel перенаправить все запросы на HTTPS
  • Миграция: не удается добавить ограничение внешнего ключа в laravel
  • Laravel перенаправляет обратно с помощью () сообщения
  • Яркая нетерпеливая загрузка
  • В чем разница между очередью: work --daemon и queue: listen
  • Красноречивый сбор: подсчет и обнаружение пустых
  • Как удалить пакет из Laravel с помощью композитора?
  • Laravel перенаправляет обратно в исходное место после входа в систему
  • Как установить тайм-аут сеанса в Laravel 4.2?
  • Interesting Posts
    Давайте будем гением компьютера.