Laravel Eloquent - Is it inefficient - php

I'm looking at developing a new site with Laravel. I was reading a tutorial on http://vegibit.com/laravel-eloquent-orm-tutorial/, regarding using Eloquent to join tables and retrieve data. In the last example they are essentially trying to do a join but still Eloquent is executing two queries instead of one. Is Eloquent inefficient or is this simply a poor example? It seems that this could have simply been done with a single left join query. Can anyone provide some insight on this? Is there a better way to execute this query? The specific example is:
Route::get('/', function()
{
$paintings = Painting::with('painter')->get();
foreach($paintings as $painting){
echo $painting->painter->username;
echo ' painted the ';
echo $painting->title;
echo '<br>';
}
});
Results in the following queries:
string ‘select * from paintings‘ (length=25)
string ‘select * from painters where painters.id in (?, ?, ?)’ (length=59)
Leonardo Da Vinci painted the Mona Lisa
Leonardo Da Vinci painted the Last Supper
Vincent Van Gogh painted the The Starry Night
Vincent Van Gogh painted the The Potato Eaters
Rembrandt painted the The Night Watch
Rembrandt painted the The Storm on the Sea of Galilee

Eloquent is extremely useful for building something quickly and also has query caching that it's really easy to use however it might not be ideal for big projects that need to scale over time, my approach is to use Repository pattern with contracts that way I can still use the power of Eloquent and have a system that will allow me to easily change the implementation if needed.

If what you want is to get all the data in a single query to use joins, then you must specify it as:
Painting::join('Painter as p', 'p.IdPainter', '=', 'Painting.IdPainting')->get()
And if you need some conditional then:
Painting::join('Painter as p', 'p.IdPainter', '=', 'Painting.IdPainting')->where('cond1', '=', 'value')->get();
Personally Eloquent is inefficient, i prefer Doctrine in this regard.

