Laravel pagination per_page/limit does not get added to nextPageUrl - laravel

When using Model::paginate($per_page) the pagination is returned including the per_page variable. The problem is that the when you get the nextPageUrl() it only provides a ?page=x url variable, so if you want to specify the per_page in the url e.g.
https://example.com/api/users?page=1&per_page=100
It doesn't get added by the LengthAwarePaginator to the next/prev urls.
Is there something I'm missing or do I have to manually append this to the URL?

If you want to add that to every single url there's a method in your paginator that allows you to add query strings.
Laravel 5.4+ :
//fetch the get parameter per_page, if doesn't exists defaults it to 100
$per_page = \Request::get('per_page') ?: 100;
$users = App\User::paginate($per_page);
$users->appends(['per_page' => $per_page])
return view('users', ['users' => $users]);
Old solution 5.3:
//fetch the get parameter per_page, if doesn't exists defaults it to 100
$per_page = \Request::get('per_page') ?: 100;
$users = User::paginate($per_page);
$users->addQuery('per_page', $per_page);
return view('users', ['users' => $users]);

Related

Laravel add more than 2 parameters in controller

In my controller I currently give 2 parameters to the view like this:
return view('home')->with(['listings'=>$listings, 'featured_listings'=>$featured_listings]);
But when I add a third parameter it gives me the following message:
Too few arguments to function App\Http\Controllers\HomeController::index(), 0 passed and exactly 1 expected
Is there any way to give more than 2 parameters to a view from a controller?
This very simple to pass data to view more than one. There are many ways to pass data to view. I suggest you
return view('home',compact('listings','featured_listings','your_data',...));
If you can put parameter like
return view('home', [
'listings' => $listings,
'featured_listings' => $featured_listings,
..............................
]);
then you can put more than 2.
There are few ways you can share data with a view. It is not limited to 1 or 2 parameters. You can share unlimited parameters.
Option 1
$categories = ProductCategory::all();
$brands = ProductBrand::all();
$product = Product::first();
return view('product.edit', compact(['categories', 'brands', 'product']));
Option 2
$categories = ProductCategory::all();
$brands = ProductBrand::all();
$product = Product::first();
return view('product.edit', ['categories' => $categories, 'brands' => $brands, 'product' => $product]);
Option 3
$categories = ProductCategory::all();
$brands = ProductBrand::all();
$product = Product::first();
return view('product.edit')->with('categories', $categories)->with('brands', $brands)->with('product', $product);

how to check if cart has already added not allow more and update if different cart

I'm using composer require gloudemans/shoppingcart I am not sure how to maintain amount.
When i'm using one route that says add item when i'm using this route adding multiple item
How can i conditionally setup to add item in cart if this is unique
public function bookItem($id) {
$item = Item::where([
'status' => '1',
'id' => $id
])->first();
$product = Cart::add($item->id, $item->name, 1, $item->price); // This should not call always if it has not generated a row id then it should call
Cart::update($product->rowId, ['price' => 200]); // Will update the price if it is differ
return redirect()->route('book.item', ['id' => $item->id]);
}
I am not sure how to manage it. please guide
Looks like the package has a getContents() function that gathers all items into a collection. It also has a search(Closure) function that calls getContents() and then uses Laravel's filter function on the collection and returns the result.
$search = Cart::search(function($key,$value) use ($item) {
return $value === $item->id;
})->first();
if(!empty($search)){
Cart::update($search->rowId, ['price' => 200]);
}
else {
$product = Cart::add($item->id, $item->name, 1, $item->price);
}
Definitely check out the Laravel collection docs if you aren't familiar. This is the entey on filters:
https://laravel.com/docs/5.8/collections#method-filter

Fetching a cached Eloquent collection of 10 items times out

