laravel 4 paginate collection - laravel

I cant create a proper pagination system using laravel 4. I have the following models and function that return collections:
Model Restaurant:
public function fooditem()
{
return $this->hasMany('Fooditem','rest_id');
}
public function get_rest_foods($id){
return Restaurant::find($id)->fooditem->toArray();
}
The second function returns all food items for a certain restaurant as an array. I also use this in an API call.
in my controller i have this:
$food = $food->get_rest_foods($id);
$paginator = Paginator::make($food, 10, 5);
I pass the paginator to the view and it shows the links ok but also shows all my item from the food array.
I tried using
public function get_rest_foods($id){
return Restaurant::find($id)->fooditem->paginate(5);
}
but i get an error:
FatalErrorException: Error: Call to undefined method Illuminate\Database\Eloquent\Collection::paginate()
I searched this and many other sites but cant understant how to paginate a collection.
Thanks for your help

The paginator must get the items that it would normally get from a database query with an offset/limit statement.
So when you have a collection with all items, you should do the offset/limit yourself.
$food = $food->get_rest_foods($id);
$page = 1;
if( !empty(Input::get['page']) ) {
$page = Input::get['page'];
}
$perPage = 15;
$offset = (($page - 1) * $perPage);
$food = Paginator::make($food->slice($offset,$perPage, true)->all(), $food->count(), $perPage);

I created a subclass of Collection and implemented my own paginate method
public function paginate($perPage) {
$pagination = App::make('paginator');
$count = $this->count();
$page = $pagination->getCurrentPage($count);
$items = $this->slice(($page - 1) * $perPage, $perPage)->all();
$pagination = $pagination->make($items, $count, $perPage);
return $pagination;
}

The Laravel paginator does not do any of the splicing for you. Typically the paginator should be used hand in hand with Eloquent/Fluent. In your second example you should be doing it like this.
return Restaurant::find($id)->fooditem()->paginate(5);
Without calling the actual method you'll just be getting a collection of results and not the query builder instance.
If you want to use the paginator manually you need to pass in the spliced array (the correct items for the current page). This usually involves determining the current page, the total results and total pages. That's why, where possibly, it's best to use it with Eloquent or Fluent.

Related

Looking up model in Laravel after returning array of objects in Controller

I am trying to do something I've never done before in Laravel and cannot figure out how to do it.
I have the following code in my Controller:
public function show($id)
{
//Get application for drug
$application = PharmaApplication::where('ApplNo', $id)->first();
//Get all products for given application (i.e. the different quantities and forms drug comes in)
$product = PharmaProduct::where('ApplNo', $id)->get();
foreach($product as $product){
$product->ProductNo;
}
//Get Marketing Status for drug
$marketingStatus = DB::table('pharma_marketing_statuses')
->where('ApplNo', $id)
->where('ProductNo', $product->ProductNo)
->get();
//Lookup marketing status Description
$marketingStatusDescription = PharmaMarketingSatusLookup::where('MarketingStatusID', $marketingStatus->MarketingStatusID);
return view('profiles.drug', compact('application', 'product', 'marketingStatus', 'marketingStatusDescription'));
}
I am trying to accomplish the following:
Get the application for a drug - this part of my code works
Return an array of objects for the products (i.e. 7 products that belong to one application). I can do this but get stuck going to the next part.
Next, I have to use the array of objects and search a table with the following columns: MarketingStatusID, ApplNo, ProductNo. I know how to query this table and get one row, but the problem is I have an array that I need to search. I imagine I have to use a loop but don't know where.
Finally, I use the MarketingStatusID to retrieve the MarketingStatusDescription which I will know how to do.
I am also getting an error message that says:
Class 'App\Http\Controllers\profiles\PharmaMarketingSatusLookup' not found
In my Controller, I have use App\PharmaMarketingStatusLookup; so I am not sure why it is searching the Controllers folder
You have a typo in your class
From PharmaMarketingSatusLookup change to PharmaMarketingStatusLookup
App\Http\Controllers\profiles\PharmaMarketingStatusLookup
USE whereIn
use App\PharmaApplication;
use App\PharmaProduct;
use App\PharmaMarketingSatusLookup;
public function show($id)
{
$application = PharmaApplication::where('ApplNo', $id)->first();
$products = PharmaProduct::where('ApplNo', $id)->get();
$productid = array();
foreach($products as $product){
$productid[] = $product->ProductNo;
}
$marketingStatus = DB::table('pharma_marketing_statuses')
->where('ApplNo', $id)
->whereIn('ProductNo', $productid)
->get();
$marketingStatusDescription = PharmaMarketingSatusLookup::where('MarketingStatusID', $marketingStatus->MarketingStatusID);
return view('profiles.drug', compact('application', 'product', 'marketingStatus', 'marketingStatusDescription'));
}

