Laravel eager loading using data inside itself - php

I'm trying to use data inside some eager loading in Laravel.
I have a "Sensors" table with many "Measurements" in another one.
I need to check every measurement a sensor has every hour. When I do it, it saves the last id of the measurement checked inside "sensors.last_checked_measurement"
$sensors = Sensor::with(array('measurements' => function($query) {
$query->where('id', '>', SENSOR.LAST_CHECKED_MEASUREMENT); // <-- in between data
}))->get();
This, as you may know, translate into
"select * from 'sensors'"
"select * from `measurements` where `measurements`.`sensor_id` in (?, ?) and `id` > ?"
If Laravel can use the result of the first query to get the sensor_id for the next one, maybe it can be done, right?
So my question:
How can I use that "SENSOR.LAST_CHECKED_MEASUREMENT" inside the eager loading?

Have you tried db::raw?
$sensors = Sensor::with(array('measurements' => function($query) {
$query->where('id', '>', DB::raw('SELECT LAST_CHECKED_MEASUREMENT FROM SENSOR ORDER BY ID DESC LIMIT 1'));
}))->get();

Related

How can I make select in select on laravel eloquent?

I use laravel 5.3
My sql query is like this :
SELECT *
FROM (
SELECT *
FROM products
WHERE `status` = 1 AND `stock` > 0 AND category_id = 5
ORDER BY updated_at DESC
LIMIT 4
) AS product
GROUP BY store_id
I want to change it to be laravel eloquent
But I'm still confused
How can I do it?
In cases when your query is to complex you can laravel RAW query syntax like:
$data = DB::select(DB::raw('your query here'));
It will fire your raw query on the specified table and returns the result set, if any.
Reference
If you have Product model, you can run
$products = Product::where('status', 1)
->where('stock', '>', 0)
->where('category_id', '=', 5)
->groupBy('store_id')
->orderBy('updated_at', 'desc')
->take(4)
->get();
I think this should give you the same result since you pull everything from your derived table.

How do I optimize this query with whereHas?

I was using this query to filter out stores with city and categories. It was working fine when I had around 1000 records in stores table. Now, when I have 5000 records it takes around 3-10 seconds to generate result.
A store belongs to multiple categories in my case.
How can I optimize this query using Eloquent orm or DB::raw()?
$stores = Store::where('city_id', $city_id)
->where('disabled', '0')
->whereHas('categories', function($q) use ($category_id){
$q->where('category_id', '=', $category_id);
})
->orderBy('rating','DESC')->paginate(10);
I solved my problem using whereRaw as DB::raw() or DB::select() can not paginate() the collection.
Problem:
Execution time: 11.449304103851s
city_id = 6 & $category_id = 1
$stores = Store::where('city_id', $city_id)
->where('disabled', '0')
->whereHas('categories', function($q) use ($category_id){
$q->where('category_id', '=', $category_id);
})
->orderBy('rating','DESC')->paginate(10);
Solution:
Execution time: 0.033660888671875s
city_id = 6 & $category_id = 1
$stores = Store::
where('city_id', $city_id)
->where('disabled', '0')
->whereRaw('stores.id in (select store_id from store_categories_pivot where category_id = ?)', [$category_id])
->orderBy('rating','DESC')
->paginate(10);
You could try running:
$stores = Store::where('city_id', $city_id)
->where('disabled', '0')
->leftJoin('categories','categories.store_id','=', 'stores.id')
->where('category_id', $category_id)
->orderBy('rating','DESC')->paginate(10);
and now verify your time execution. But you might need to add extra changes to such query because we don't know exact tables structure and how data is organized in them.
If it doesn't help you should get the query that is executed (exact query) and then run
EXPLAIN your_query
in Database Tool to show you what exactly is happening and whether do you really have indexes on everything that is needed.
Looking at your query you should probably have indexes for stores for columns:
city_id
disabled
rating
and for categories you should have indexes for columns:
category_id
store_id
or for some combinations of those columns.

SQL exists in Laravel 5 query builder

Good morning,
I've been trying for quite a lot of time to translate this query(which returns an array of stdClass) into query builder so I could get objects back as Eloquent models.
This is how the query looks like untranslated:
$anketa = DB::select( DB::raw("SELECT *
FROM v_anketa a
WHERE not exists (select 1 from user_poeni where anketa_id=a.id and user_id = :lv_id_user)
Order by redni_broj limit 1"
), array( 'lv_id_user' => $id_user,
));
I have tried this, but it gives a syntax error near the inner from in the subquery:
$anketa = V_anketa::selectRaw("WHERE not exists (select 1 from user_poeni where anketa_id=a.id and user_id = :lv_id_user)", array('lv_id_user' => $id_user,)
)->orderBy('redni_broj')->take(1)->first();
The problem is this exists and a subquery in it. I couldn't find anything regarding this special case.
Assume each table has an appropriate Eloquent model.
V_anketa is a view. The db is postgresql.
As far as the query goes I believe this should work:
$anketa = V_anketa::whereNotExists(function ($query) use ($id_user) {
$query->select(DB::raw(1))
->from('user_poeni')
->where('anketa.id', '=', 'a.id')
->where('user_id', '=', $id_user);
})
->orderBy('redni_broj')
->first();
but I'm not clear on what do you mean by "assuming every table has an Eloquent model" and "V_anketa" is a view...
Assuming the SQL query is correct, this should work:
$anketa = DB::select(sprintf('SELECT * FROM v_anketa a WHERE NOT EXISTS (SELECT 1 FROM user_poeni WHERE anketa_id = a.id AND user_id = %s) ORDER BY redni_broj LIMIT 1', $id_user));
If you want to get back an Builder instance you need to specify the table:
$anketa = DB::table('')->select('');
If you however, want to get an Eloquent Model instance, for example to use relations, you need to use Eloquent.

Laravel where also returns empty results

I'm using Laravel 4 to get all the persons that have a score for a certain event
This is the query i'm using
$event = Person::with(array('eventscore' => function($q){
$q->where('id', 3);
}))->get();
The problem is that it's also returning the persons that don't have a score in the eventscore table.
This is the output
Is there any way that i can return only the persons that have a score?
Thanks!
with() will not limit the Persons returned, it will only limit eventscores. If you want only Persons that have an event score, you use has or whereHas.
$event = Person::whereHas('eventscore', function($q) {
$q->where('id', 3);
})->get();

Get total number of records with Laravel?

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.

Categories