[8.x] Add is() method to 1-1 relations for model comparison #34693
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR adds replicates the
is()
andisNot()
methods of theIlluminate\Database\Eloquent\Model
to the 1-1 Eloquent relations:BelongsTo
,HasOne
,MorphTo
andMorphOne
.We can now do model comparisons between related models, without extra database calls! This is especially useful in gates/policies when we need to know if a model is owned or owns another model.
Example on a
PostPolicy
with a simpleBelongsTo
relationship:Example with a polymorphic
MorphOne
relationship:The implementation is similar to the
Model
, it compares the keys, the tables, and the connections of two models. But instead of comparing the primary keys usingModel::getKey()
, it compares the parent key of the parent model of the relationship with the foreign key of the given model. Both key names are known to the relation instance, so the comparison is straightforward.The only thing that is not really accurate is the type of the keys. A model usually has an integer or a string primary key, but the foreign keys are not cast to the related model's primary key type, or sometimes the relationships do not include any primary key, but are constrained by other keys. For this reason, if one of the keys is an integer, we are casting both keys to integers before comparing them, otherwise we are comparing them as they are.
For convenience, the functionality is defined in a
ComparesModels
trait, so it can be used on custom relationships.This trait needs two methods to be implemented:
getParentKey()
which already existed onHasOne
/MorphOne
relationsgetRelatedKeyFrom($model)
, which is new, but is quite useful because it can be reused in many places in the existing relations codebase, instead of calling$model->{$this->ownerKey}
,$model->{$foreign}
,$parent->{$this->localKey}
, etc. in so many different ways. I've refrained myself from refactoring these calls, but I can do that in a next PR.This PR was inspired by this discussion on Reddit: https://www.reddit.com/r/laravel/comments/iylnnl/this_is_how_we_write_our_policies_now.
Both unit and integration tests are in place.
PS: I'm also pinging @staudenmeir since he has a deep understanding of eloquent relations.