Laravel 6, passing multiple arrays from a controller to a view

I'm brand new to Laravel, I need to display around 8 different drop down menus on a page all populated from tables in my Db, I am using blades.
In my controller I can create various types of arraysin one function (using eloquent) and I can dd(); them out correctly one at a time, my issue appears to be that you can only pass one array through a controller to a view. I have tried various options I found here but without success, including ->with and compact(). I have tried defining the arrays in the controller one at a time and passing them using compact() all result in errors either the variable not defined or trying to get an non-object. I am obviously going about this all wrong any help would be great.
This is not a code issue (hence no code posted) I think it more of a Laravel issue that I don't yet understand, thanks in advance.
Try like this
class YourController extends Controller{
public function yourMethod(){
$arr1 = [];
$arr2 = [];
return view('view.name', ['arr1' => $arr1, 'arr2' => $arr2]);
}
}
If you have:
$array1 = [...];
$array2 = [...];
Then you can:
return view('path.to.view', compact('array1', 'array2');
This is my route from web.php and my controller from ReservationContoller any help as to my the arrays wont pass would be great, many thanks.
Route::get('/client/{client}/reservation/{reservation}', 'ReservationController#getReservation');
public function getReservation($client, $reservation)
{
$client = Client::findOrFail($client);
$reservation = Reservation::where('client_id', $client->id)->get();
$company = Company::where('type', 'staghen')
->where('status', 'Active')
->orderBy('comp_name')
->pluck('comp_name', 'id');
$cl = array(['client' => $client]);
$res = array(['reservation' => $reservation]);
$comp = array(['company' => $company]);
return view('admin.reservations.reservation', compact('$cl', '$res', '$comp'));
}

How I can paginate DB::select(...) result and get links() method?

I have a big and difficult SQL query. It works fine for me. And I have the following code in the controller:
public function index(OpenRegDepDataReportInterface $openRegDepDataReport, Request $request): Renderable
{
$applications = $openRegDepDataReport->getReport($advertisers, $category);
return view('applications.index', compact('applications'));
}
So, the method getReport gives me a result of DB::select('<here is my big diffecult SQL>'), and, as well known it's an array.
But as you can see I'm trying to pass the result on a view. And, of course, I would like to call $applications->links() in the view, like for eloquent collection. Which is proper and faster way to do that?
doc
To display pagination at the table, you must call the select and then the pagination method.
in Controller:
$test = DB::table('users')->select('id')->paginate(10);
in View:
$test->links();
So based on the documentation if your $applications returns a Query Builder result, then just append ->paginate(10); to it.
https://laravel.com/docs/master/pagination#paginating-query-builder-results
$applications = $openRegDepDataReport->getReport($advertisers, $category)->paginate(10);
Simple answer, use paginate() method:
$basicQuery = DB::select(DB::raw("<here is the big diffcult SQL query>"));
However, paginate() works only on collections, and since you have an array of objects, you need to turn it into a collection first, using the forPage() method:
The forPage method returns a new collection containing the items that would be present on a given page number. The method accepts the page number as its first argument and the number of items to show per page as its second argument:
$collection = collect($basicQuery);
$chunk = $collection->forPage(2, 3);
$chunk->all();
Complicated answer: build a paginator instance yourself:
$perPage = 10;
$page = $request->input("page", 1);
$skip = $page * $perPage;
if($take < 1) { $take = 1; }
if($skip < 0) { $skip = 0; }
$basicQuery = DB::select(DB::raw("<here is the big diffcult SQL query>"));
$totalCount = $basicQuery->count();
$results = $basicQuery
->take($perPage)
->skip($skip)
->get();
$paginator = new \Illuminate\Pagination\LengthAwarePaginator($results, $totalCount, $take, $page);
return $paginator;
I would recommend using the paginate() method.
You can read more about Laravel's pagination.

Laravel pagination not working with array instead of collection

I'm trying to paginate an array data set and it has proven more challenging than I thought.
I'm using Laravel 5
So I have an abstract interface/repository that all my other models extend to and I created a method inside my abstract repository call paginate.
I've included both
use Illuminate\Pagination\Paginator;
and
use Illuminate\Pagination\LengthAwarePaginator;
Here is the method
public function paginate($items,$perPage,$pageStart=1)
{
// Start displaying items from this number;
$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()));
}
So as you can imagine this function accepts an array of $items a $perPage variable that indicates how many to items to paginate and a $pageStart that indicates from which page to start.
The pagination works and I can see LengthAwarePaginator instance when I'm doing a dd() , all of it's values seem fine.
The problem starts when I'm displaying the results.
When I do {!! $instances->render() !!} The paginator links display's fine, the page parameter changes according to links but the data is not changing.
Data is the same in every page. When I'm using Eloquent for example Model::paginate(3) everything works fine, but when I dd() this LengthAwarePaginator it's identical to the LengthAwarePaginator instance of my custom paginator with the exception that it paginates an array ofcourse and not a collection.
You are not passing the current page, like you should so you also get same array. This will work
public function paginate($items,$perPage)
{
$pageStart = \Request::get('page', 1);
// Start displaying items from this number;
$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()));
}
Your function will also work if you pass correct value for $pageStart - Request::get('page', 1)

