-
Notifications
You must be signed in to change notification settings - Fork 0
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
Support HasManyThrough relationships #8
Support HasManyThrough relationships #8
Conversation
@@ -54,6 +52,6 @@ public function resolveModelClass(Route $route): string | |||
*/ | |||
final public function namespaceModel($model_class) | |||
{ | |||
return sprintf('%s%s', $this->getAppNamespace(), $model_class); | |||
return sprintf('%s%s', App::getNamespace(), $model_class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AppNamespaceDetectorTrait
was deprecated after Laravel 5.8 and rolled into the Application
.
@@ -18,8 +18,6 @@ | |||
*/ | |||
class RouteGuessingModelResolver implements ModelResolver |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This resolver is not used in our projects and it had no test coverage. It also contained a trait that was deprecated after Laravel 5.8.
I assumed that it was meant to work with named routes with a format of version.resource
since the comments refer to a version and resource.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Left a few comments & questions but the only critical note is adding some documentation about this new HasManyThrough
functionality to the README
src/EloquentQueryModifier.php
Outdated
//Use the relationship to build a new query without the model constraint and name the subtable the same as the requested relationship name | ||
//Alias the "first" key to prevent collisions with other columns | ||
$subQuery = $instance->$relation()->select([ | ||
$related->getQualifiedFirstKeyName().' AS '.($uniqeFirstKey = str_replace('.', '_', uniqId().$related->getQualifiedFirstKeyName())), //Id to left join on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor spelling suggestion
$related->getQualifiedFirstKeyName().' AS '.($uniqeFirstKey = str_replace('.', '_', uniqId().$related->getQualifiedFirstKeyName())), //Id to left join on | |
$related->getQualifiedFirstKeyName().' AS '.($uniqueFirstKey = str_replace('.', '_', uniqueId().$related->getQualifiedFirstKeyName())), //Id to left join on |
@@ -652,6 +653,18 @@ protected function applyNestedJoins(array $relations, Model $instance, $field, $ | |||
// Join related's table on the base table's foreign key | |||
$query->join($table, $instance->getQualifiedKeyName(), '=', $related->getQualifiedForeignKeyName()); | |||
break; | |||
case HasManyThrough::class: | |||
Relation::noConstraints(function () use ($related, $relation, $instance, $query, $field) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to use Relation::noConstraints
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The relation query is constrained to the model that has the HasManyThrough relationship by default.
Going with the same example in the Pouch tests where a User has many Reaction through Post:
$user->reactions query looks like this SELECT FROM users INNER JOIN (SELECT * FROM posts INNER JOIN reactions ON reactions.post_id = posts.id WHERE posts.user_id = ?)
. The inner select query is the relation with constraints.
That is not useful for aggregations on a field that comes from a HasManyThrough relationship. The unconstrained query will have an inner query that looks like SELECT * FROM posts INNER JOIN reactions ON reactions.post_id = posts.id
.
@@ -394,6 +394,19 @@ protected function fill(Model $instance): bool | |||
'value' => $value, | |||
]; | |||
break; | |||
case HasManyThrough::class: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a note to the README documentation to explain this functionality and the limitations
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding this!
What
Adds full support for Repositories that use models that contain a
HasManyThrough
relationship.Why
EloquentRepository
already supports filtering through fields that live in any Eloquent relationship. But, it lacks in these ways:HasManyThrough
relationshipHasManyThrough
relationship.How
Full support for sort and aggregation with fields defined through a
HasManyThrough
relationshipEloquentQueryModifier::applyNestedJoins
- Added a case join HasManyThrough related data - the "through" model and the "far" model - so the fields are accessible when used as a sort or aggregation.Preliminary support for the creation of models related through
HasManyThrough
EloquentRepository::fill()
The ability to create related child models lives here.
Related models are filled and associated according to how the input data is nested.
An example with directly nested model data:
A
User
"HasMany
"Post
s. And, aPost
"HasMany
"Reaction
s.The
posts
are directly nested under under the user, and thereactions
are nested directly underposts
, so the relationship is easily known when it is time to create models and link them.And, an example with a
HasManyThrough
relationship:HasManyThrough
relationships make the relationship more difficult to parse. In this example, aUser
has manyReaction
s through theirPost
s. ThePost
still has theHasMany
relationship withReaction
s. But the "through" relationship is not apparent.There is an assumption that a
Post
withpost_id
already exists.TODO: Come up with a language in this fill body to relate a not-yet-created
reactions
model to a not-yet-createdposts
model. Create models in the fill action in an order where a new "through" model (in this caseposts
) is created first, and thereactions
model is created last and contains theposts
id.EloquentRepository::cascadeRelations
This is where the related models created in
EloquentRepository::fill()
are saved along with their relationships with other models. The persistence of Models that were created because of aHasManyThrough
relationship are delayed until all other related models are saved. There is the same assumption that the "through" model already exists.TODO: Handle cases where the "through" model is part of the same fill operation, and the id is not yet known until the "through" model is saved.
TODO: Properly handle chained
HasManyThrough
relationships, and save the models in the correct order.CC 🐨