Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel - Paginate Random records

How we can paginate through random records in laravel? for example:

$products = Product::all()->orderBy(DB::raw('RAND()'));
$products->paginate(4);
$products->setPath('products');

Above will ends in duplicate records, because of random order. How can I persist the $products object so that, when a new page request made, it should filter though same/fixed random record set ?

like image 674
dang Avatar asked May 31 '16 13:05

dang


3 Answers

Solution with Laravel + Eloquent

Before Eloquent query, set the session variable. I used time() for the session value, so I can keep the same ordering for a certain amount of time. For example, 3600 seconds (1 hour).

if ($request->session()->has('session_rand')) {

    if((time() - $request->session()->get('session_rand')) > 3600){
        $request->session()->put('session_rand', time());
    }
}else{
    $request->session()->put('session_rand', time());
}

Add ->orderBy() to the Eloquent query using DB::raw() and the session variable that we set above:

->orderBy(DB::raw('RAND('.$request->session()->get('session_rand').')'))
like image 118
Darren Murphy Avatar answered Nov 14 '22 07:11

Darren Murphy


Whe you dive into the documentation of mysql and search for the RAND() functionality you will see you can use a "seed".

By using a seed you will always get the same results that are randomised.

Example:

$products = Product

    ::all()

    ->orderBy(DB::raw('RAND(1234)'))

    ->paginate(4);

You can generate your own seed and store in in a session or something to remember it.

Update

The Laravel query builder now has a function that does exactly the same:

$products = Product

    ::all()

    ->inRandomOrder('1234')

    ->paginate(4);
like image 24
Thomas Van der Veen Avatar answered Nov 14 '22 08:11

Thomas Van der Veen


Solution Ajax + chunk + session + lazyload + paginate + eloquent + blade

get all products, set the number of item for each chunk (like paginate) and store it into session (we store the chunks into session so when ajax request comes in it won't call $products = Product::inRandomOrder()->get(); again) , first chunk will be the first pagination

when ajax is requesting for more products it will get the products from session and get the correct chunk of products base on the page number ajax requested.

if there are no more chunks just return without any products

    if(!$request->ajax()){

        $products = Product::inRandomOrder()->get();
        $chunks = $products->chunk(4);

        \Session::put('chunks',$chunks);
        $products = $chunks[0];
    }else{
        $page = $request->all()['page'];                
        $chunks = \Session::get('chunks');

        if(count($chunks)<$page){
            \Session::forget('chunks');
            return;
        }else{
            $products = $chunks[$page-1];
        }
    }

   if ($request->ajax()) {
        $view =  view('product_lazyLoad',compact('products'))->render();
        return response()->json(['html'=>$view]);
   }

   return view('products',compact('products'));

products.blade.php : main page

@if($products)
    <ul class="row" id="productLists">
        @include('product_lazyLoad')
    </ul>
@endif

<!-- load records -->

<div class="col-12 ajax-load text-center" style="display:none">
    <p><img src="/images/loader.gif">Loading More Products</p>
</div>

product_lazyload.blade.php : display products

@foreach($products as $product)
    <div>
        <p>Display your products here</p>
    </div>
@endforeach

font-end ajax call : when the page is scroll to bottom of the page the variable page will auto increment and request backend for more products. if more products are return it will append the products to element with id = "productlist" in products.blade.php

    //lazy load
    var page = 1;
    $(window).scroll(function() {
        if($(window).scrollTop() + $(window).height() >= $(document).height()) 
        {
            page++;
            loadMoreData(page);
        }
    });

  function loadMoreData(page){
        $.ajax({
            url: '?page=' + page,
            type: "get",
            beforeSend: function(){
                $('.ajax-load').show();
            }
        }).done(function(data){
            if(!data.html){
                $('.ajax-load').html("No more records found");
                $('.ajax-load').show();
                return;
            }

            $('.ajax-load').hide();
            $("#productLists").append(data.html);

        }).fail(function(jqXHR, ajaxOptions, thrownError){
             //error handle
        });
    }
like image 1
Jason Avatar answered Nov 14 '22 06:11

Jason