I'm building an application using Laravel 4.2. I have a model for units and another for users and pivot table user_units. Every user in this application can select a unit and add it to his favorite list then he can publish this unit with his information as an ad.
I want to select all units published by all users
The user_units (pivot) table has the following columns:
id
user_id
unit_id
publish
adtype
addinfo
created_at
updated_at
With relations methods on models
public function users() {
return $this->belongsToMany('User', 'user_units')
->withPivot('id','publish', 'adtype', 'addinfo');
}
public function units() {
return $this->belongsToMany('Unit', 'user_units')
->withPivot('id','publish', 'adtype', 'addinfo');
}
My query to select all published units by all users
// Get all published units by users for sale.
$users = User::all();
$publishedSaleUnits = [];
foreach($users as $user){
$userUnits = $user->units()->orderBy('adtype', 'desc')->get();
if(count($userUnits)){
foreach($userUnits as $unit){
if($unit->pivot->publish == 1 && $unit->unit_purpose_id == 1){
if( $unit->pivot->adtype ){
//push all featured ads onto the beginning of array
array_unshift($publishedSaleUnits, $unit);
}else{
//push all normal ads onto the end of array
array_push($publishedSaleUnits, $unit);
}
}
}
}
}
Now I got the result but I can't use pagination with results because it's an array of objects.
So is there any better solution to get all published units by user with pagination?
according to this article
https://www.itsolutionstuff.com/post/how-to-create-pagination-from-array-in-laravelexample.html
you can paginate your array by creating a custom method and using LengthAwarePaginator
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
use Illuminate\Pagination\LengthAwarePaginator;
class PaginationController extends Controller
{
public function index()
{
$myArray = [
['id'=>1, 'title'=>'Laravel CRUD'],
['id'=>2, 'title'=>'Laravel Ajax CRUD'],
['id'=>3, 'title'=>'Laravel CORS Middleware'],
];
$data = $this->paginate($myArray);
return view('paginate', compact('data'));
}
public function paginate($items, $perPage = 5, $page = null, $options = [])
{
$page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
$items = $items instanceof Collection ? $items : Collection::make($items);
return new LengthAwarePaginator($items->forPage($page, $perPage), $items->count(), $perPage, $page, $options);
}
}
Your approach to query the data is extremely inefficient. Fetch your data in one query. Nested traversing is not only hard to read but also a performance killer.
To the pagination problem:
Laravel provides a Pagignator Factory. With it you will be able to build your own Paginator with your own data.
It's as easy as
$units = Paginator::make($unit, count($unit), 10);
if you're using the Facade. Otherwise Illuminate\Pagination\Factory is the class you are looking for.
You can try my code with your own array,
$page = isset($request->page) ? $request->page : 1; // Get the page=1 from the url
$perPage = $pagination_num; // Number of items per page
$offset = ($page * $perPage) - $perPage;
$entries = new LengthAwarePaginator(
array_slice($contact_list, $offset, $perPage, true),
count($contact_list), // Total items
$perPage, // Items per page
$page, // Current page
['path' => $request->url(), 'query' => $request->query()] // We
need this so we can keep all old query parameters from the url
);
I got a better solution to paginate array result and I found the answer here
Paginator::make function we need to pass only the required values instead of all values. Because paginator::make function simply displays the data send to it. To send the correct offset paginated data to the paginator::make, the following method should be followed
$perPage = 5;
$page = Input::get('page', 1);
if ($page > count($publishedSaleUnits) or $page < 1) { $page = 1; }
$offset = ($page * $perPage) - $perPage;
$perPageUnits = array_slice($publishedSaleUnits,$offset,$perPage);
$pagination = Paginator::make($perPageUnits, count($publishedSaleUnits), $perPage);
this code work for me on laravel 8
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
public function paginate($items, $perPage = 5, $page = null, $options = [])
{
$page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
$items = $items instanceof Collection ? $items : Collection::make($items);
return new LengthAwarePaginator($items->forPage($page, $perPage), $items->count(), $perPage, $page, $options);
}
according to this refrence https://www.itsolutionstuff.com/post/how-to-create-pagination-from-array-in-laravelexample.html
Related
i have object given below and i wanted to pagination in this how can i get
$productListArray = array();
$productListObject = ((object)[
"id"=>$productList->id,
"title"=>$productList->title,
"slug"=>$productList->slug,
'categoryName'=>$categoryName[0]->cat_title,
'brand'=>$brandName[0]->brandname,
'minMrp'=>$minMrp,
'maxMrp' =>$maxMrp,
'minSellingPrice' => $minSellingPrice,
'maxSellingPrice' => $maxSellingPrice,
'rating'=>$productList->rating,
'rating_count' => $productList->rating_count,
'image' => $img[0]
])->paginate();
array_push($productListArray, $productListObject);
}
return response()->json($productListArray, 200);
Hi you can get the Current page using laravel paginator Paginator::currentPageResolver
public function index()
{
$currentPage = 3; // You can set this to any page you want to paginate to
// Make sure that you call the static method currentPageResolver()
// before querying users
Paginator::currentPageResolver(function () use ($currentPage) {
return $currentPage;
});
$users = \App\User::paginate(5);
return view('user.index', compact('users'));
}
thanks for your support i found solution,
first add on your header
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
use Illuminate\Pagination\LengthAwarePaginator;
then make an other function
public function paginate($items, $perPage = 5, $page = null, $options = [])
{
$page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
$items = $items instanceof Collection ? $items : Collection::make($items);
return new LengthAwarePaginator($items->forPage($page, $perPage), $items->count(), $perPage, $page, $options);
}
after than call this function where you want in class
$data = $this->paginate($productListArray);
return response()->json($data, 200);
last 2 lines already mentioned in my questions
thanks again
I have two models : Task and Comment
In my user profil I want to display tasks and comments sort by created date.
For this I do :
$timeline_array = $customer->comments;
$timeline_array = $timeline_array->toBase()->merge($customer->tasks);
//sort timeline event
$timeline_array = $timeline_array->sortByDesc(function($timeline_event){
return $timeline_event->created_at;
});
And I foreach my array in my view. It's work fine but if I have too much comments or tasks it's will be a big request so I want to add a paginator.
How can I do it ?
If I had a $timeline_array->paginate(5); at the end I get the error :
Method Illuminate\Support\Collection::paginate does not exist.
And I think it's not fixing my problem because I load all the comments and tasks before I paginate it.
Somebody have an idea/solution ?
Finally found a solution :
$timeline_array = $customer->comments;
$timeline_array = $timeline_array->toBase()->merge($customer->tasks);
//sort timeline event
$timeline_array = $timeline_array->sortByDesc(function($timeline_event){
return $timeline_event->created_at;
});
$item_per_page = 10;
$timeline_array = new LengthAwarePaginator($timeline_array->forPage(Paginator::resolveCurrentPage(), $item_per_page), count($timeline_array), $item_per_page, Paginator::resolveCurrentPage(), [
'path' => Paginator::resolveCurrentPath()
]);
Paginate method only works on a query builder or an eloquent ,
someone has created a gist over here where you can use paginate over an array or collection :
https://gist.github.com/vluzrmos/3ce756322702331fdf2bf414fea27bcb
Try using it like this :
$timeline_array = $customer->comments;
$timeline_array = $timeline_array->toBase()->merge($customer->tasks);
//sort timeline event
$timeline_array = $timeline_array->sortByDesc(function($timeline_event){
return $timeline_event->created_at;
});
$timeline_array = $this->paginate($items);
public function paginate($items, $perPage = 15, $page = null, $options = [])
{
$page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
$items = $items instanceof Collection ? $items : Collection::make($items);
return new LengthAwarePaginator($items->forPage($page, $perPage),
$items->count(), $perPage, $page, $options);
}
Or adding it as a Macro for collection in your App Service Provider for more better practice.
I've checked out the rather thin docs, but still unsure how to do this.
I have a collection. I wish to manually create a paginator.
I think I have to do something like, in my controller:
new \Illuminate\Pagination\LengthAwarePaginator()
But, what params do I need and do I need to slice the collection? Also how do I then display the 'links' in my view?
Could someone post a simple example how to create a paginator?
Please note, I don't want to paginate eloquent, eg. User::paginate(10);
Take a look at the Illuminate\Eloquent\Builder::paginate method for an example on how to create one.
A simple example of doing one using an eloquent model to pull out the results etc:
$page = 1; // You could get this from the request using request()->page
$perPage = 15;
$total = Product::count();
$items = Product::take($perPage)->offset(($page - 1) * $perPage)->get();
$paginator = new LengthAwarePaginator(
$items, $total, $perPage, $page
);
The first parameter accepts the results to display on the page that you're on
the second is the total number of results (The total number of items you're paginating, not the total number of items you're displaying on that page)
the third is the number per page you want to display
the fourth is the page that you're on.
You can pass in extra options as a fifth parameter if you want to customise things as well.
The links you should just be able to generate using the ->render() or ->links() method on the paginator as you would if you used Model::paginate()
With an existing collection of items you could do this:
$page = 1;
$perPage = 15;
$total = $collection->count();
$items = $collection->slice(($page - 1) * $perPage, $perPage);
$paginator = new LengthAwarePaginator(
$items, $total, $perPage, $page
);
You can create a Paginator like this:
$page = request()->get('page'); // By default LengthAwarePaginator does this automatically.
$collection = collect(...array...);
$total = $collection->count();
$perPage = 10;
$paginatedCollection = new \Illuminate\Pagination\LengthAwarePaginator(
$collection,
$total,
$perPage,
$page
);
According to the source code for LengthAwarePaginator (constructor)
public function __construct($items, $total, $perPage, $currentPage = null, array $options = [])
{
foreach ($options as $key => $value) {
$this->{$key} = $value;
}
$this->total = $total;
$this->perPage = $perPage;
$this->lastPage = (int) ceil($total / $perPage);
$this->path = $this->path != '/' ? rtrim($this->path, '/') : $this->path;
$this->currentPage = $this->setCurrentPage($currentPage, $this->pageName);
$this->items = $items instanceof Collection ? $items : Collection::make($items);
}
See more about LengthAwarePaginator
To display links in the view:
$paginatedCollection->links();
Hope this helps!
My Laravel pagination output is like laravel pagination used to be, but I need to change the data array for each object.
My output is:
As you can see, the data object has 2 items, which I need to change.
My code is:
$items = $this->items()
->where('position', '=', null)
->paginate(15);
Which returns the user items with pivot table, but I don't like the way the pivot table is shown in the JSON, so I decided to change the items and organize each item with the pivot before the item.
For this purpose, I tried to use foreach
foreach ($items->data as $item)
{
}
which giving my an error, for a reason I don't know:
Undefined property: Illuminate\Pagination\LengthAwarePaginator::$data"
status_code: 500
Any help?
The paginator's items is a collection. You can grab it and transform the data like so:
$paginator = $this->items()->where('position', '=', null)->paginate(15);
$paginator->getCollection()->transform(function ($value) {
// Your code here
return $value;
});
If you are familiar with tap helper here is the snippet that does exact same.
$paginator = tap($this->items()->where('position', '=', null)->paginate(15),function($paginatedInstance){
return $paginatedInstance->getCollection()->transform(function ($value) {
return $value;
});
});
We can't chain method getCollection to paginator instance because AbstractPaginator will return paginator's underlying collection. so the paginator instance will be transformed to Collection. So fix that we can use tap helper.
If you'd like to keep items paginated:
$itemsPaginated = $this->items()
->paginate(15);
$itemsTransformed = $itemsPaginated
->getCollection()
->map(function($item) {
return [
'id' => $item->id,
];
})->toArray();
$itemsTransformedAndPaginated = new \Illuminate\Pagination\LengthAwarePaginator(
$itemsTransformed,
$itemsPaginated->total(),
$itemsPaginated->perPage(),
$itemsPaginated->currentPage(), [
'path' => \Request::url(),
'query' => [
'page' => $itemsPaginated->currentPage()
]
]
);
There is a setCollection method for such purpose.
$items = Model::paginate(10);
$updatedItems = $items->getCollection();
// data manipulation
// ...
$items->setCollection($updateItems);
From the source code of /Illuminate/Pagination/AbstractPaginator.php
/**
* Set the paginator's underlying collection.
*
* #param \Illuminate\Support\Collection $collection
* #return $this
*/
public function setCollection(Collection $collection)
{
$this->items = $collection;
return $this;
}
Source
I could make shorter way. This returns edited $array instead of simple $paginated. This example modify file names.
This doc was useful for me.
$paginated=$query->paginate(12);
$array=$paginated->toArray();
foreach ($array['data'] as $r=>$record) {
$array['data'][$r]->gif=$array['data'][$r]->gif.".gif";
}
return $array;
Sample Example :
$franchiseData=[ 'id'=>1 ,'name'=>'PAnkaj'];
$models = $bookingsQuery->paginate(10);
$models->setCollection(collect($franchiseData));
return $models;
Note that $models->setCollection(collect($franchiseData)); you have to use collect() else you will get error.
getCollection
is one way to get the items. Another way is to use this
For ex- Assuming, user doesn't have name param and only have first_name and last_name
$userPaginatedData = User::paginate(15);
$users = $userPaginatedData->items();
foreach($users as $user) {
$user->name = $user->first_name . ' ' . $user->last_name;
}
return $userPaginatedData;
Now in the data key, you would see that each user has name param with it.
Laravel 8.9.0 has added the through method to AbstractPaginator.
It transforms each item in the slice of items using a callback, and keeps the items paginated.
$paginator = $this->items()->where('position', '=', null)->paginate(15);
$paginator->through(function ($value) {
// Your code here
return $value;
});
The source code:
/**
* Transform each item in the slice of items using a callback.
*
* #param callable $callback
* #return $this
*/
public function through(callable $callback)
{
$this->items->transform($callback);
return $this;
}
-Laravel 5.4
// example update column "photo"
// from "/path/to/photo.png"
// to "abc.com/path/to/photo.png"
foreach ($items as $item)
{
$path = $item->photo;
// Remove
$item->offsetUnset("photo");
// Set
$item->offsetSet("photo", url($path));
}
Laravel AbstractPaginator has methods setCollection() and getCollection()
<?php
$itemsPaginated = $this->items()->paginate(15);
$itemsPaginated->setCollection(
$itemsPaginated->getCollection()->transform(function ($item) {
// Your code here
return $item;
})
)
This is your paginated items...
$items = $this->items()
->where('position', '=', null)
->paginate(15);
I am using Laravel 8, can simply use each
$items->each(function ($item) {
// your code here
$item->custom_data = calcSomeData(); // sample
});
It does the same as this...
$items->getCollection()->transform(function ($item) {
// your code here
$item->custom_data = calcSomeData(); // sample
});
<?php
$itemsPaginated = $this->items()->paginate(15);
$itemsPaginated = json_encode($itemsPaginated);
foreach ($itemsPaginated->data as $key => $item) {
$results->data[$key]; //Modify
}
$itemsPaginated = json_encode($results);
you have to use below code in your blade
{!! $items->render() !!}
Ignore the pagination in laravel and hit the normal data
foreach ($items as $item)
{
}
I have to set pagination in array result.
Here is my code.
My Controller code
use Illuminate\Pagination\Paginator;
use Illuminate\Pagination\LengthAwarePaginator;
public function getCVList(){
.
.
.
$jobseeker1 = array_merge($jobseekers, $apps_array);
// in $jobseeker1 there are 6 result.
$jobseeker = $this->paginate($jobseeker1, 3);
return view('frontend.CVList', compact('jobseeker'));
}
public function paginate($items, $perPage, $pageStart = 1) {
$offSet = ($pageStart * $perPage) - $perPage;
// Get only the items you need using array_slice
$itemsForCurrentPage = array_slice($items, $offSet, $perPage, true);
return new LengthAwarePaginator($itemsForCurrentPage, count($items), $perPage, Paginator::resolveCurrentPage(), array('path' => Paginator::resolveCurrentPath()));
}
In blade temoplate i used rander() method and thare are also display pagination. But in all page display same record.
Thanks....
This is because you are not reading the page number clicked in the paginator, you are setting "3" always as the page to display. Try this:
//include the request
use Illuminate\Http\Request;
Now, read the current page:
public function getCVList(Request $request){
$perPage = 3;
// read the page number. When page number is not presented, then you
// set it as 0
$page = $request->get('page', 0);
$page = ($page == 0)? ($page * $perPage) : ($page * $perPage) - $perPage;
// now, calling the paginator do magic dynamically
$jobseeker = $this->paginate($jobseeker1, $perPage, $page);