Get total number of records with Laravel? - php

MySQL has a feature for getting the total number of records a query would return without a limit, SQL_CALC_FOUND_ROWS. Does Laravel support this?
Currently I have to do it in two queries:
public function dataTable() {
$bookings = DB::table('bookings')
->limit(Input::query('iDisplayLength'))
->offset(Input::query('iDisplayStart'))
->get();
$count = $bookings = DB::table('bookings')
->count();
return Response::json([
'iTotalRecords' => $count,
]);
}
Not only will this be less efficient, but there's going to be a lot of redundant code once I add in all the ->where() criteria.

For any complicated or vendor-specific queries, you generally have to pass the query directly with DB::raw(), e.g.:
$bookings = DB::table('bookings')
->select(DB::raw('SQL_CALC_ROWS_FOUND ...

Refined what #giaour said.
$bookings = DB::table('bookings')
->select(array(DB::raw('SQL_CALC_FOUND_ROWS booking_id,name')))
->where('...')
->orWhere('...')
->take(10)
->skip(50)
->get();
$bookingsCount = DB::select( DB::raw("SELECT FOUND_ROWS() AS Totalcount;") );
After SQL_CALC_FOUND_ROWS you can fetch count by SQL_CALC_FOUND_ROWS();

I came across this issue because I wanted to paginate a set of results which already included a GROUP BY and Laravel's built-in paginate() method uses COUNT(*) and doesn't work with GROUP BY.
So I ended up extending the Builder class to inject the SQL_CALC_FOUND_ROWS right before a query is run, and run FOUND_ROWS right after. I did this by overriding the Laravel getModels() method.
I've uploaded the code here.

Related

how to pass a pure sql query to laravel eloquent

This SQL:
SELECT COUNT(*) AS total_see_all_video
from users u
WHERE 7 = (SELECT COUNT(*) FROM lecciones_users lu WHERE lu.uuid = u.uuid)
I tried this code but did not work:
$data = LeccionesUsers::select(
DB::raw('COUNT(*) AS total_ase_vis_videos'),
DB::raw('where 7 = (SELECT COUNT(*) FROM lecciones_users where leccion_users.uuid = users.uuid)')
)
->join('users', 'lecciones_users.uuid', '=', 'users.uuid')
->get();
Your issue is that you are not correctly forming your query. You can do ->toSql(); instead of ->get(); and you would see the final SQL (would definitely not be the same as the one you wrote first).
So, you should have this to have the same SQL:
$total_see_all_video = LeccionesUsers::whereRaw('7 = (SELECT COUNT(*) FROM lecciones_users where leccion_users.uuid = users.uuid)')
->count();
Please, try my query (and also run ->toSql() to see if you have a correct SQL).
I would still recommend to use relationships and it is very weird to do 7 = query.
You can use
DB::query()->fromSub('Raw sql query here..')
and then can perform actions on this.For the reference you can use the documentation fromSub
You can also look into this convert this where break this query to parts to be used accordingly. You can use this section for the reference purpose:
Laravel-Raw-Expressions
Hope this will help you with the result.

Eloquent With Nested Where Clauses

I am curious if there is a way using Eloquent's query builder to nest where clauses or if I should just run a raw DB query.
Here is the raw query:
SELECT * FROM `inventory` WHERE (`sold_date` > '2020-12-31' OR `sold_date` IS NULL) AND (`removed_date` > '2020-12-31' OR `removed_date` IS NULL) AND `category` <> 1 AND `purchased_date` <= '2020-12-31'
In Laravel Eloquent you can use the below query:
$inventory = Inventory::where(function($query) {
$query->where('sold_date', '>', '2020-12-31')->orWhereNull('sold_date');
})->where(function($query) {
$query->where('removed_date', '>', '2020-12-31')->orWhereNull('removed_date');
})->where('category', '<>', 1)->where('purchased_date', '<=', '2020-12-31')
->order('id', 'DESC')
->get();
Yes you can pass an array with conditions into to Eloquent's where() and have multiple where()s.
See this answer (including the comments) for how you could build your query: https://stackoverflow.com/a/27522556/4517964
You can try this
Inventory ::where(function($query) use ($d1){
$query->where('solid_date','=',$d1)
->orWhereNull('solid_date');
})->where(function($query2) use ($da1){
$query2->where('removed_date','=',$da1)
->orWhereNull('removed_date');
})->where(function($query3) use (){
$query2->where('category','<>',1)
->where('purchased_date','=','2020-12-31');
}}->get();
I perfer to use parameter in few functions may be you will need it otherwise you can hard code it like I did in the last function

Laravel Query Builder returns no results while generated SQL works perfect

I am actively working on a laravel project and have ran into some issues with the Query Builder. I am trying to avoid using DB::Raw but it is looking like I may need to.
$query = app($this->model());
$query = $query->select(['last_name', 'first_name', 'birthday'])
->distinct()
->leftJoin('enrollments', 'students_meta.uid', '=', 'enrollments.student_uid')
->whereIn('enrollments.type', $types)
->where('enrollments.startdate', '<=', "'{$today}'")
->where(function ($join) use ($today) {
$join->where('enrollments.dropdate', '>=', "'{$today}'")
->orWhereNull('enrollments.dropdate');
});
// todo: add viewBy filter
$query = $query->where('birth_month', '=', Carbon::today()->month);
$query = $query->orderBy('last_name')->orderBy('first_name');
$models = $query->get();
The above query builder generates the following SQL :
SELECT distinct `last_name`, `first_name`, `birthday`
FROM `students_meta`
LEFT JOIN `enrollments` ON `students_meta`.`uid` = `enrollments`.`student_uid`
WHERE `enrollments`.`type` IN ('ACTIVE', 'active')
AND `enrollments`.`startdate` <= '2019-10-29'
AND (`enrollments`.`dropdate` >= '2019-10-29' OR `enrollments`.`dropdate` IS NULL)
AND `birth_month` = 10
ORDER BY `last_name` asc, `first_name` asc;
The generated SQL is perfect based on the old code I'm moving from and produces the expected results. If I move some things around, it seems I can get the query builder to return results, but they're not the correct ones. I've looked at other questions/answers about this kind of problem and tried multiple scenarios of moving the join/changing the where's around, still no luck.
Any suggestions? (other than taking the generated sql and running it in DB::Raw()
$query = $query->orderBy('last_name')->orderBy('first_name')->get();
Your query worked fine but you didn't output it.
You can also use ->get()->toArray(); to retrieve the data in array format
Remove your quotes from around $today. The third (or second, if you eliminate the comparison operator) parameter of the where clause sends the values as a parameter in a prepared statement . So
"'{$today}'"
would look like this in a straight query:
where enrollments.startdate <= "'2019-10-29'"
So change your query to
->where('enrollments.startdate', '<=', $today)
Make sure you remove the quotes from all instances like this in your query.

Laravel: Filter while querying a DB or after the query?

I am a JS developer but trying to help out my team with some Laravel.
I have his query below:
$customers = ShopUser::selectRaw('shop_users.name, shop_users.email, shop_users.unique_id, shop_users.created_at, SUM(orders.total) AS total_spent, MIN(orders.created_at) AS first_purchase, MAX(orders.created_at) AS last_purchase, count(orders.id) AS total_orders')
->leftJoin('orders', 'orders.customer_id', 'shop_users.id')
->groupBy('shop_users.id')
->get();
I want to then put the unique_id of users of this query who have a total over $1,000 spent in an array. Is there a way to do that in the query above or should I make a separate iteration after this to sort that?
I think you can use use havingRaw
ShopUser::selectRaw('shop_users.name, shop_users.email, shop_users.unique_id, shop_users.created_at, SUM(orders.total) AS total_spent, MIN(orders.created_at) AS first_purchase, MAX(orders.created_at) AS last_purchase, count(orders.id) AS total_orders')
->leftJoin('orders', 'orders.customer_id', 'shop_users.id')
->havingRaw('total_spent > ?', [1000])
->groupBy('shop_users.id')
->get();
If you want to return the result as an array, you can use pluck
ShopUser::selectRaw('shop_users.name, shop_users.email, shop_users.unique_id, shop_users.created_at, SUM(orders.total) AS total_spent, MIN(orders.created_at) AS first_purchase, MAX(orders.created_at) AS last_purchase, count(orders.id) AS total_orders')
->leftJoin('orders', 'orders.customer_id', 'shop_users.id')
->havingRaw('total_spent > ?', [1000])
->groupBy('shop_users.id')
->get()
->pluck('unique_id')
->all()
I haven't tested it, but I hope this helps
Since there are lot of approaches to achieve this goal in Laravel, I prefer the Eloquent Query Builder approach rather than Row Queries.
I assume that all you need is a list of unique ids that have a total over 1,000. Ex: [233123, 434341, 35545123].
So any other selections, groupings and filters you have written in your code is ignored.
&shopUserIds = ShopUser::whereHas('orders', function ($query) {
$query->where(DB::row('SUM(total)'), '>', '1000');
})
->get()
->map->id
->toArray();

Laravel 4 select column from another table in subquery

I am attempting to do the equivalent of this:
select p.id, p.title, b.brand,
(select big from images where images.product_id = p.id order by id asc limit 1) as image
from products p
inner join brands b on b.id = p.brand_id
Here is where I am at now, but it of course doesn't work:
public function getProducts($brand)
{
// the fields we want back
$fields = array('p.id', 'p.title', 'p.msrp', 'b.brand', 'p.image');
// if logged in add more fields
if(Auth::check())
{
array_push($fields, 'p.price_dealer');
}
$products = DB::table('products as p')
->join('brands as b', 'b.id', '=', 'p.brand_id')
->select(DB::raw('(select big from images i order by id asc limit 1) AS image'), 'i.id', '=', 'p.id')
->where('b.active', '=', 1)
->where('p.display', '=', 1)
->where('b.brand', '=', $brand)
->select($fields)
->get();
return Response::json(array('products' => $products));
}
I don't really see anything in the docs on how to do this, and I can't seem to piece it together from other posts.
In "regular" SQL, the subquery is treated AS a column, but I am not sure how to string that together here. Thanks for any help on this.
I strongly recommend you to use Eloquent, instead of pure SQL. It's one of the most beautful things in Laravel. Two models and relations and it's done! If you need to use pure SQL like that, put it all in DB::raw. It's easier, simpler and (ironically) less messy!
With the models, you could use relations between the two tables (represented by the models itself) and say (so far I understood) that Brands belongs to Products, and Images belongs to Product. Take a look at Eloquent's documentation on Laravel. Probably will be more clearly.
Once the relations are done, you can only say that you wanna get
$product = Product::where(function ($query) use ($brand){
$brand_id = Brand::where('brand', '=', $brand)->first()->id;
$query->where('brand_id', '=', $brand_id);
})
->image()
->get();
That and a better look at Eloquent's documentation should help you to do the job.
P.S.: I didn't test the code before send it and wrote it by head, but i think it works.

Categories