Laravel : Fetch big Data from Api (URL) - laravel

Hwllo please i'm trying to fetch 15000 objects from api i used guzzle and file_get_contents but it's take long time and sometime the page doesn't load completely .
the controller :
$client = new \GuzzleHttp\Client();
$request = $client->get('http://XXX');
$response = $request->getBody();
$books = json_decode($response, true);
return view('n.search', compact('books'));
the books variable return 10000 items and take very time to load !
Could you help me to fix that ?

Here's an example just to give you an idea how I made this kind of problem.
in your controller, you should always paginate the returned collection of it. Like below
$users = User::all()->paginate(100);
laravel will return these 20 users together with some data like, next url, prev url, current_page and etc. These additional is important so that you will know what is the next url and prev url (these urls will be use in your api requests);
Now, after you get the paginated users, you need also to count the total users and return it together with users so that your front will know if how many pages it will display. Like this below
$total_users = User::all()->count();
now your final controller will look like this.
public function getUsers() {
$users = User::all()->paginate(20);
$total_users = User::all()->count();
return compact('users', 'total_users');
}
and it will return these results.
users // the 20 users
total_users // the total number of users in your table
currentPage
lastPage
perPage
hasMorePages
url
nextPageUrl
firstItem
lastItem
total
count
and so, every time you click pages in your datatable, you just need to use the next nextPageUrl and prevPageUrl as url in your api request.

Related

Create laravel api with page and limit results

I want to create an api with limit and page number, I am trying to use the api to load more data on scroll in my app.
Question 1: How can I limit the result, per each page.
For example
https://placewave.com/allusers?results=10&page=2
The URL above the page=2 show the second page with results=10 10 fetch result for page 2, same thing for page 3 with 10 result.
My Code
public function allUser(Request $request)
{
$pageno = $request->pageno;
return Users::where('active', 'online')
->limit(10)->offset($pageno)
->get();
}
Laravel's Pagination already handles limit and offset by passing the number of records you'd like per-page, and by examining the page=X query string parameter. So, you can rewrite your code as follows:
public function allUser(Request $request) {
return Users::where('active', 'online')->paginate($request->input('results', 10));
}
You'll likely want to provide some validation too, to prevent bad actors from messing with your results parameter:
public function allUser(Request $request) {
$perPage = $request->input('results', 10);
if (!is_numeric($perPage)) {
$perPage = 10;
}
return Users::where('active', 'online')->paginate($perPage);
}
->paginate($request->get('results', 10))

Passing data from blade to blade in Laravel

I have a page where you can create your workout plan. Second page contains "pre-saved" workouts and I want them to load by passing parameters from second page to first. If you directly access first page, you create your workout plan from scratch.
// first page = https://prnt.sc/y4q77z
// second page = https://prnt.sc/y4qfem ; where you can check which one you want to pass to first page
// final step looks like this: https://prnt.sc/y4qh2q - but my URL looks like this:
www.example.com/training/plan?sabloni%5B%5D=84&sabloni%5B%5D=85&sabloni%5B%5D=86
this 84,85,86 are IDS
Can I pass params without changing URL ? Like having only /training/plan without anything after ?
public function plan(Request $request){
$workout = false;
if($request->workout){
$workout = $request->workout;
$workout = SablonTrening::find($sabloni); // $workout = array [1,3,4,5,6]
}
return view('trener.dodaj_trening', compact('workout'));
}
If you are getting to the /training/plan page with GET request, you could simply change it to POST. That way the parameters would be hidden in the URL but would be present in the request body. You would need a new post route:
Route::post('/training/plan', 'YourController#plan')->name('training.plan');
And then, in the form where you are selecting these plans, change the method on submit:
<form action="{{route('training.plan')}}">
//Your inputs
</form>
Your method should still work if your inputs stay the same.
Note: Not sure you would still keep the functionalities that you need, since I can't see all the logic you have.
If you have any questions, let me know.
To pass data from on blade to another blade.
At the end of first post before redirect()-route('myroute') add $request->session()->put('data', $mydata);
At the begining of the route 'myroute', just get back your data with $data = $request->old('data');

Caching Eloquent models in Laravel 5.1

