How to handle new incoming entries while paging through posts? - laravel

Let's assume I have a pagination like this
return App\Post::paginate(1);
After loading this someone creates a new entry in the database for posts. How can i ensure that the "second page" of my pagination does not return the first one again?
Will I always need to send a timestamp with every request to make an additional query before pagination is used?
like
App\Post::where('created_at', '<', $request->input('max_created_at'))->paginate(1);

You can use orderBy. You could do something like that:
// Get the last post (most recent)
return App\Post::orderBy('created_at', 'DESC')->paginate(1);
Or:
// Same as above
return App\Post::orderBy('created_at', 'DESC')->first();
orderBy means all you result are sorted in your query. Hope it will help.

You don't need to pass any timestamp value to verify pagination.
In the controller, in view file you need to pass result of this query.
$result = App\Post::orderBy('created_at', 'desc')->paginate(1);
return view('pagination',compact('result'));
In view file you need below line for pagination work automatically as per Laravel syntax.
{{ $result->links() }}
Visit https://laravel.com/docs/5.2/pagination/ how laravel pagination work.

Related

Eloquent resulting single search result as array not collection

I have some instances where a eloquent is resulting an single array not a collection. Although dd shows it as a collection with a single entry.
For example I have a query in a controller:
$pg = Page::with('getPanels')->where('slug',$slug)->get();
This will return a single result and works fine, so I pass this to a blade template. My complete function is
$pg = Page::with('getPanels')->where('slug',$slug)->get();
return view('front.page',['pg' => $pg]);
As soon as he template is brought in it will fall over at
if (!is_null($pg->headImage))
{$img = asset('images/pages')."/".$pg->headImage;}
and I will get
Property [headImage] does not exist on this collection instance.
If I change the line to
if (!is_null($pg[0]['headImage']))
it will continue OK. This is of course a pain as I would much rather use $pg->headImage.
Can someone enlighten me please?
I have sorted this and I hope it will help other people.
If I use
$pg = Page::with('getPanels')->where('slug',$slug)->first();
it will be just one result (naturally) and therefore
$pg->headImage
will fail as it wants
$pg[0]['headImage']
but if I change the eloquent instead of get(0 to first() (still just one result)
$pg = Page::with('getPanels')->where('slug',$slug)->first();
I can use $pg->headImage or what field I want.

Is it inefficient to use count() in Laravel Blade?

This is more a "best practice" question.
My website has articles, and articles can have comments.
public function comments() {
return $this->hasMany('App\Comment', 'submission_id');
}
On the front page, where the articles are listed, the amount of comments are shown:
#if ($article->comments->count())
{{ $article->comments->count() }}
{{ $article->comments->count() == 1 ? 'comment' : 'comments' }}
#endif
And I was thinking to myself, in this example, is my database being queried 3 times for this simple code snippet?
Every time I get the comment count through this relationship, is it loading the entire array of comments and all of its columns? Because sometimes articles can have upwards of 1k+ comments.
If so, would it not be best practice to simply have a column in my articles database table that increments every time a comment is posted and then fetching that directly rather than through the relationship?
Not the way you're doing it. It depens on when you're using ->count(), and whether or not it's the Collection version, or the Builder version. See the following examples:
Example one - new query with comments() method:
$article = Article::first();
$article->comments()->count();
In this example, using $article->comments() causes a new query to be started (using the Builder class). No matter how you finish this query, i.e. using ->first(), ->get() or ->count() a new query will be executed.
Now, example two - new query with comments property:
$article = Article::first();
$article->comments->count();
In this example, $article->comments will be executing a new query, as relationship accessors act differently if the relationship has been loaded or not. In this case, it hasn't been loaded, so $article->comments returns a Builder instance instead of a Collection, so finishing the query will execute a new query.
Finally, example three - Collection access with comments property:
$article = Article::with(["comments"])->first();
$article->comments->count();
In this last example, $article->comments has been specified as loaded via the ->with(["comments"]) clause, so it is a Collection, and not a Builder instance. For this reason, calling ->count() is using the Collection count method, and does not perform an additional query

Laravel append overall total value on pagination result

I have a Laravel query using pagination.
I want to be able to return the result based on the pagination but also get the overall total of the query and append this to the return. So for example the pagination is set to 5 but the overall total might be 20.
$query = Model::paginate(5);
$queryTotal = $query->total();
$query->append($queryTotal);
return $query;
The Laravel Paginator does this already.
You can see that when serializing results to JSON there is a total key which represents all rows matching that query.
You can also see there is a total method available from the paginator:
$results->total()
Along side other methods that can be found in the Pagination Docs
$query = Model::paginate(5);
return $query;
You can access overall total using
{{ $query->total() }}
For more Info read Paginator instance
The paginate function returns a LengthAwarePaginator object. It simply not possible to add another field to this object.
Your best option is to manually create a new collection in which you merge the LengthAwarePaginator with your newly added data.
An example would be:
$query = Model::paginate(5);
$addition = collect(['totalResult' => $query->total()]);
$queryData = $addition->merge($query);
return $queryData;
Naturally, if you just return the LengthAwarePaginator object, you can simply call the total() function, if you use it in your blade files for example.
Hope this helps!

Laravel 5.1 eloquent query orderby external data and paginated

I'm trying to get data from an Eloquent query (that works), then order the data, then paginate the data.
I had the code ordering by date, then paginating, but now I want to order by facebook API obtained data (I get the data correctly).
The thing is I don't know what I should do first (paginate or ordering):
If I paginate first, I don't know how to order the data since the object is LengthAwarePaginator and doesn't have any orderBy method.
If I order first, I get a collection object and can't use ->paginate($perPage) to do the pagination.
This is the code:
$posts = Post::with('User')->
where('created_at', '<', new \DateTime)->
paginate($perPage);
$counter = 0;
foreach ($posts as $post) {
$url = 'http://myweb.com/' . $post['id'];
$fb_stuff = json_decode(file_get_contents('https://api.facebook.com/method/links.getStats?urls=' . $url . '&format=json'), true);
$posts[$counter]['share_count'] = $fb_stuff[0]['share_count'];
$counter++;
}
If you need to sort by some value that is not stored in the database then the only option is to fetch all records from the database, fetch the sorting data from external source (Facebook API), sort it by user defined function (see http://php.net/manual/en/function.usort.php) and then paginate.
Once you have your data sorted you can easily get the paginated version by creating a Paginator object:
$paginator = new \Illuminate\Pagination\Paginator($posts, $perPage, $currentPage);
Anyway, this solution will be quite heavy as you'll need to fetch data from Facebook API every time you want a sorted list of posts. I doubt you need real time data so I suggest to store share_count in your posts table and refresh it on regular basis, e.g. by running a scheduled laravel command.

Laravel Paginator getTotal returns null - Need total record count for all pages

I have implemented simple pagination using laravel. It works fine. However, I want to add total number of records and trying to use the method getTotal() but it returns null value.
$records = DB::table('tablename')
->where(condition)
....
....
->simplePaginate(10);
In the view, adding links works fine.
{{$records->links();}}
When I use,
{{$records->getTotal();}}
it returns null.
If I use,
{{$records->count();}}
it returns the count of records for a given page.
Any insights please?
That's how simplePaginate works. From the docs:
If you are only showing "Next" and "Previous" links in your pagination view, you have the option of using the simplePaginate method to perform a more efficient query. This is useful for larger datasets when you do not require the display of exact page numbers on your view.
The simple method is simple because it doesn't need to do the extra, inefficient-on-large-tables count query to obtain the total.
There is method total() in that pagination object. Just call that object. You will get total count.
ex: $records->total();
I had the same problem, what I did was
$records = DB::table('tablename')
->where(condition)
....
....
->paginate(1); //set as 1
$total = $records->getTotal(); //get total
Then returning it like this(I am using ajax that is why I return it as array):
return Response::JSON(array(
'pagination' => (string) $records->links('pagination::simple'),
'total_records' => $total
));

Resources