Wrong total pagination with select distinct in Laravel 6 - laravel

I use Laravel 6.12, I have this request :
$queryJob = DB::table('jobs as j')->join('job_translations as jt', 'j.id', 'jt.job_id')
->whereNull('j.deleted_at')
->whereNull('jt.deleted_at')
->select('j.id', 'j.short_name', 'j.status', DB::raw("case when j.short_name = '{$request->short_name}' then 0 else 1 end"))
->distinct();
$jobs = $queryJob->paginate($qtyItemsPerPage);
The results displays an error for the total :
The total = 3, but as you can see the data contains only 2 elements.
I read here that when using a distinct, I must be clear on which column the total must be calculated: distinct() with pagination() in laravel 5.2 not working
So I modified my query like that:
$jobs = $queryJob->paginate($qtyItemsPerPage, ['j.*']);
But without success, the total is still wrong.

Hoping that I don't misunderstand your DB and relations structure and purpose of your query perhaps this will avoid using distinct or groupBy altogether?
$shortname = $request->input('short_name');
$queryJob = Job::with('job_translations')->select('id','short_name',
'status', DB::raw("case when short_name = '" . $shortname . "'
then 0 else 1 end")
->paginate($qtyItemsPerPage);
Pagination can be easily manually added with skip and take in case you need to use groupBy
$queryJob->skip(($page - 1) * $qtyItemsPerPage)->take($qtyItemsPerPage)->get();

The solution for me was to pass a field name to the distinct() method.
With your example:
$queryJob = DB::table('jobs as j')
// joins, where and other chained methods go here
->distinct('j.id')
Solution taken from https://stackoverflow.com/a/69073801/3503615

Related

How to update a single value with laravel

Hello i want to update a single value on another table using laravel. This is the code i have done until now but doesnt seem to work:
$amount = Product::findorFail($request->products[$i]);
$total_value = $request->amount[$i] + $amount->amount;
$amount->update(['amount', $total_value]);
dd($total_value);
with dd i see that the result is correct but the update function is not, the query im trying to make is
update table set amount=x where id=y
You have multiple choices. The shortes are:
$amount->update(['amount'=> $amount->amount + request->amount[$i]]);
or
Product::findorFail($request->products[$i])->increment('amount', $request->amount[$i]);
you could change your code like below
$amount = Product::findorFail($request->products[$i]);
$total_value = $request->amount[$i] + $amount->amount;
$amount->amount=$total_value;
$amount->update();
or as mentioned in comments you could use eloquent increment function

Write multiple laravel query at once

im new to laravel, I just want to know if there's any way to efficiently rewrite this code?
$answer1 = SurveyAnswer::where('answer_1','=','1')->get()->count();
$answer2 = SurveyAnswer::where('answer_1','=','2')->get()->count();
$answer3 = SurveyAnswer::where('answer_1','=','3')->get()->count();
$answer4 = SurveyAnswer::where('answer_1','=','4')->get()->count();
$answer5 = SurveyAnswer::where('answer_1','=','5')->get()->count();
Get the data first:
$answers = SurveyAnswer::whereIn('answer_1', [1, 2, 3, 4, 5])->get();
Then count answers using loaded collection:
$answer1 = $answers->where('answer_1', 1)->count();
$answer2 = $answers->where('answer_1', 2)->count();
...
This code will generate just one DB query instead of five.
Try this:
$matches = [1,2,3,4,5];
$answer = SurveyAnswer::whereId('answer_1', $matches)->groupBy('answer_1')->get();
You can easily do it with an agregate function with a case expression, since mysql doesnot support native pivoting functions
Following is a sample, and try to rewrite according to your requirement and runt it against database directly, if it works, then you can use it with laravel raw sql.
select id,
sum(case when value = 1 then 1 else 0 end) ANSWER1_COUNT,
sum(case when value = 2 then 1 else 0 end) ANSWER2_COUNT
from survey
group by answer

Skip and take all?

In eloquent, how can I skip 10 rows and then get the rest of the table?
User::skip(10)->all();
The above does not work, but it gives you an idea what I am looking for.
Try this:
$count = User::count();
$skip = 10;
User::skip($skip)->take($count - $skip)->get();
With one query:
User::skip($skip)->take(18446744073709551615)->get();
It's ugly, but it's an example from official MySQL manual:
To retrieve all rows from a certain offset up to the end of the result
set, you can use some large number for the second parameter. This
statement retrieves all rows from the 96th row to the last:
SELECT * FROM tbl LIMIT 95,18446744073709551615;
try something like this it work for sure..
$temp = User::count();
$count = $temp - 10;
$data = User::take($count)->skip(10)->get();
Laravel 5 returns Eloquent result as Collection.
So you can use collenction function slice();
$users = User::get();
$slicedUsers = $users->slice(10);

Eloquent to count with different status

It's possible to make one query to get total, sold & unsold in laravel eloquent?
$total_apple = Item::whereName('Apple')->count();
$sold_apple = Item::whereName('Apple')->whereStatus(2)->count();
$unsold_apple = Item::whereName('Apple')->whereStatus(1)->count();
Yes you can totally do that. You can use filter method on collection object returned by your Eloquent query.
$apples = Item::whereName('Apple')->get();
$soldApples = $apples->filter(function ($apple){
return $apple->status == 2;
});
$unsoldApples = $apples->filter(function ($apple){
return $apple->status == 1;
});
$soldApples and $unsoldApples contains the object of the items. You can then just use count($soldApples) and count($unsoldApples) to get their count.
filter method is against the collection object so there is no sql overhead.
There is no need run multiple queries or even fetch the entire results and use collection methods to loop through. Just use raw queries.
$apples = Item::whereName('Apple')
->selectRaw('COUNT(*) as total_apples,
SUM(status=2) as sold_apples,
SUM(status=1) as unsold_apples')
->first();
echo $apples->total_apples; // Outputs total apples
echo $apples->unsold_apples; // Outputs the unsold apples
echo $apples->sold_apples; // Outputs the sold apples
Since you are only doing simple counts though, you can use the query builder as well.
I would get all the items in one collection, then run the where statement on that collection. This should trigger a single Query.
$apples = Item::whereName('Apple')->get(); // This goes against SQL
$total_apple = $apples->count(); //This runs on the Collection object not SQL
$sold_apple = $apples->whereStatus(2)->count();
$unsold_apple = $apples->whereStatus(1)->count();

Add a clause to a MySQL statement without quotes using CodeIgniter's Active Record functions

I wanted to update a row using active records in codeigniter, and I only want to increment a field value (received_qty = received_qty +1) , I realized that I can do that in usual sql, but I cant in codeigniter active records
$update['received_qty'] = 'received_qty + 1';
$update['received_date'] = date("Y-m-d");
$this->db->where($where);
$this->db->update('vrs_distribution', $update);
anyone know how to do it using active records?
This will work.
$this->db->set('received_qty', 'received_qty + 1', FALSE);
$this->db->set('received_date', date("Y-m-d"));
$this->db->where($where);
$this->db->update('vrs_distribution');
ActiveRecord escapes everything put into a set(), where() and many other methods. Where and set can both take an optional third parameter of $escape which is a boolean. Set it to FALSE and CodeIgniter will not escape anything, meaning your field increment wont be treated as a string.
Or you can do:
$this->db->query('UPDATE vrs_distribution SET received_qty = received_qty + 1, received_date = CURDATE() WHERE id = ' . $id);
You would need to modify WHERE clause to suit you though
status set to zero(Update)
$this->db->set('IsCurrent', '0');
$this->db->where('AcademicYearID',$academicYearId);
$this->db->update('academicyear');
I was going to ask a similar question just a little bit different, but the problem was the same: I needed to update a date with an interval (expiry_date = expiry_date + interval 3 month) and Phil Sturgeon's answer did solve the problem.
However, what I realized is that you can still use the array for the fields that can have quotes, meaning that you could write:
$this->db->set('received_qty', 'received_qty + 1', FALSE);
$this->db->set('expired_date', 'CURDATE() + INTERVAL 10 DAY', FALSE); //extra example 1
$update['received_date'] = date("Y-m-d");
$update['receiver'] = $receiver_name; //extra example 2
$this->db->where($where);
$this->db->update('vrs_distribution', $update);
Where $this->db->last_query() would output:
UPDATE `vrs_distribution`
SET `received_qty` = received_qty + 1,
`expiry_date` = CURDATE() + INTERVAL 10 DAY, #extra example 1
`received_date` = '2015-07-01 16:00:00',
`receiver` = 'strike_noir', #extra example 2
WHERE #where clause with backticks
Notice that the fields where set() was used do not have quotes and appear in the first place. The rest of the query has backticks (letting "CodeIgniter protect the remaining fields").
You seem pretty close, there isn't an 'increment by one' command in CI's ActiveRecord (or in SQL for that matter).
$update['received_qty']++;
$update['received_date'] = date("Y-m-d");
$this->db->where($where);
$this->db->update('vrs_distribution', $update);

Resources