Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New methods: BaseActiveRecord::loadRelations() and BaseActiveRecord::loadRelationsFor(). #19893

Merged
merged 1 commit into from
Oct 13, 2023

Conversation

PowerGamer1
Copy link
Contributor

Q A
Is bugfix?
New feature? ✔️
Breaks BC?
Fixed issues #11000, #12743

Equivalent of https://laravel.com/docs/10.x/eloquent-relationships#lazy-eager-loading in Eloquent ORM.

Should be mentioned in Yii2 guide.

@what-the-diff
Copy link

what-the-diff bot commented Jul 13, 2023

PR Summary

  • Introduction of Eager Loading Methods
    These changes introduce two new methods BaseActiveRecord::loadRelations() and BaseActiveRecord::loadRelationsFor(). These methods provide the functionality to pre-load related data for existing primary model instances. This enhancement can significantly improve application performance as it reduces the number of required queries to the database when retrieving associated data. Therefore, it's a beneficial update, particularly for applications with complex relational data structures.

@codecov
Copy link

codecov bot commented Jul 13, 2023

Codecov Report

Attention: 1 lines in your changes are missing coverage. Please review.

Comparison is base (505fd5a) 48.94% compared to head (02ed808) 48.95%.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #19893   +/-   ##
=======================================
  Coverage   48.94%   48.95%           
=======================================
  Files         445      445           
  Lines       42817    42826    +9     
=======================================
+ Hits        20956    20964    +8     
- Misses      21861    21862    +1     
Files Coverage Δ
framework/db/BaseActiveRecord.php 77.77% <88.88%> (+0.18%) ⬆️

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

framework/db/BaseActiveRecord.php Outdated Show resolved Hide resolved
framework/db/BaseActiveRecord.php Outdated Show resolved Hide resolved
@samdark samdark requested review from a team July 14, 2023 14:46
@samdark samdark added the pr:request for unit tests Unit tests are needed. label Jul 14, 2023
@samdark
Copy link
Member

samdark commented Jul 14, 2023

Would you please add tests for the method?

@PowerGamer1
Copy link
Contributor Author

Would you please add tests for the method?

Done.

public static function loadRelationsFor(&$models, $relationNames, $asArray = false)
{
// ActiveQueryTrait::findWith() called below assumes $models array is non-empty.
if (empty($models)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not if (!empty($models)) and avoid empty return statement which raises Codecov warning?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will make the code less readable IMO:

  • your variant uses extra logical operator (which makes expression more complex for human understanding);
  • the ! operator is easily missable at a glance;
  • my variant has a clearly visible useful work of a function (last line) and a clearly separated condition when it can be skipped; your variant would have these tangled up making it harder to understand what is the normal function flow and what are the exceptions in it.

Making the code less readable just to shut up a warning in some imperfect external tool is a bad idea.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But shouldn't it cover the test, for when the model is empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But shouldn't it cover the test, for when the model is empty?

You mean if there should be a test for a loadRelationsFor([], /*...*/) call? I don't think this test will be very useful.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it is testing the code, in the case that the model is empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it is testing the code, in the case that the model is empty.

So you just want to do the testing for the sake of doing testing even if it doesn't test anything useful at all? If you disagree, please show us how such test could possibly look and explain its usefulness.

Copy link
Contributor

@mtangoo mtangoo Jul 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PowerGamer1 when things breaks for empty models with some bug in the code (let say someone else modified your now empty return to something else, how will we detect it? That's where this test comes to rescue. Currently sound like useless, but it will make code "future proof" sort of!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PowerGamer1 ... let say someone else modified your now empty return to something else, how will we detect it?

At present, testing a single return statement is useless. If, in the future, as you say someone replaces/modifies this return statement with a MORE COMPLEX piece of code - then at that point in time testing such MORE COMPLEX piece of code might become warranted. Obviously, testing of such (currently non-existent) more complex code path should be done when it is introduced by the person who introduced it.

Also, if I were you (i.e. engine maintainer), I would be much more concerned with testing infinitely more complex and currently completely untested pieces of code (such as ActiveQueryTrait::findWith() and myriad others). In fact, the only reason I am currently writing any tests at all is the fact that new shortcut methods rely on such untested functionality - i.e. I don't really test NEW code, I have to test already EXISTING code.

P.S. That test you want me to write - have you even imagined how exactly it will look? What exactly are you going to assert (like assertTrue() etc.) in it?

@PowerGamer1
Copy link
Contributor Author

It looks to me that the code relies on the "inverseOf" definition of the related ActiveRecord hasOne/hasMany relation. Is that correct? And if so, what happens when there is no "inverseOf" relation defined?

No such reliance on the inverseOf at all. The new methods just call (in a convenient way) the same existing functionality that is executed when you perform the eager loading using with() in a call like:

Customer::find()->with('orders.items')->all();

That example above works without defining inverse relation and so do new methods which you can see in the tests (neither Customer::getOrders() nor Order::getItems() relation in the tests uses inverseOf).

@rhertogh
Copy link
Contributor

@PowerGamer1 Yes, when I dived deeper into it I came to the same conclusion and removed my comment. But your response beat me to it 😄.

@samdark samdark removed the pr:request for unit tests Unit tests are needed. label Jul 27, 2023
@samdark samdark added this to the 2.0.49 milestone Jul 27, 2023
@samdark
Copy link
Member

samdark commented Jul 27, 2023

@PowerGamer1 All good. Would you please resolve conflicts so I can merge it?

@PowerGamer1
Copy link
Contributor Author

@PowerGamer1 All good. Would you please resolve conflicts so I can merge it?

Done.

@samdark
Copy link
Member

samdark commented Aug 18, 2023

Please resolve the conflicts so I can hit the merge button. Thanks.

1 similar comment
@samdark
Copy link
Member

samdark commented Aug 18, 2023

Please resolve the conflicts so I can hit the merge button. Thanks.

@PowerGamer1
Copy link
Contributor Author

@samdark Conflicts resolved.

@samdark
Copy link
Member

samdark commented Aug 26, 2023

Damn :( Conflicts again. @PowerGamer1 need your help.

@PowerGamer1 PowerGamer1 force-pushed the fix12743 branch 2 times, most recently from ba168ec to 9e3595f Compare September 7, 2023 09:12
@bizley bizley modified the milestones: 2.0.49.1, 2.0.50 Oct 5, 2023
@PowerGamer1
Copy link
Contributor Author

You might want to merge this soon as I will not be able to rebase this commit to latest master for much longer (as I will be leaving github due to forced 2FA policy).

@bizley
Copy link
Member

bizley commented Oct 13, 2023

@PowerGamer1 could you resolve conflicts one more time? I'll merge this immediately after. Sorry for the long time waiting.

@PowerGamer1
Copy link
Contributor Author

@PowerGamer1 could you resolve conflicts one more time? I'll merge this immediately after. Sorry for the long time waiting.

@bizley No worries, conflicts resolved.

@bizley bizley merged commit 4fe40f9 into yiisoft:master Oct 13, 2023
49 checks passed
@bizley
Copy link
Member

bizley commented Oct 13, 2023

Thank you. Sorry to hear you are leaving GH.

@xicond
Copy link
Contributor

xicond commented Oct 14, 2023

I'm wandering, is this support nested relationship like dot notation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants