[11.x] Fix fluent syntax for HasManyThrough when combining HasMany followed by HasOne#53335
Conversation
|
Thanks for submitting a PR! Note that draft PR's are not reviewed. If you would like a review, please mark your pull request as ready for review in the GitHub user interface. Pull requests that are abandoned in draft may be closed due to inactivity. |
|
@calebdw Maybe I can ask you for help here on the typing, given all your (amazing!) work on adding generic types for all this. Currently the type of the resulting relation is inferred based (solely) on the type of the After my change, the resulting type is also influenced by |
Hmm, you might need to add a new |
f51b643 to
a8e18c3
Compare
a8e18c3 to
efe1781
Compare
…ional template type
| * : ( | ||
| * $relationship is \Illuminate\Database\Eloquent\Relations\HasMany<TIntermediateModel, $this> | ||
| * ? \Illuminate\Database\Eloquent\PendingHasThroughRelationship<TIntermediateModel, $this, \Illuminate\Database\Eloquent\Relations\HasMany<TIntermediateModel, $this>> | ||
| * : \Illuminate\Database\Eloquent\PendingHasThroughRelationship<TIntermediateModel, $this, \Illuminate\Database\Eloquent\Relations\HasOne<TIntermediateModel, $this>> | ||
| * ) |
There was a problem hiding this comment.
You can probably clean up the conditions if you add a Relation template type here:
/**
* Create a pending has-many-through or has-one-through relationship.
*
* @template TIntermediateModel of \Illuminate\Database\Eloquent\Model
* @template TLocalRelation of \Illuminate\Database\Eloquent\Relations\HasOneOrMany<TIntermediateModel, covariant $this>
*
* @param string|TLocalRelation $relationship
* @return (
* $relationship is string
* ? \Illuminate\Database\Eloquent\PendingHasThroughRelationship<\Illuminate\Database\Eloquent\Model, $this, ...>
* : \Illuminate\Database\Eloquent\PendingHasThroughRelationship<TIntermediateModel, $this, TLocalRelation>
* )
*/There was a problem hiding this comment.
Wow, thanks for your quick feedback!
I must admit this was my first idea as well, but somehow I couldn't get it to play right. Using this I was faced with:
------ ------------------------------------------------------------------------------------------------------------------------------
Line Relations.php
------ ------------------------------------------------------------------------------------------------------------------------------
195 Expected type
Illuminate\Database\Eloquent\PendingHasThroughRelationship<Illuminate\Types\Relations\Mechanic,
$this(Illuminate\Types\Relations\User),
Illuminate\Database\Eloquent\Relations\HasOne<Illuminate\Types\Relations\Mechanic,
$this(Illuminate\Types\Relations\User)>>, actual:
Illuminate\Database\Eloquent\PendingHasThroughRelationship<Illuminate\Types\Relations\Mechanic,
$this(Illuminate\Types\Relations\User),
Illuminate\Database\Eloquent\Relations\HasOneOrMany<Illuminate\Types\Relations\Mechanic, covariant
$this(Illuminate\Types\Relations\User)>>
199 Expected type Illuminate\Database\Eloquent\Relations\HasOneThrough<Illuminate\Types\Relations\Car,
Illuminate\Types\Relations\Mechanic, $this(Illuminate\Types\Relations\User)>, actual:
Illuminate\Database\Eloquent\Relations\HasManyThrough<Illuminate\Types\Relations\Car,
Illuminate\Types\Relations\Mechanic,
$this(Illuminate\Types\Relations\User)>|Illuminate\Database\Eloquent\Relations\HasOneThrough<Illuminate\Types\Relations\Car,
Illuminate\Types\Relations\Mechanic, $this(Illuminate\Types\Relations\User)>
215 Expected type
Illuminate\Database\Eloquent\PendingHasThroughRelationship<Illuminate\Types\Relations\Mechanic,
$this(Illuminate\Types\Relations\User),
Illuminate\Database\Eloquent\Relations\HasMany<Illuminate\Types\Relations\Mechanic,
$this(Illuminate\Types\Relations\User)>>, actual:
Illuminate\Database\Eloquent\PendingHasThroughRelationship<Illuminate\Types\Relations\Mechanic,
$this(Illuminate\Types\Relations\User),
Illuminate\Database\Eloquent\Relations\HasOneOrMany<Illuminate\Types\Relations\Mechanic, covariant
$this(Illuminate\Types\Relations\User)>>
224 Expected type Illuminate\Database\Eloquent\Relations\HasManyThrough<Illuminate\Types\Relations\Car,
Illuminate\Types\Relations\Mechanic, $this(Illuminate\Types\Relations\User)>, actual:
Illuminate\Database\Eloquent\Relations\HasManyThrough<Illuminate\Types\Relations\Car,
Illuminate\Types\Relations\Mechanic,
$this(Illuminate\Types\Relations\User)>|Illuminate\Database\Eloquent\Relations\HasOneThrough<Illuminate\Types\Relations\Car,
Illuminate\Types\Relations\Mechanic, $this(Illuminate\Types\Relations\User)>
------ ------------------------------------------------------------------------------------------------------------------------------
This seems to suggest that somehow TLocalRelation is not properly inherited as either HasOne or HasMany, but keeps using HasOneOrMany which is kind of unfortunate.
| * @template TIntermediateModel of \Illuminate\Database\Eloquent\Model | ||
| * @template TDeclaringModel of \Illuminate\Database\Eloquent\Model | ||
| * @template TLocalRelationship of \Illuminate\Database\Eloquent\Relations\HasOneOrMany<TIntermediateModel, TDeclaringModel> |
There was a problem hiding this comment.
I'm not sure if you need all three templates---you might can do with just the relation template given that both the model types can be gleaned from the relation type
There was a problem hiding this comment.
Is there some canonical way to do this? Last time I dived into this I was told there to be no trivial way to do such a thing; see phpstan/phpstan#8814.
|
Adding a new generic type to a class should've been considered a BC break from type system POV. But I guess Laravel doesn't care about that. |
In #45894 'fluent' syntax for has-many-through and has-one-through relations was added. When combining has-one and has-many, one has 4 options:
HasOnefollowed byHasOne=>HasOneThroughHasOnefollowed byHasMany=>HasManyThroughHasManyfollowed byHasMany=>HasManyThroughHasManyfollowed byHasOne=>HasManyThroughAll of these options work fine when using the 'classic' relation definitions. However, the 4th option does not work with the new 'fluent' syntax, as ending with a
HasOnemakes it incorrectly infer the type of the 'full' relation to beHasOneThroughrather thanHasManyThrough.This PR aims to fix this discrepancy between 'classic' and 'fluent' syntax, by fixing the latter to also allow the 4th option. This is illustrated by adding tests in the first commit that will fail without the fix.
Open issue: the inferred typing on the
hasmethod will still incorrectly reportHasOneThroughwith current changeset.