I'm building a search functionality that returns large collections which are paginated using a LengthAwarePaginator. I'm trying to cache results using a key called $searchFilter_$query_$offsetPages for a single page of returned results (10 items). It goes into the cache just fine. However, it times out when I try to check using Cache::has($key) or fetch using Cache::get($key).
The same problem occurs in the browser as well as in artisan Tinker. Strangely, when I put a random set of 10 items into the cache in Tinker and fetch them back, everything works fine. I'm using Redis as the cache driver.
Here is my controller method:
public function search($filter, $query, $layout, Request $request) {
if($layout == "list-map") {
return view("list-map")->with(['filter' => $filter, 'query' => $query, 'layout' => 'list-map']);
} else {
$offsetPages = $request->input('page', 1) - 1;
$cacheKey = $filter . "_" . $query . "_" . $offsetPages;
if(Cache::has($cacheKey)) {
\Log::info("fetching results from cache");
$data = Cache::get($cacheKey);
$totalCt = $data[0];
$results = $data[1];
} else {
$results = $this->getResults($filter, $query);
$totalCt = $results->count();
$results = $results->slice($offsetPages, $this->resultsPerPage);
\Log::info("caching results");
Cache::put($cacheKey, [$totalCt, $results], 5);
}
$results = new LengthAwarePaginator($results,
$totalCt,
$this->resultsPerPage,
$request->input('page', 1),
['path' => LengthAwarePaginator::resolveCurrentPath()]
);
return view($layout)->with(['filter' => $filter, 'query' => $query, 'layout' => $layout, 'results' => $results]);
}
}
So, the issue was that many of the models in the collection returned from my getResults() method were obtained via relationship queries. When I would dd($results) on the single page of 10 results, I could see that there was a "relations" field on each model. Inside that array were thousands of recursively related models based on the relationship I originally queried. I was unable to find any information about an option to not eager load these related models. Instead I came up with a bit of a hacky workaround to fetch the models directly:
$results = $results->slice($offsetPages, $this->resultsPerPage);
//load models directly so they don't include related models.
$temp = new \Illuminate\Database\Eloquent\Collection;
foreach($results as $result) {
if(get_class($result) == "App\Doctor") {
$result = Doctor::find($result->id);
} else if(get_class($result == "App\Organization")) {
$result = Organization::find($result->id);
}
$temp->push($result);
}
$results = $temp;
\Log::info("caching results");
Cache::put($cacheKey, [$totalCt, $results], 5);
If anyone knows the best practice in this situation, please let me know. Thanks!
Edit:
I've found a better solution instead of the above workaround. If I query my relationships like this: $taxonomy->doctors()->get() rather than $taxonomy->doctors, it does not load in the huge recusive relations.
I dont really see why your code doesn't work. The only potential problems I see are the cache keys, which could contain problematic characters, as well as the way you check for a cached value. As you are using Cache::has($key) before Cache::get($key), you could end up with a race condition where the first call returns true and the latter null because the cached value timed out just between the two calls.
I tried to address both issues in the following snippet:
public function search($filter, $query, $layout, Request $request)
{
if($layout == "list-map") {
return view("list-map")->with(['filter' => $filter, 'query' => $query, 'layout' => 'list-map']);
} else {
$offsetPages = $request->input('page', 1) - 1;
$cacheKey = md5("{$filter}_{$query}_{$offsetPages}");
$duration = 5; // todo: make this configurable or a constant
[$totalCount, $results] = Cache::remember($cacheKey, $duration, function () use ($filter, $query) {
$results = $this->getResults($filter, $query);
$totalCount = $results->count();
$filteredResults = $results->slice($offsetPages, $this->resultsPerPage);
return [$totalCount, $filteredResults];
});
$results = new LengthAwarePaginator($results,
$totalCount,
$this->resultsPerPage,
$request->input('page', 1),
['path' => LengthAwarePaginator::resolveCurrentPath()]
);
return view($layout)->with(compact('filter', 'query', 'layout', 'results'));
}
}
The inbuilt function Cache::remember() doesn't use Cache::has() under the hood. Instead, it will simply call Cache::get(). As this function will return null as default if no cache was hit, the function can easily determine if it has to execute the closure or not.
I also wrapped the $cacheKey in md5(), which gives a consistently valid key.
Looking at the following part of your code
$results = $this->getResults($filter, $query);
$totalCount = $results->count();
$filteredResults = $results->slice($offsetPages, $this->resultsPerPage);
I am quite sure the whole search could be improved (independently of the caching). Because it seems you are loading all results for a specific search into memory, even if you throw away most parts of it. There is certainly a better way to do this.

Laravel - Change URL parameters using GET

I have RESTful API built on Laravel.
Now I'm passing parameter like
http://www.compute.com/api/GetAPI/1/1
but I want to pass parameter like
http://www.compute.com/api/GetAPI?id=1&page_no=1
Is there a way to change Laravel routes/functions to support this?
you can use link_to_route() and link_to_action() methods too.
(source)
link_to_route take three parameters (name, title and parameters). you can use it like following:
link_to_route('api.GetAPI', 'get api', [
'page_no' => $page_no,
'id' => $id
]);
If you want to use an action, link_to_action() is very similar but it uses action name instead of route.
link_to_action('ApiController#getApi', 'get api', [
'page_no' => $page_no,
'id' => $id
]);
href text
with these methods anything after the expected number of parameters is exceeded, the remaining arguments will be added as a query string.
Or you can use traditional concatination like following:
create a route in routes.php
Route::get('api/GetAPI', [
'as' => 'get_api', 'uses' => 'ApiController#getApi'
]);
while using it append query string like this. you can use route method to get url for required method in controller. I prefer action method.
$url = action('ApiController#getApi'). '?id=1&page_no=1';
and in your controller access these variables by following methods.
public function getApi(Request $request) {
if($request->has('page_no')){
$page = $request->input('page_no');
}
// ...your stuff
}
Or by Input Class
public function getApi() {
if(Input::get('page_no')){
$page = Input::get('page_no');
}
// ...your stuff
}
Yes you can use those parameters, then in your controllers you can get their values using the Request object.
public function index(Request $request) {
if($request->has('page_no')){
$page = $request->input('page_no');
}
// ...
}

laravel queries in controller displays a variable

Right now i'm working with Crinsane Laravel Shopping cart. I'm using 5.0
My problem is: I cant turn the variable into values, instead system display it as it is (variable) .
I want to know how exactly query database with specific column base on id , and turn then into values not variable it self .
My code :
public function addcart (){
$products = Product::find(Input::get('id'));
$qty = Input::get('qty');
Cart::add('$products->id', 'products->name', $qty, $products->price, array('size' => 'large'));
$cart_content = Cart::content();
return view('pages.cart')->with('cart_content', $cart_content);
}
When run , it turn to display like this :
Don't wrap $products->id and $products->name in quotes:
Cart::add(
$products->id,
$products->name,
$qty,
$products->price,
['size' => 'large']
);

Resources