The simple answer to your question: No. It is not inefficient.
Using Eloquent in the way described by Jorge really defeats its purpose as an ORM.
Whilst you can write joins as in the example given, an ORM really isn't designed with that in mind.
The example you've given isn't an n+1 query (where there's an additional query run for each item in the loop - the worst type of query).
Using two queries as reported by your output isn't a huge overhead, and I think you'd be fine to use that in production. Your use of Eloquent's 'with' eager loading is precisely what you should use in this context.
An example of an n+1 query (which you want to avoid) is as follows:
foreach (Book::all() as $book)
{
echo $book->author->name;
}
If 20 books are returned by Book::all(), then the loop would execute 21 queries in total. 1 to get all of the books, and 1 for each iteration of $book to get the author's name.
Using Eloquent with eager loading, combined with caching would be enough to minimise any performance issues.

Related

Laravel Query efficiency difference

Please could somebody tell me which one is most efficient select in Laravel:
$car = Car::all(); ------- $car = Car::find();
$car = DB::table('car')->get(); ------ $car = DB::table('car')->first();
Your first approach:
$car = Car::all(); ------- $car = Car::find();
Makes use of Eloquent. This means, all the rows received from the query will be hydrated to Model instances, and all of those will be injected into an instance of Collection (for multiple elements, of course). This is useful because you then will have all the benefits that this brings. However, this comes with a little decrease on performance (understandable)
Your second one:
$car = DB::table('car')->get(); ------ $car = DB::table('car')->first();
Uses the Query Builder instead. The results (as a whole) will be also casted into an instance of Collection, but its items will be simple arrays. This means that the process will be faster (more performant) but on detriment of not having all the cool features of Eloquent.
There's even a more performant option: Using raw queries. That also has tradeoffs: Results are not hydrated into a Collection instance.
Which one to use? It depends on your needs. Usually I go for the Eloquent option. I use the Query Builder directly when I need to make queries to big databases and need speed.
For me most efficient is selecting from the Model: like Car:all(), but it's always better if you use pagination or just don't take all of the records from the database with all() method.
But selecting with DB is a bit faster and in some cases maybe it would be better to use.
In the end, it always depends on what`s your problem and which way do you want to solve it.
for a better understanding I recommend you to watch this video and after that maybe keep going to search for some more information or just try it out yourself.
https://www.youtube.com/watch?v=uVsY_OXRq5o&t=205s

Why is the speed of elquent is lower than db query builder?

I have two query in lumen like this
Doc:all();
DB:table('docs')->get();
Speed of first is 946 ms, speed of second is 46 ms (in Postman)
Can you tell me about this?
DB::table('docs')->get();
returns a collection of basic objects with no added logic.
Doc:all();
returns a collection of Doc models. Hydrating every Doc model in the results with all the added logic ($appends, $casts, $with, $withCount) takes more processing time.
For these cases you'll always get better speed in query builder, but in long term for example while playing with table relations and other complex queries you have to write very less and simple code with eloquent. In case of query builder you have to deal with complex join and stuff.

Doctrine2 Lazy vs Eager, or why it makes multiple queries

So I am using QueryBuilder in some of my projects, but others I need to create RAW SQL queries in order to increase performance, as I have more than a million rows with their relations...
What I found awful about QueryBuilder, is the fact that it creates several queries when you have relations, for example, I have a OneToMany relation from Product to Image and a ManyToOne on the inversed side.
My query has a pagination, so it is limited to LIMIT 10 OFFSET 0 and on. My Image entity has about 2.7 million rows, that's why I am using pagination, doing this simple query, which fetches Image i plus Product p because I need p.title I end up with 1 Query for my 10 images, and 10 queries for each Product for Image.
That's unneeded, It can be done with just 2 queries, one for Image and one for Product, using fetch="EAGER" that's what I get. But I need to put fetch="EXTRA_LAZY" inside Product mapping, otherwise I will get 11 queries again..
With just 10 images isn't hard, but when the user filters 500 images, response time gets higher and higher... That's why I've ended up doing RAW queries, best performance, no extra queries (just 1 query that contains everything) BUT unable to work with objects like QueryBuilder does, can't access to image.product.title inside twig to get the title, instead I need to do SELECT p.title AS product_title and call image.product_title etc
So I need to know why QueryBuilder is that sh*t when reading but so marvelous when persisting objects (easy, fast, clean...) and how can I work with huge DB's using QueryBuilder without loosing performance and without getting tons of extra unneeded queries.
An example query, is this one
$qb = $this->createQueryBuilder('i');
$qb->innerJoin('i.product', 'p');
$qb->where('i.X = Y');
return $qb->getQuery()->getResult()
Using $qb->select('i, p'); seems to use only one query, which it's runnable raw has an INNER JOIN (which is actually how it is supossed to work WITHOUT the $qb->select()) but performance is still lower than doing a RAW SQL query... RAW SQL = 500MS for a 10.000 rows query, using QB it's 1,100 MS. I know I wont use 10.000 rows, but there's a chance...
The question is still the same, what advantages and disadvantages, besides the object manipulation, which is gone with a RAW SQL. And when to use LAZY or EAGER and WHY, or why/when you don't need them.
All of this may end a discussion in my DevTeam once for all. As I'm a QB lover.
Did you do something like this:
SELECT i FROM AcmeBundle:Image i JOIN i.product p WHERE ...
?
That would explain numerous queries because Doctrine does not preserve fetched data.
Doing something like this tells Doctrine to actually preserve fetched data of both Image and Product:
SELECT i, p FROM AcmeBundle:Image i JOIN i.product p WHERE ...
Then, you would have no need neither for EAGER nor EAGER_LAZY.
I might have missed the point in your question. If I have, please correct me and I might be able to suggesting something else.
Edit:
$qb = $this->createQueryBuilder('i');
$qb->innerJoin('i.product', 'p');
$qb->addSelect('p'); // Very importang, hints Doctrine to preserve fetched Product
$qb->where('i.X = Y');
return $qb->getQuery()->getResult()
Or using PARTIAL:
$qb = $this->createQueryBuilder('i');
$qb->innerJoin('i.product', 'p');
$qb->select('PARTIAL i.{image_field1, image_field2}', 'PARTIAL p.{product_field1, product_field2}'); // Very importang, hints Doctrine to preserve fetched Product
$qb->where('i.X = Y');
return $qb->getQuery()->getResult()

laravel get all places for all events where place

I'm finally learning the Laravel ORM, and quite frankly, it's making me depressed as I'm working against a tight deadline.
I'm trying the simplest thing, easily to achieve in SQL in a few seconds, and after half an hour with the ORM, I just can't get it to work.
1 event has 1 place. In EventModel:
public function place(){
$this->hasOne('zwoop\models\place\PlaceModel', 'place_id');
}
1 place can belong to many events. In PlaceModel:
public function events(){
$this->belongsToMany('zwoop\models\event\EventModel');
}
Now in the EventModel, what is the structure to get all events where place is within certain bounds? (eg. where place.lat > input_lat and lng > input_lng (simplified).
I would give example code, but I can't figure it out at all.
This is just the beginning of a query that is really long, I really wonder if the ORM will be the most efficient solution in the end...
Have you looked at the Querying Relations section of the Laravel Docs?
EventModel::whereHas('place', function($q) {
$q->where('lat', '>', input_lat)
->where('lng', '>', input_lng);
})->get();
Hmm. let me explain here a little bit. There are quite several ways to do it. But the most efficient way should be the following:
Get all places within certain bounds using
$places = Place::where('lat', '>', input_lat)->where('lng', '>', input_lng)->get();
$places = $places->lists('id');
Get all events ids using
Events::whereIn('place_id', $places)->get();
It is done!
Of course you can combine the two and make it one query. Personally I do not like mix queries thought it should give you a little boost in performance in your case.

Caching only a relation data from FuelPHP ORM result

I'm developing app with FuelPHP & mySql and I'm using the provided ORM functionality. The problem is with following tables:
Table: pdm_data
Massive table (350+ columns, many rows)
Table data is rather static (updates only once a day)
Primary key: obj_id
Table: change_request
Only few columns
Data changes often (10-20 times / min)
References primary key (obj_id from table pdm_data)
Users can customize datasheet that is visible to them, eg. they can save filters (eg. change_request.obj_id=34 AND pdm_data.state = 6) on columns which then are translated to query realtime with ORM.
However, the querying with ORM is really slow as the table pdm_data is large and even ~100 rows will result in many mbs of data. The largest problem seems to be in FuelPHP ORM: even if the query itself is relatively fast model hydration etc. takes many seconds. Ideal solution would be to cache results from pdm_data table as it is rather static. However, as far as I know FuelPHP doesn't let you cache tables through relations (you can cache the complete result of query, thus both tables or none).
Furthermore, using normal SQL query with join instead of ORM is not ideal solution, as I need to handle other tasks where hydrated models are awesome.
I have currently following code:
//Initialize the query and use eager-loading
$query = Model_Changerequest::query()->related('pdmdata');
foreach($filters as $filter)
{
//First parameter can point to either table
$query->where($filter[0], $filter[1], $filter[2]);
}
$result = $query->get();
...
Does someone have a good solution for this?
Thanks for reading!
The slowness of the version 1 ORM is a known problem which is being addressed with v2. My current benchmarks are showing that v1 orm takes 2.5 seconds (on my machine, ymmv) to hydrate 40k rows while the current v2 alpha takes around 800ms.
For now I am afraid that the easiest solution is to do away with the ORM for large selects and construct the queries using the DB class. I know you said that you want to keep the abstraction of the ORM to ease development, one solution is to use as_object('MyModel') to return populated model objects.
On the other hand if performance is your main concern then the ORM is simply not suitable.

Categories