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

contain()ing an association with strategy=join with joins in the query builder affects outer query #17184

Open
andres-ml opened this issue Jul 10, 2023 · 1 comment
Milestone

Comments

@andres-ml
Copy link

Description

Hi,

I feel like this may have been already talked about / be a known query builder "gotcha", but I can't seem to find issues or mentions about it in the docs. Using matching/innerJoinWith inside a contain is limiting the results of the base query, if the contained association uses a join strategy (the default for hasOne / belongsTo). The provided examples use hasOne.

What I did

A hasOne B
B belongsTo C

$this->A->find()->contain(['B' => fn($q) => $q->innerJoinWith('C')])

What I got

Only items from A that have one associated B

What I expected

Every row from A, regardless of whether or not they have associated Bs

Workarounds

Changing strategy to select, lazy loading after query with loadInto, rewriting into a subquery, etc.

$this->A->find()->contain(['B' => [
  'queryBuilder' => fn($q) => $q->innerJoinWith('C'),
  'strategy' => 'select',
])

None of the workarounds seem ideal to me as they require you to be actively aware of the implementation when containing the association (in the examples the innerJoinWith is evident, but in real life scenarios you're likely to use finders).

Solutions

  • if the difference in behavior between strategies is intended, maybe we could mention it in the documentation. I feel like it's easy to stumble upon this bug as you're composing queries with just the default association settings. Also, I'd like to know if I missed any better workarounds.
  • if it's not intended, a possible solution may be having the EagerLoader detect whether or not the query builder adds any join clauses, and automatically change the strategy of the contain to select.

Test/reproduce

git-apply the following patch https://gist.github.com/andres-ml/988d4202d607a63556a842eb0ef79869 into a fresh cakephp/app, then run ./vendor/bin/phpunit tests/TestCase/Model/

CakePHP Version

4.4.15

PHP Version

No response

@markstory markstory added this to the 4.4.16 milestone Jul 11, 2023
@markstory markstory added the ORM label Jul 11, 2023
@markstory
Copy link
Member

markstory commented Jul 11, 2023

This is likely due to differences in how the SQL is generated. Containing an association does behave differently because of how the semantics of the ORM have worked for a long time.

The SQL generated for the 'wrong' results is roughly

SELECT ...
FROM users
LEFT JOIN subscriptions
INNER JOIN products

Which ends up implicitly making both joins inner. I think this might be the source of your wrongness.

@markstory markstory modified the milestones: 4.4.16, 4.4.17 Aug 8, 2023
@markstory markstory modified the milestones: 4.4.17, 4.4.18 Aug 20, 2023
@markstory markstory modified the milestones: 4.4.18, 4.4.19 Sep 28, 2023
@markstory markstory modified the milestones: 4.4.19, 4.5.1 Oct 15, 2023
@markstory markstory modified the milestones: 4.5.1, 4.5.2 Nov 15, 2023
@markstory markstory modified the milestones: 4.5.2, 4.5.3 Dec 15, 2023
@markstory markstory modified the milestones: 4.5.3, 4.5.4 Jan 20, 2024
@markstory markstory modified the milestones: 4.5.4, 4.5.5 Mar 6, 2024
@markstory markstory modified the milestones: 4.5.5, 4.5.6 May 3, 2024
@markstory markstory modified the milestones: 4.5.6, 4.5.7 Jun 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants