Larvel 9 pagination with limit - php

Working inside a Laravel 9 project and need to limit my returned results and then paginate, I have over 40,000 total rows and would like to show no more than 500 entries as a paginated list.
When I add limit to my query it's still returning all my rows and isn't limiting, what am I missing?
$query = Application::query();
$query = $query->with(['payday', 'response', 'apiLinks']);
if ($request->input('filters.from')) {
$query->where('created_at', '>=', Carbon::parse($request->input('filters.from')));
}
if ($request->input('filters.to')) {
$query->where('created_at', '<=', Carbon::parse($request->input('filters.to')));
}
if ($request->input('search')) {
foreach ($request->input('search') as $field => $value) {
$query->where($this->mapSearchField($field), $value);
}
}
$query = $query->limit(500);
$query = $query->paginate(25);
return response()->json([
'applications' => $query ?? []
], 200);

There is no built in solution to limit the maximum number of pages with a LengthAwarePaginator
You will need to instantiate it yourself.
$query = Application::query();
$query = $query->with(['payday', 'response', 'apiLinks']);
if ($request->input('filters.from')) {
$query->where('created_at', '>=', Carbon::parse($request->input('filters.from')));
}
if ($request->input('filters.to')) {
$query->where('created_at', '<=', Carbon::parse($request->input('filters.to')));
}
if ($request->input('search')) {
foreach ($request->input('search') as $field => $value) {
$query->where($this->mapSearchField($field), $value);
}
}
//current page
$page = \Illuminate\Pagination\Paginator::resolveCurrentPage('page');
//total number of element (we force max to 500)
$total = min($query->count(), 500);
//the items for current page (25 is items per page)
$items = $query->forPage($page, 25)->get();
//we instantiate the paginator manually (25 is items per page)
$results = new \Illuminate\Pagination\LengthAwarePaginator($items, $total, 25, $page);
return response()->json([
'applications' => $results
], 200);
Yeah, no out of the box answer for this one.

Related

Laravel variable cache

I need to cache the results from Steam API parsing. And so the cached result lasts 15 minutes. I have a code:
public function load()
{
if (Auth::guest()) return response()->json(['success' => false, 'msg' => 'You need login!']);
$inventory = $this->getInventory(file_get_contents('http://steamcommunity.com/inventory/' . $this->user->steamid64 . '/570/2?l=russian&count=5000', true));
if (!$inventory) {
return response()->json(['success' => false, 'msg' => 'Error']);
}
$items = [];
$items_with_prices = json_decode(\Storage::get('prices.txt'));
$items_with_prices_by_key = [];
foreach ($items_with_prices->items as $item_price_key => $item_price_data) {
$items_with_prices_by_key[$item_price_key] = $item_price_data->price;
}
foreach ($inventory['rgInventory'] as $info) {
$item = $inventory['rgDescriptions'][$info['classid'] . '_' . $info['instanceid']];
if ($item['tradable'] == 0) continue;
$price = 0;//InventoryController::getItemPrice($item['market_hash_name']);
if (array_key_exists($item['market_hash_name'], $items_with_prices_by_key)) {
$price = $items_with_prices_by_key[$item['market_hash_name']];
}
if (!$price) continue;
if ($price < 1) $price = 0.64;
$type = $this->getType($item['type']);
$items[] = [
'id' => $info['id'],
'classid' => $item['icon_url'],
'price' => round($price, 2),
'type' => $type
];
}
usort($items, function($a, $b){
return ($b['price'] - $a['price']);
});
return response()->json(['success' => true, 'items' => $items]);
}
This code only works when a site user clicks on the "show my items" button and a request is sent to the list of user items in Steam Dota 2. Now if click constantly to get a list of items, Steam can block the server’s IP address for 24 hours. As I understand it, I need to throw the result of a $inventory variable into the cache. I create database table cache with fields id, user_id, items, date.
How can I now cache the result from a $inventory variable of 15 minutes?
Here is basic caching in laravel
$rememberTimeInSeconds = 3600;
$cachedResult = Cache::remember('name_of_your_cache', $rememberTimeInSeconds, function(){
// It can be any logic I just showing a simple query.
$users = User::all();
return $users;
});

How to fetch and return record with total number of records in codeigniter

I am working with rest api using codeigniter,I want to fetch records + total records + per page records,but not worked for me
Here is my function in controller
<?php
public function search_shop()
{
$users['rec'] = $this->Model_users->find_shop($_POST);
if($users['rec']!="")
{
$responseJSON = array("Status" => true,"Result" => $users['rec']);
header("content-type:application/json");
$response = json_encode($responseJSON);
echo $response;
}
else
{
$responseJSON = array("Status" => false,"Message" => "There is no matching record");
header("content-type:application/json");
$response = json_encode($responseJSON);
echo $response;
}
}
And here is my "find_shop" function in model,How can i fetch all records with total number of records and per page records ?
public function find_shop()
{
$add_data['page_number'] = ($this->input->post('page_number') && !empty($this->input->post('page_number'))) ? $this->input->post('page_number') : NULL;
$add_data['search'] = ($this->input->post('search') && !empty($this->input->post('search'))) ? $this->input->post('search') : NULL;
$records="10";
$mins="10";
if($add_data['page_number']=="" || $add_data['page_number']=="0" || $add_data['page_number']=="1")
{
$starting_row="0";
$last_row="10";
}
else
{
$last_row="9";
$cd="1";
$lim="10";
$starting_row=$add_data['page_number']*$lim-$last_row-$cd;
$last_row=$add_data['page_number']*$records-$cd;
}
$this->db->select('*');
$this->db->from('shop s');
$this->db->like('shop_name',$add_data['search']);
$this->db->or_like('city',$add_data['search']);
$this->db->limit($last_row,$starting_row);
$query = $this->db->get();
if ( $query->num_rows() > 0 )
{
$row = $query->result_array();
return $row;
return $query->num_rows;
}
else
{
return false;
}
}
?>
Try This, Hope it helps
in the Controller please test this after model related changes
$users = array();
$users = $this->Model_users->find_shop($_POST);
echo'<pre>';print_r($users);die;
in the Model, There is a mistake in the return, You cannot do two return at the same time
$query = $this->db->get();
if ( $query->num_rows() > 0 )
{
$data = array();
$data['row'] = $query->result_array();
$data['total_records'] = $this->db->count_all_results('shop');
$data['per_page_records'] = $query->num_rows();
return $data;
}else{
return array('row'=>array(),'total_records'=>'0','per_page_records'=>'0');
}
Change your if condition like this:
$records="10"; // This is records per page showing
if ($add_data['page_number']=="" || $add_data['page_number']=="0" || $add_data['page_number']=="1")
{
$starting_row="0";
} else {
$starting_row = ($add_data['page_number'] - 1) * $records;
}
You need to set limit and offset of query. For example: limit 0, 10: this will show first 10 records. Here 0 is starting point and 10 is the length of records which you want to show. 10 will always be same as you are showing 10 records. Limit 10, 10: it will show records from 10 to 19. hope it clears your doubt.

DataTable with Ajax is not working well after use serverSide: true

I have an user table using DataTables, that will contain more than 200 rows. It's look so fine when I'm using DataTables for default the "pageLength": 10, and this is the table example.
Username | Type | Request |
user01 1 request01
user02 1 request02
user03 2 request03
user04 1 request04
user05 1 request05
user06 1 request06
user07 1 request07
user08 1 request08
user09 1 request09
user10 1 request10
Showing 1 to 10 of 200 entries FirstPrevious123...20NextLast
So, for reducing the loading time, I decide to use "processing": true and "serverSide": true. Then I got some issue with this "serverSide" : true, It's print 200 rows of data in table.
Showing 0 to 0 of 0 entries (filtered from NaN total entries). Then the pagination is still print and after I click the page 2, it's doing nothing.
I wan't the DataTables is getting the 10 data for the first, after pagination 2 is clicked, it will get 10 more and so on.
I'm using CodeIgniter, here is my code :
On my Views + Js :
<select name="task" id="task">
<option value="1">Task 1</option>
<option value="2">Task 2</option>
</select>
<table id="user-request" class="table">
<thead>
<tr>
<th>Username</th>
<th>Type</th>
<th>Request</th>
</tr>
</thead>
</table>
<script>
... on task change ...
... var task = $("#task").val(); ...
$('#user-request').DataTable({
'processing': true,
'serverSide': true,
'ajax': {
'type': 'POST',
'url': base_url+'user/get_user_request',
'data': {"task":task,"csrf_token":$("input[name=csrf_token]").val()}
}
})
</script>
Note : Task is a different group, example like Class 1 or Class 2, Orchard University or Harvard University
On my Controller :
$task = $this->input->post('task', TRUE);
$user_request = $this->model->all_user_request(task);
foreach ($user_request as $ur)
{
$arr = array();
$arr[] = $ur->username;
$arr[] = $ur->type;
$arr[] = $ur->request;
$data[] = $arr;
}
$output = array(
"data" => $data
);
if (COUNT($output) > 0)
{
echo json_encode($output);
}
On my Model :
public function all_user_request($task_id) {
$query = "SELECT * FROM user_request WHERE task_id = ?";
return $this->db->query($query, $task_id)->result();
}
Note : In model is actually using 2 INNER JOIN, I'm just simplifying the select only for asking here. (turning into denormalization table only in here).
I was trying to add draw, recordsTotal, recordsFiltered to $output in my controller just using numeric data. Example
$output = array(
"draw" => 5,
"recordsTotal" => 5,
"recordsFiltered" => 5,
"data" => $data
);
if (COUNT($output) > 0)
{
echo json_encode($output);
}
I was searching for the answer but, and I think the problem is here but I still have no idea where I must get the draw - recordsTotal - recordsFiltered data. I see on another answer from others, they use "draw" => $_POST['draw'], then I tried it, and it's do nothing.
So I'm trying that using numeric data, but the result is still same. I need some help with this. It's still print 200 rows of data in table.
Showing 0 to 0 of 0 entries (filtered from NaN total entries). Then the pagination is still print and after I click the page 2, it's doing nothing.
Datatables send everything you need - if you take a look in your console under network you'll see, that they use the ajax-get method to send those requests to the server
The GET Parameter are as follows
draw
columns
start
length
search
You can find the entire list here
which means - you've to adapt your model properly...
something like that should work
public function all_user_request($task_id)
{
$intStart = intval($this->input->get("start"));
$intLength = intval($this->input->get("length"));
$strSearch = (strlen($this->input->get("search")["value"]) >= 2) ? $this->input->get("search",true)["value"] : false;
$order = $this->input->get("order",true);
$this->setQuery($task_id,$strSearch);
$query = $this->db->get();
$this->recordsTotal = $query->num_rows();
$this->setQuery($task_id, $strSearch);
if ($intStart >= 0 && $intLength > 0)
{
$this->db->limit($intLength,$intStart);
}
$strOrderField = 'username';
$strDirection = "ASC";
if (is_array($order))
{
switch($order[0]['column'])
{
case 1:
$strOrderField = 'type';
break;
case 2:
$strOrderField = 'request';
break;
}
if (!empty($order[0]['dir'])) $strDirection = $order[0]['dir'];
}
$this->db->order_by($strOrderField,$strDirection);
$query = $this->db->get();
$arrData = $query->result();
return $arrData;
}
public function getRecordsTotal()
{
return $this->recordsTotal;
}
private function setQuery($task_id, $strSearch="")
{
$this->db
->select('*')
->from('user_request')
->where('task_id', $task_id);
if (!empty($strSearch))
{
$this->db->like('task_id', $strSearch);
}
}
and your controller
//controller
$task = $this->input->post('task', TRUE);
$user_request = $this->model->all_user_request($task);
$data = [];
foreach ($user_request as $ur)
{
$data[] = [
$ur->username,
$ur->type,
$ur->request
];
}
$arrCompiledData = [
'data' => $data,
'draw' => $this->input->get('draw'),
'recordsTotal' => $this->model->getRecordsTotal(),
'recordsFiltered' => $this->model->getRecordsTotal(),
];
$this->output
->set_content_type('application/json')
->set_output(json_encode($arrCompiledData));
Please keep in mind i just wrote this down - maybe there are some typos, but you should be able to understand how the serverside processing of a datatables request should work.
As long as you chose the server mode, you have to manage everything via the requests.
So, you have to dynamically create the values of the output array :
$output = array(
"draw" => $_POST['draw'],
"recordsTotal" => $this->my_model->get_total_records(),
"recordsFiltered" => $this->my_model->get_total_filtered(),
"data" => $this->my_model->all_user_request($id)
);
and the model functions
public function all_user_request($task_id) {
$query = "SELECT * FROM user_request WHERE task_id = ?"; // add limit $_POST['length'], $_POST['start'] to your request
return $this->db->query($query, $task_id)->result();
}
If you're using serverSide = true, you should provide your own filter count and total count. Also provide your own search function, ordering and etc. Use controller & model below for your reference.
Controller
$task = $this->input->post('task', TRUE);
$user_request = $this->model->all_user_request($task);
$output = array(
'draw' => $this->input->post('draw', TRUE),
'recordsTotal' => $user_request['recordsTotal'],
'recordsFiltered => $user_request['recordsFiltered'],
'data' => empty($user_request['data'])? array() : $user_request['data']
);
echo json_encode($output);
Model
public function all_user_request($task_id) {
$params = $this->input->post(null, TRUE);
$search_fields = array('username','type','request'); //change this into your table fields
$data = array();
$this->db->start_cache();
$this->db->select("username, type, request");
$this->db->from("user_request");
$this->db->where("task_id", $task_id);
if(!empty($params['search']['value'])){
$str = $params['search']['value'];
$this->db->group_start();
foreach($search_fields as $row){
$this->db->or_like($row, $str, 'BOTH');
}
$this->db->group_end();
}
$data['recordsTotal'] = $this->db->count_all_results();
$this->db->stop_cache();
$this->db->limit($params['length'], $params['start']);
$data['recordsFiltered'] = $this->db->count_all_results();
$query = $this->db->get();
$this->db->flush_cache();
foreach($query->result_array() as $row){
$data['data'][] = array_values($row);
}
return $data;
}

Bug in PHP code

Am working on a student portal, below is the code i used for students position but, every user keeps getting 1st position. Every student seems to have 1st position, when they check their results from the front view. Cant seem to figure out where the problem is from in the code
function get_position($student, $class, $session, $term){
$this->db->select('*');
$this->db->from('result');
$this->db->where(array( 'class_id'=>$class, 'Session'=>$session, 'Term'=>$term));
$this->db->order_by('Total', 'asc');
$other_results = $this->db->get()->result_array();
$this->db->select("*");
$this->db->from('result');
$this->db->where(array('class_id'=> $class, 'Session'=>$session, 'Term'=>$term, 'StudentID'=>$student ));
$student_result = $this->db->get()->result_array();
$student_total = $this->get_student_total($student_result);
$position =1;
foreach($other_results as $res){
if($student_total < $res['Total']){
$position++;
}
}
return $position;
}
function get_student_total($result){
$total = 0;
foreach($result as $res){
$total+= $res['Total'];
}
return $total;
}
}
?>
I am assuming that the result table may have many records for a given studentID. Several test scores (Totals) for each student during the term right?
This should do the trick
public function get_position($student, $class, $session, $term)
{
//I like to use db method chaining.
$ranking = $this->db->select('StudentID')
->select_sum('Total', 'sumScore')
->from('result')
->where(array('class_id' => $class, 'Session' => $session, 'Term' => $term))
->group_by('StudentID')
->order_by('sumScore', 'desc')
->get()->result_array();
//The code above retrieves results from a query statement that looks like this:
//SELECT `StudentID`, SUM(`Total`) as sumScore
//FROM `result` where `class_id` = $class and `Session` = $session and `Term` = $term
//GROUP BY `StudentID`
//order by `sumScore` desc
$position = 1;
foreach($ranking as $rank)
{
if($rank['StudentID'] !== $student)
{
++$position;
}
else
{
return $position;
}
}
return NULL; //to indicate studentID was not found
}

Laravel - Counting from third relationship

I have the following code:
$main = Main::with(['clients', 'events.orderitems' => function($query) {
$query->whereIn('order_id', function($query) {
$query->select('id')->from('orders')->where('orderPaid', 1)->orWhere('orderStatus', 3);
});
}])->where('id', $id)->first();
foreach($main->events as $dates) {
$all_paid = 0;
$all_pending = 0;
foreach($dates->orderitems as $item) {
if($item->orderPaid == 1) {
$all_paid = $all_paid + $item->quantity;
}
}
$dates->orderscount = $all_paid;
foreach($dates->orderitems as $item) {
if($item->orderStatus == 3) {
$all_pending = $all_pending + $item->quantity;
}
}
$dates->pendingcount = $all_pending;
}
Is there maybe an MYSQL way to Count the PAID orders and the orders with orderStatus == 3 in the SQL? I think, how I am doing it, it's way to messy and not very good for the performance.
So a "Main" has n-events which have n-orderItems.
I need to get to a "Main" Event, all the Events with all PAID and orderStatus == 3 items. How can I do that?
UPDATE - SOLUTION:
foreach($main->events as $dates) {
$dates->orderscount = OrderItems::where('events_id',$dates->id)->whereHas('orders', function($q) {
$q->where('orderPaid', 1);
})->sum('quantity');
$dates->pendingcount = OrderItems::where('events_id',$dates->id)->whereHas('orders', function($q) {
$q->where('orderStatus', 3);
})->sum('quantity');
}
Laravel has an aggregate method which does just that.
The same way you are using first to only get the first result of a query as in:
)->where('id', $id)->first();
to get the count you could use count()
DB::table('orders')->where('orderPaid', 1)->orWhere('orderStatus', 3)->count();
If you need the sum() of the products and not the count as in your title, you could for your particular example use:
$total_quantity = DB::table('orders')->where('orderPaid', 1)->orWhere('orderStatus', 3)->sum('quantity');
UPDATE:
As in your example to get both counts you would change you foreach to be something similar to:
foreach($main->events as $dates) {
$all_paid = DB::table('orders')->where('order_id',$dates->id)->where('orderPaid', 1)->count();
$all_pending = DB::table('orders')->where('order_id',$dates->id)->where('orderStatus', 3)->count();
$dates->orderscount = $all_paid;
$dates->pendingcount = $all_pending;
}
You can use sum() with your eloquent query in laravel. Check under the #Aggregates section here:
http://laravel.com/docs/4.2/queries

Categories