I've created an API using Laravel and I'm trying to find out how to cache Eloquent models. Lets take this example as one of the API endpoints /posts to get all the posts. Also within the method there are various filter options such as category and search and also gives the option to expand the user.
public function index()
{
$posts = Post::active()->ordered();
if (Input::get('category')) $posts = $posts->category(Input::get('category'));
if (Input::get('search')) $posts = $posts->search(Input::get('search'));
if ($this->isExpand('user')) $posts = $posts->with('user');
$posts = $posts->paginate($this->limit);
return $this->respondWithCollection($this->postTransformer->transformCollection($posts->all()), $posts);
}
I have been reading up and found in Laravel 4 you could cache a model like this
return Post::remember($minutes);
But I see this has been removed for Laravel 5.1 and now you have to cache using the Cache facade, but is only retrievable by a single key string.
$posts = Cache::remember('posts', $minutes, function()
{
return Post::paginate($this->limit);
});
As you can see, my controller method contains different options, so for the cache to be effective I would have to create a unique key for each option like posts_cagetory_5, posts_search_search_term, posts_category_5_search_search_term_page_5 and this will clearly get ridiculous.
So either I'm not coming across the right way to do this or the Laravel cache appears to have gone backwards. What's the best solution for caching this API call?
As the search is arbitrary, using a key based on the search options appears to be the only option here. I certainly don't see it as "ridiculous" to add a cache to for expensive DB search queries. I may be wrong as I came by this post looking for a solution to your exact problem. My code:
$itemId = 1;
$platform = Input::get('platform'); // (android|ios|web)
$cacheKey = 'item:' . $itemId . ':' . $platform;
$item = Item::find(1);
if( Cache::has($cacheKey) ) {
$result = Cache::get($cacheKey);
} else {
$result = $this->response->collection( $item, new ItemTransformer( $platform ) );
Cache::tags('items')->put($cacheKey, $result, 60); // Or whatever time or caching and tagged to be able to clear the lot in one go...
}
return $result;
I realise that my example has less complexity but it seems to cover all the bases for me. I then use an observer to clear the cache on update.

Route after filters are not being canceled when before filter fails

According to the docs, when the before filter of a route fails, the after filter is canceled, but it doesn't seem to be the case. I'm have a navigation pulled from the database. My route before filter whether on a Route::group or a plain route checks whether a person has visited that a certain page. If it hasn't, it will return a Redirect::route('new route').
The route after filter will add a row to a visited_pages table.
What is happening when I click on a link where I haven't visited its prerequisite page, is it will redirect. But - it will still add a row to the database. So the after isn't being canceled. It still fires.
The way I tested this was I was logged in and was on a page. Cleared the page visits from the database. I then clicked on my "classroom" nav item. This requires "orientation"
What gets entered into the database is a page visit in the following in this order:
Classroom
Classroom Instructions
Orientation
What I'm expecting to see is:
Orientation
Routes
Route::group(array("prefix"=>"classroom","before"=>"checkPrerequisite"),function()
{
Route::get('/',array(
'as'=>'classroom',
'uses'=>'ClassroomController#index',
'after'=>'addvisit',
));
//there are more routes here, but they don't need after filters.
Route::get('/instructions',array(
'as'=>'classroom.instructions',
'after'=>'addvisit',
function()
{
return View::make('classroom.instructions');
}
));
});
Before filter
Route::filter('checkPrerequisite', function($route, $request)
{
$sPrerequisite = Navigation::where('url','=',Navigation::where('url','=',Route::currentRouteName())->first()->prerequisite)->first();
// get the module id from session
$mod_id = Session::get('current_module');
// get the page from the user_page_visits
$page = Auth::user()
->pages()
->where('module_id','=',$mod_id)
->where('nav_id','=',$sPrerequisite->id)
->first();
if(!$page) return Redirect::route($sPrerequisite->url);
});
After filter
Route::filter('addvisit', function($route, $request, $response)
{
// get the current route
$current = Route::currentRouteName();
// get the id of the navigation item with this route
$nav_id = Navigation::where('url','=',$current)->first()->id;
// get the module id from cache
$mod_id = Session::get('current_module');
// see if the page has been visited
$page = Auth::user()
->pages()
->where('module_id','=',$mod_id)
->where('nav_id','=',$nav_id)
->first();
if($page)
{
// if it has been visited, increment the visits column by 1
$page->increment('visits');
}
else
{
// otherwise, create a new page visit
$visit = new UserPageVisits;
$visit->user_id = Auth::user()->id;
$visit->module_id = $mod_id;
$visit->nav_id = $nav_id;
$visit->save();
}
});

laravel clean up empty query vars

I want to remove empty query vars from a url in my controller. my url is /search?qi=yoga&q= notice that q is empty. Sometimes qi will be empty. How can I remove these? Seems like a simple issue, but I can't seem to find a elegant solution.
function search() {
$qi = Request::get('qi');
$q = Request::get('q'));
$results = getResults($qi, $q);
return View::make('search.results', compact('results'));
}
You could do that in the next request, but you would have to Redirect::refresh() or Redirect::to($url) with a clean url, like
$items = Redirect::query();
$items = $this->removeEmptyItems($items); /// you'll have to create this method!
return Redirect::route('your.current.route', $items);
As you can see, this will clean up your url, but it requires a new request.
But this looks like something you have in your current request and I'm afraid Laravel cannot change a URL in the browser for you. If this is a form submission query, Javascript can help you prevent from sending those empty queries:
$('form').submit(function(){$('input[value=]',this).remove();return true;})
I suggest this:
function search()
{
$search = array_filter(Request::all()); // or only(..) / except(..)
$results = getResults($search);
}

Resources