I am working on a project that uses the ORM heavily instead of model relationships in the controller to get to the data (for eg: using leftJoins instead of establishing proper model relationships using hasMany etc and then retrieving through that)
My question is actually to do with the performance. Is using model relationships to access data faster than using leftJoins? I am not sure if Laravel actually does perform a leftJoin under the hood.
I am on a tight schedule so I want to decide if its actually worth trying to refactor the code to use the model relationships and if it would provide gains in performance?
Thanks in advance
Laravel anbd Eloquent may execure queries differently using the relationship as opposed to the join. You can use the barryvdh/laravel-debugbar to see query execution.
My observation is that Laravel will tend to use two queries instead of a join if possible. It will execute the first query and then using the IDs returned from that query execute a second query on the "joined" table as a "where myID in (1,2,3,4,5..)" and then stitch the results together.
As others have suggested, building up a quick test and seeing what works best in your environment would be the best bet. I prefer to use the relationships as much as possible becuase you can easily read what your code is doing.
Joins and relationship are not opposite of each other. Relationships are for ease of development, cleaner, more readable code, easy to maintain. Performance difference is almost non existant unless you are developing a data miner of some sort.
Edited
If you are focusing on performance, you can work on implementing caching in a efficient way.
Related
I am starting a project which requires some kind of hard (complex) database structure (meaning that It's not easy to draw a database structure).
My questions:
I know very well how laravel relationships work. Will those relationships be enough to construct a database by following it and then the database must be constructed so good that code written in application should be super flexible.
If those relationships are not enough, what do you advise me to do?
Eloquent Relationships are good for almost all cases, otherwise if you need a more complex query you can use Fluent Queries
Query Builder
Is it possible to have an eloquent query builder return StdClass rather then Model?
For example User::where('age', '>', 34)->get() returns a Collection of User models.
Whereas DB::table('users')->where('age', '>', 34)->get() returns a Collection of StdClass objects. Much faster.
Therefore:
Is it possible to prevent hydrating eloquent models and return StdClass objects as a database query builder would, but still leverage the usefulness of an eloquent query builder syntax?
Yes, is possible using the 'getQuery' or 'toBase' method. For example:
User::where('age', '>', 34)->getQuery()->get();
or
User::where('age', '>', 34)->toBase()->get();
In my opinion,
Hydrating models rarely affects application performance
There are so many ORMs out there and if you look at any framework, these questions keep popping up - but the truth, as I've come to realize, is that ORMs hardly affect performance.
More often than not the culprits are the queries themselves and not
the ORM
Let me give you a few examples of why Eloquent models may perhaps be slower than DB facade queries:
1. Model events:
When you have model events (such as saving, creating, etc.) in your models, they sometimes slow down processing. Not to say that events should be avoided, you just need to be careful when and when not to use them
2. Loading Relationships:
Countless times have I seen folks load relationships using appends lists provided by Eloquent and sometimes models have 5-10 relationships. That's 5-10 joins each time you fire an Eloquent query! If you compare that with a DB facade query, it would definitely be faster. But then again, who's the real culprit? Not the ORM, it's the queries (with the extra joins!)
As an example, not so long someone asked a question on this and he/she wondered why an Eloquent query was slower than a raw one. Check it out!
3. Not understanding what triggers an Eloquent query
This is by far the most prominent reason why people think ORMs are slower. They usually (not always) don't understand what triggers a query.
As an example, lets say you want to update a products table and set the price of product #25 to $250.
Perhaps, you write in your controller, the following:
$id = 25;
$product = Product::findOrFail($id);
$product->price = 250;
$product->save();
Then, your colleague says hey, this is super slow. Try using DB facade. So you write:
$id = 25;
DB::table('products')->where('product_id', $id)->update(['price' => 250]);
And boom! It's faster. Again, the culprit isn't the ORM. It's the query. The one above is actually 2 queries, the findOrFail triggers a select * query and the save triggers an update query.
You can and should write this as a single query using Eloquent ORM like so:
Product::where('product_id', 25)->update(['price' => 250]);
Some Good Practices for Query Optimization
Have your database do most of the work instead of PHP: E.g. instead of iterating over Eloquent collections, perhaps frame your DB query in such a manner that the database does the work for you.
Mass Updates Over Single Updates: Pretty obvious. Avoid saving models in for loops, yuk!
For heavy queries, use transactions: DB transactions avoid re-indexing on every insert. If you really need to call say thousands of inserts/update queries in a single function call, wrap them into a transaction
Last but not the least, when in doubt check your query: If you're ever ever ever in doubt, that perhaps the ORM is the real culprit - think again! Check your query, try and optimize it.
If the ORM is slowing things down, use obervers or the Laravel debugbar to compare the queries with and without the ORM. More often than not, you'll find that the queries are different, and the difference isnt in hydration but the actual queries themselves!
It is inefficient to have small models and load them. What ->toBase() does is lowering the inefficiency. Memory inefficiency for 4-5 model persisted attributes with average length between 5 and 10 when loading model is 90+ percent. What is even more inefficient is having many and small models and as definition of hell - when there is a lot of traffic on them. Then you should think of another persistence design for that data. Wisely choose when model is the right home for a piece of data.
I'm doing a web app here using Laravel + AngularJS and I have a question.Do I need a model for each table that I have in my database? There are 87 tables in my database and I need to query all of them according to with the input that the User wants.
I just want to make sure with all tables must have a model file or if just one is enough.
There are 2 ways by which you can access your DB tables:
Eloquent ORM (dependent on Models)
DB Facade Query Builder(independent on Models)
Former, is more clean and best approach to perform DB query and related task, whereas latter is not clean, and it is going to be difficult for you to manage large application, as you told there are 80+ tables in your application.
Also, if you're using Eloquent way, then it's also a better to have a base model, which will have common code which you can inherit in child models. Like if you want to store "user id" who did some DB changes, then in the boot function, you can write Auth::id() and assign that value to changed_by field on your table.
In DB Facade way, you've to hard code table name every time you're performing DB operation, and which leads to inconsistency when you found that you've to change the name of the table, it's a rare scenario still it'll be difficult to manage if in a file there are multiple tables DB operation going on. There are options like, creating a global table name variable which can be accessed to perform DB operation.
Conclusion:
Yes, creating 80+ model for implementing Eloquent way is painful, but for a short term, as the application grows it will be easy for you to manage, it will be good for other developer if they start working on it, as it will give a overview of DB and it will improves code readability.
It depends on how you'd like to handle queries.
If you'd like to use Eloquent ORM, you need model classes to handle objects and relationships. That is a model for a table, except intermediate relationship tables, which may be accessed through pivot attribute.
Raw SQL queries are also supported. You don't really need model classes for them, as each result within the result array will be a PHP StdClass object. You need to write raw SQL though.
See Laravel documentation.
I am working on an architecture redesign at my work and we've basically settled on a loosely-basic MVC custom solution. The intentions are to have the standard CRUD operations plus additional list operations defined in each of the models in our system.
Unfortunately about 30% of the code in our system uses complex joins and otherwise advanced querying that doesn't fit this model. Which is to say it could fit the model, but the list function would be huge and certainly error prone which is something we are trying to solve with the rewrite.
Given that, where would you place complex and very specific queries in such a system? We've been toying with a few options.
Add multiple versions of list/get in addition to the basic ones
Add in custom models for these queries that reside as siblings to the model directory
Don't use models in this situation and add the work directly in the action
We have outsourced help as well so we are attempting to keep it as simple as we can in terms of implementation and maintainability. ORM solutions or other heavyweights are out of the question.
Where would you want to see such things placed as a developer?
I apparently lack the privileges necessary to comment, so I'm posting this as answer...
Could you provide an example or two of the kinds of queries you have that don't fit into a model? Generally speaking: a good ORM will get you a long way, but some queries really are just too hairy to map easily, and if your team already has strong SQL skills the ORM can also seem like it's getting in the way.
First , all you're queries should stay in you're model .
Second , most of mvc frameworks provide more than just simple crud for you're database operations like a query functionality that where you can pass the query string , in this case you can build you're queryes manualy or with a query builder like for example Zend_Db_Table_Select and that handles multiple joins prety well . Or again if we look some place else than Zend let's say Codeigniter , it still provides a query builder for the model where you can add you're joins or build any other kind of complex queries .
That being sayd , it looks like you're base model class ( the one you extend each of you're models ) needs a query builder functionality , then you should be all good as you would be able to build any query you like inside any model you like .
I have similar issues in am MVC framework I've been building from scratch.
I don't particularly like the overhead of SELECT * on complex queries so I didn't build any of that functionality in whatsoever.
It's slower to code, but I code every query by hand in the relevant class (my model calls a Class 99% of the time).
For really complex queries shared amongst various routines, I have functions that return the generic joins and then concat the additional parameters for that particular query.
Example provided as requested:
private function returnFindClientRequests(){
$query = "SELECT
SR.sign_project_name, SR.module_signregister_id_pk
,SRI.module_signregister_sign_id_pk,SRI.sign_location_address
,SRR.status, SRR.module_signregister_item_client_request_id_pk, SRR.client_comment, SRR.requested_by_user, SRR.date_created
,SRR.admin_comment, SRR.date_actioned
,CL.client_name, CL.module_client_id_pk
FROM
`module_signregister` SR, `module_signregister_item` SRI, `module_signregister_item_client_request` SRR, `module_client` CL
WHERE
SR.module_signregister_id_pk = SRR.module_signregister_id_pk
AND SRR.module_signregister_sign_id_pk = SRI.module_signregister_sign_id_pk
AND SRR.requested_by_group = CL.module_client_id_pk
AND " . Database::groupQuery('CL');
return $query;
}
This query is shared amongst some other functions but also uses a call to Database::groupQuery() that us used to return session specific variables to many of the queries.
Models are workers - if you have 100 reports you're potentially going to need 100 models. Joins have nothing to do with MVC - how your data is addressed is another pattern altogether. If you're not using ORM and you're not using active records then all that's left is sending the SQL straight to the server via a model. Probably via a dedicated database class but the model will handle the query and its results.
I would have liked to know if it was a good idea to return a select object from a method like '$selectObj = getSomethingByName($name)' to then pass it to another method like 'getResult($selectObj)' which will do the trick.
The idea is to be able to pass the select object to any usefull function like 'setLimit(10)' or addCriteria('blabla') depending on my model...
But is it a good idea to do this ? it could be 'unsecure' because user will be able to modify the object himself, and i should not want to this..
I used to do simple method before like above but returning the result as a row... but it's sometimes painfull when you have complex statement depending on different tables..
The problem you are facing (complex statements depending on different tables) is an old and widespread problem with ORM frameworks in general. There are lots of things SQL can do, that an ORM doesn't do very well. Inevitably, you have to make up the different in complexity by writing lots of complicated code in your Controller or your View.
Instead, use a Domain Model pattern and encapsulate the complex multi-table database logic into one place, so your Controllers and Views don't have to know about all the sundry details. They just know about the interface of your Domain Model class, and that class has the sole responsibility to know how to fetch the information from the database.
Remember: a Model "HAS-A" table (or multiple tables) -- instead of Model "IS-A" table.