Open eli-s-r opened 2 years ago
Hi @eli-s-r,
Thanks. I'm working on the *OfDescendants
relationships, but I'm not sure they would actually solve your case:
Do you need descendantTags
to look like this?
User
-> morph-to-many -> Tag -> recursive descendants -> Tag
@staudenmeir yes, exactly!
@staudenmeir so would this not be covered by the *OfDescendants
relationships you mentioned you're working on?
Unfortunately not, *OfDescendants
relationships work the other way around:
Tag -> recursive descendants -> Tag -> morph-to-many -> User
Your case/direction is a whole new type of relationship.
How would you use a descendantTags
relationship in your app? There might be workarounds.
@staudenmeir we have a bank of educational activities (and other things) that can be tagged by many things (e.g., topic). The structure of the tags is a graph, so breakfast might be a child of food, but also a child of morning activities. If a user searches for food, it's important that anything tagged with food's descendants is also in the results.
For the activity -> ancestor tags direction, what I'm doing is to use the results of the tags
query as input into a second query:
public function _getAncestorTagsQuery(
bool $taggablePivotsWithTrashed = false,
bool $ancestorsWithTrashed = false,
bool $includeSelf = false,
): Builder|Tag {
$descendantTagIds = $this
->tags(withTrashed: $taggablePivotsWithTrashed)
->pluck('tags.id');
$baseRelName = $includeSelf
? 'descendantsAndSelf'
: 'descendants';
$relName = $ancestorsWithTrashed
// resolves via `resolveRelationUsing`
? Tag::getRelationNameWithTrashed($baseRelName)
: $baseRelName;
return Tag::query()
->whereHas(
$relName,
fn (Builder|Tag $query)
=> $query->whereIntegerInRaw(
'id',
$descendantTagIds,
),
);
}
For the tags -> descendants -> activities direction, I'm doing the inverse:
public function _getDescendantsQueryForTaggableModel(
string $modelClass,
bool $descendantsWithTrashed = false,
bool $taggablePivotsWithTrashed = false,
bool $includeSelf = false,
): Builder|HasTagsInterface {
$descendantQuery = $includeSelf
? $this->descendantsAndSelf(withTrashed: $descendantsWithTrashed)
: $this->descendants(withTrashed: $descendantsWithTrashed);
$descendantTagIds = $descendantQuery->pluck('id');
$relName = $taggablePivotsWithTrashed
? $modelClass::getRelationNameWithTrashed('tags')
: 'tags';
return $modelClass::query()
->whereHas(
$relName,
fn (Builder|HasTagsInterface $query)
=> $query->whereIntegerInRaw(
'tags.id',
$descendantTagIds,
),
);
}
edit: just realized my original goal on the first snippet above was inverted -- I want the ancestors of tags on a model, not the descendants. I updated the first snippet accordingly
For the tags -> descendants -> activities direction, I'm doing the inverse:
A MorphToManyOfDescendants
relationship as I described here would solve your inverse case, right (with Activity
instead of User
)?
Unfortunately not, *OfDescendants relationships work the other way around: Tag -> recursive descendants -> Tag -> morph-to-many -> User
Hi @staudenmeir, thank you so much for creating this amazing package and for releasing the new graphs feature last week, which perfectly aligns with our use-case. I was wondering if there's a way to get the functionality of the
MorphtoManyOfDescendants
relation that is supported for the tree structure OOTB to work for the graph structure.Below is an oversimplified example of what I'm trying to accomplish (note also the workaround for soft-deleted pivots, there's probably a better way to handle this for the graph structure than what I'm doing?):
Everything above works smoothly. What would be amazing is to be able to define a
descendantTags
relation onHasTags
exactly like is possible for the tree structure:I also tried using your
eloquent-has-many-deep
package, but I don't think it supports the intermediateDescendants
relation (understandably).Do you know of a way to get something like this to work? Thank you so much!