filtering a paginated eloquent collection

I am trying to filter a paginated eloquent collection, but whenever I use any of the collection methods, I lose the pagination.
$models = User::orderBy('first_name','asc')->paginate(20);
$models = $models->each(function($model) use ($filters) {
if(!is_null($filters['type'])) {
if($model->type == $filters['type'])
return $model;
}
if(!is_null($filters['state_id'])) {
if($model->profile->state_id == $filters['state_id'])
return $model;
}
if(!is_null($filters['city_id'])) {
if($model->profile->city_id == $filters['city_id'])
return $model;
}
});
return $models;
I am working with Laravel 4.2, is there any way to persist the pagination?
An answer to the titular question, which is possible in Laravel 5.2+:
How to filter the underlying collection of a Paginator without losing the Paginator object?
You can eject, modify and inject the collection as follows:
$someFilter = 5;
$collection = $paginator->getCollection();
$filteredCollection = $collection->filter(function($model) use ($someFilter) {
return $model->id == $someFilter;
});
$paginator->setCollection($filteredCollection);
The Paginator is built on an underlying collection, but indeed when you use any of the inherited Collection methods they return the underlying collection and not the full Paginator object: collection methods return collections for chaining together collection calls.
Expanding on mininoz's answer with your specific case:
//Start with creating your object, which will be used to query the database
$queryUser = User::query();
//Add sorting
$queryUser->orderBy('first_name','asc');
//Add Conditions
if(!is_null($filters['type'])) {
$queryUser->where('type','=',$filters['type']);
}
if(!is_null($filters['state_id'])) {
$queryUser->whereHas('profile',function($q) use ($filters){
return $q->where('state_id','=',$filters['state_id']);
});
}
if(!is_null($filters['city_id'])) {
$queryUser->whereHas('profile',function($q) use ($filters){
return $q->where('city_id','=',$filters['city_id']);
});
}
//Fetch list of results
$result = $queryUser->paginate(20);
By applying the proper conditions to your SQL query, you are limiting the amount of information that comes back to your PHP script, and hence speeding up the process.
Source: http://laravel.com/docs/4.2/eloquent#querying-relations
I have a scenario that requires that I filter on the collection, I cannot rely on the Query Builder.
My solution was to instantiate my own Paginator instance:
$records = Model::where(...)->get()->filter(...);
$page = Paginator::resolveCurrentPage() ?: 1;
$perPage = 30;
$records = new LengthAwarePaginator(
$records->forPage($page, $perPage), $records->count(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]
);
return view('index', compact('records'));
Then in my blade template:
{{ $records->links() }}
paginate() is function of Builder. If you already have Collection object then it does not have the paginate() function thus you cannot have it back easily.
One way to resolve is to have different builder where you build query so you do not need to filter it later. Eloquent query builder is quite powerful, maybe you can pull it off.
Other option is to build your own custom paginator yourself.
You can do some query on your model before do paginate.
I would like to give you some idea. I will get all users by type, sort them and do paginate at the end. The code will look like this.
$users = User::where('type', $filters['type'])->orderBy('first_name','asc')->paginate(20);
source: http://laravel.com/docs/4.2/pagination#usage
This was suitable for me;
$users = User::where('type', $filters['type'])->orderBy('first_name','asc')->paginate(20);
if($users->count() < 1){
return redirec($users->url(1));
}
A workaround when mixing search parameters and pagination, since default pagination won't keep the search parameters, using GET:
$urlSinPaginado = url()->full();
$pos=strrpos(url()->full(), 'page=');
if ($pos) {
$urlSinPaginado = substr(url()->full(), 0, $pos-1);
}
[...]
->paginate(5)
->withPath($urlSinPaginado);
sample generated pagination link: http://myhost/context/list?filtro_1=5&filtro_2=&filtro_3=&filtro_4=&filtro_5=&filtro_6&page=8
You can simply use this format in views
{!! str_replace('/?', '?', $data->appends(Input::except('page'))->render()) !!}
For newer versions of Laravel, you can use this:
$data->paginate(15)->withQueryString();

Resources