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

[8.x] Add withColumn() to QueriesRelationships to support min, max, sum and… #34965

Merged
merged 1 commit into from
Oct 27, 2020

Conversation

khalilst
Copy link
Contributor

@khalilst khalilst commented Oct 25, 2020

I add a method named withColumn (copied/edited from withCount) method to support more SQL aggregation functions like min, max, sum, avg over relationships, e.g.

Post::withCount('comments');
Post::withMax('comments', 'created_at');
Post::withMin('comments', 'created_at');
Post::withSum('comments', 'foo');
Post::withAvg('comments', 'foo');

Also, the withCount methods uses withColumn to avoid duplicated code and it works with backward-compatibility.
However, it is possible to call other/custom SQL function using withColumn, e.g.

Post::withColumn('comments', 'created_at', 'distinct');
Post::withColumn('comments', 'content', 'length');
Post::withColumn('comments', 'created_at', 'custom_function');

In addition to, it is possible to add relationship's column to selected columns without SQL function, e.g.

Comment::withColumn('post', 'title');
Post::withColumn('comments', 'content');

Both are working. Please consider the sub-query limited to have only one result.

@GrahamCampbell GrahamCampbell changed the title Add withColumn() to QueriesRelationships to support min, max, sum and… [8.x] Add withColumn() to QueriesRelationships to support min, max, sum and… Oct 25, 2020
@driesvints
Copy link
Member

This PR unfortunately contains breaking changes to a method signature. It also contains unrelated style changes to docblocks etc. Please send to master.

@driesvints driesvints closed this Oct 26, 2020
@khalilst
Copy link
Contributor Author

@driesvints
I think there was a misunderstanding because of the similarity between withCount() and withColumn() source due to git diff.
The withCount() method has no changes to the method signature and it is completely the same as the original source.

    /**
     * Add subselect queries to count the relations.
     *
     * @param  mixed  $relations
     * @return $this
     */
    public function withCount($relations)
    {
        return $this->withColumn(is_array($relations) ? $relations : func_get_args(), '*', 'count');
    }

Just the body of the method has been changed.
And the changes to docblocks are completely related, because those blocks are not inside withCount() method, it is inside the general-purpose withColumn() method which is used by withCount, withMin, winMax, .....

@taylorotwell
Please review this PR again because of the mentiond misunderstandings.

@driesvints
Copy link
Member

@khalilst right, thanks for clarifying.

@driesvints driesvints reopened this Oct 26, 2020
@taylorotwell
Copy link
Member

@khalilst did you follow this for this implementation? https://github.com/dwightwatson/aggregate/blob/master/src/AggregateServiceProvider.php

@khalilst
Copy link
Contributor Author

@taylorotwell
Hmmm, honestly no, I did not know about that. However, seems there are similarities, but:
In fact, I was inspired from Builder's sum, min, max, average methods located in src/Illuminate/Database/Query/Builder.php.

I addition to those methods and support for other aggregation methods, I have add capability to return single column without aggregation which are not available in your given link.
Please consider the limit 1 in withColumn.

@taylorotwell
Copy link
Member

OK, after reviewing the implementation by @dwightwatson do you see any problems with this implementation?

@khalilst
Copy link
Contributor Author

@taylorotwell
If you are asking me, I have to say no, there is no problem with this PR's implementation.
The withCount() automatic tests were successful.

@taylorotwell taylorotwell merged commit 95d0c9a into laravel:8.x Oct 27, 2020
@taylorotwell
Copy link
Member

@khalilst this PR forgot to add loadAvg, loadMin, etc. to Eloquent/Collection and Eloquent/Model. Note there are also methods for loadMorphCount.

@khalilst
Copy link
Contributor Author

@taylorotwell
All right. I will add those methods to Eloquent/Collection, Eloquent/Model, and loadMorth....

@kanidjar
Copy link
Contributor

kanidjar commented Oct 29, 2020

hi @khalilst, @taylorotwell, the new withCount has broken our app.

We are using withCount inside a globalScope:

    public static function bootHasAudiences(): void
    {
            static::addGlobalScope(
                'has_audiences',
                function (Builder $query): void {
                    // sort by audience
                    $query->withCount('audiences')->orderBy('audiences_count', 'desc');
                }
            );
        }
    }
    
    public function audiences()
    {
        return $this->belongsToMany(Audience::class)->using(AudiencePivot::class);
    }
Error: Maximum function nesting level of '256' reached, aborting!

before this merge, everything was working correctly.

@khalilst
Copy link
Contributor Author

@kanidjar
I will check it right now.

@taylorotwell
Copy link
Member

@khalilst let me know soon if you see the problem / fix - otherwise I will have to revert everything.

@khalilst
Copy link
Contributor Author

@taylorotwell
I am already working on this. I will let you know the result.

@khalilst
Copy link
Contributor Author

@taylorotwell I tried different projects with the given code by @kanidjar and I have seen no problem.

@kanidjar You have edited your code several times. Are you sure there was not missing code here?
I have to metion that withCount() tests passed successfully.
Please paste the call-stack here.

@kanidjar
Copy link
Contributor

@khalilst here it is: https://gist.github.com/kanidjar/5594c42ced9b7494239bf28733f243a0

I've edited the code to remove all unrelated code.

A simple Item::all() with the global scope enabled is enough.

@khalilst
Copy link
Contributor Author

@kanidjar Thank you. I'm trying to find out the problem.

@khalilst
Copy link
Contributor Author

@taylorotwell @kanidjar I found the problem raised from line 385 at QueriesRelationships.php.
It was not in this PR, I think it was from code modifications/optimizations have done after merging by @sebastiaanluca.

I corrected this line and commit to open related PR #35029.

@kanidjar
Copy link
Contributor

Thank you very much for investigating. So, according to you, the issue comes from this PR #35004

@taylorotwell
Copy link
Member

@khalilst we need the fix in a separate PR. Also, why does that cause the problem. The change @sebastiaanluca made is pretty valid that we would want to wrap the column names.

@kanidjar
Copy link
Contributor

@taylorotwell I can confirm that applying the fix locally fixes the issue.

@khalilst
Copy link
Contributor Author

@taylorotwell Please take a look at the stack trace given by @kanidjar here.
You can see the exact line number 385 which the wrapping done here (at the bottom of gist).
I tested in my local environment and after modifying this line the reported issue resolved.
I will create another PR for this bugfix.

@kanidjar Yes, you are right.

@taylorotwell
Copy link
Member

We can call $this->getQuery()->getGrammar()->wrap() instead.

@taylorotwell
Copy link
Member

Pushed to 8.x. Please remove your fix from this from your other PR.

@taylorotwell
Copy link
Member

@khalilst can you confirm this commit fixes your problem if you apply the change directly in your vendor directory: 20b0c6e

@khalilst
Copy link
Contributor Author

@taylorotwell Yes, it does.

I'm gonna remove the bugfix from other PR.

@taylorotwell
Copy link
Member

Released as 8.12.3.

@robsontenorio
Copy link

robsontenorio commented Oct 30, 2020

@khalilst

The withCount method allow us adding constraints. How can we achieve that with withMax, withMin ?

$posts = App\Models\Post::withCount(['votes', 'comments' => function (Builder $query) {
    $query->where('content', 'like', 'foo%');
}])->get();

@khalilst
Copy link
Contributor Author

@robsontenorio
Same as withCount. Just consider to pass the second argument $column:

Post::withMax(['votes', 'comments' => function (Builder $query) {
    $query->where('content', 'like', 'someting%');
}], 'column_name')->get();

Note: If you are using with{Max, Min, Sum, Avg} for an array of relations, the column should exist in all relations.
In the above example, the column_name should be available in votes & comments,

@robsontenorio
Copy link

@khalilst thank you! The trick was second param column_name , not present in withCount (at least on laravel docs)

@khalilst
Copy link
Contributor Author

@robsontenorio You are welcome. I will create a PR to Laravel Doc asap.

@Alexmg86
Copy link

Alexmg86 commented Nov 3, 2020

@taylorotwell
Good afternoon!
I am a big fan of your work and in particular laravel is very upset. That year I tried to suggest you new things that were uploaded in this commit #32474 but you rejected it and offered to write a package.
Which is what I did https://github.com/Alexmg86/laravel-sub-query
But this person just took all my work and added here and was accepted?
Very disappointing.

@driesvints
Copy link
Member

@Alexmg86 sometimes we reconsider feature requests. Don't be disappointed that your PR wasn't accepted at the time. Be happy that it's now finally in the core of the framework.

@driesvints
Copy link
Member

@Alexmg86 also, looking at your PR it wasn't closed because we didn't accept it. It was closed because it lacked a thorough explanation of the feature and tests.

@Alexmg86
Copy link

Alexmg86 commented Nov 3, 2020

@driesvints But you could have said it, but a person just stole my work and this is at least bad. I'm really very upset

@driesvints
Copy link
Member

driesvints commented Nov 3, 2020

Can you point out which parts the contributor stole from you?

@Alexmg86
Copy link

Alexmg86 commented Nov 3, 2020

@driesvints Methods related to aggregation withMax, withMin, withSum, withAvg. You can see the dates of my commits, ideas.

@driesvints
Copy link
Member

Please link to them.

@Alexmg86
Copy link

Alexmg86 commented Nov 3, 2020

@driesvints how to do it?

@driesvints
Copy link
Member

driesvints commented Nov 3, 2020

@Alexmg86 select the links to the pieces of your code and paste them here so we can compare them.

@khalilst
Copy link
Contributor Author

khalilst commented Nov 3, 2020

@taylorotwell
Good afternoon!
I am a big fan of your work and in particular laravel is very upset. That year I tried to suggest you new things that were uploaded in this commit #32474 but you rejected it and offered to write a package.
Which is what I did https://github.com/Alexmg86/laravel-sub-query
But this person just took all my work and added here and was accepted?
Very disappointing.

@Alexmg86 I have never seen your code and I can not understand your insulting words without any proof.
You think you are the only person in this world that knows about aggregation.
I have just used Laravel's latest source the old withCount method and changed it to support other aggregation funcitons and single column as a result.

@Alexmg86
Copy link

Alexmg86 commented Nov 3, 2020

@khalilst ok. I think so. Maybe you are right

@khalilst
Copy link
Contributor Author

khalilst commented Nov 3, 2020

@Alexmg86 Sure, but definitely you were wrong

@driesvints
Copy link
Member

I'm going to close this off because I believe nothing more constructive can come out of this discussion.

@laravel laravel locked and limited conversation to collaborators Nov 3, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants