staudenmeir / eloquent-has-many-deep

Laravel Eloquent HasManyThrough relationships with unlimited levels
MIT License
2.67k stars 157 forks source link

Support custom relations #152

Closed G3z closed 1 year ago

G3z commented 2 years ago

sometimes Laravel relations are not enough and custom classes can be created to define custom relations, for example https://github.com/lazychaser/laravel-nestedset it's not possible to use this kind of relations with this package. Is it possible to develop a contract to be implemented on those relations to enable deep relationing ?

staudenmeir commented 2 years ago

It's basically impossible to develop a contract like that. I looked into supporting other complex relationships from my packages https://github.com/staudenmeir/laravel-adjacency-list and https://github.com/staudenmeir/eloquent-json-relations and it would have required a lot of work.

It's not only complicated to generate the SQL for fetching deep relationships like that, but they (can) have highly customized "post-processing" of query results – especially for eager loading.

Do you have a specific use case for https://github.com/lazychaser/laravel-nestedset?

G3z commented 2 years ago

Thank you fro your response.

I use it to model a hierarchical user group structure groups

-child group
-granchild group
-child group

then I have things associated (clients, permissions, etc..) to parent group or granchild group and users associated to parent group are associated also to clients in a hierarchical way: clients set on granchild are associated to users in parent group Then I can have other things associated to clients, like machines and I should be able to tell that a user has many machines through user->groups->clients->machines

As of now I have created a service class to get User->Client and User->Machine relation

staudenmeir commented 2 years ago

As of now I have created a service class to get User->Client and User->Machine relation

Can you share this class?

G3z commented 2 years ago

I've set up a PHPSandbox

I would like a deep relation from User to Machine, please have a look at app\Models\User.php, web.php, welcome.blade.php and DatabaseSeeder.php

staudenmeir commented 2 years ago

Thanks, I'll take a look.

staudenmeir commented 2 years ago

Do you want to use other nested set relationships besides descendants in deep relationships?

G3z commented 2 years ago

Ideally I would like to support all relations, if you show me how to support a "custom" relation I'll send a PR for the others.

staudenmeir commented 2 years ago

I pushed a draft to the descendants branch. Update the package to dev-descendants to test it in your project.

You need to adjust your models because the deep relationship requires an alias for Hierarchy as it occurs twice in the path. Unfortunately, the descendants relationship isn't initialized correctly and you need to override it:

class User extends Model
{
    public function extended_hierarchies()
    {
        return $this->hasManyDeepFromRelations($this->hierarchies(), (new Hierarchy)->setAlias('alias')->descendants());
    }
}
use Kalnoy\Nestedset\DescendantsRelation;
use Staudenmeir\EloquentHasManyDeep\HasTableAlias;

class Hierarchy extends Model
{
    use HasTableAlias;

    public function descendants()
    {
        return new DescendantsRelation((new $this)->newQuery(), $this);
    }
}

If this works for you, we'll have to see how similar the other relationships are.

G3z commented 2 years ago

It's perfect! I've updated the phpsandbox (I've also installed debugbar) and everything works like a charm

G3z commented 2 years ago

I've noticed a bug: if you try dd($user->extended_hierarchies;) this error is returned

SQLSTATE[HY000]: General error: 1 ambiguous column name: hu._lft (SQL: select "hierarchies" as "hu.*", "hierarchy_user"."user_id" as "laravel_through_key" from "hierarchies" as "hu" inner join "hierarchies" as "hu" on "hu"."_lft" between "hu"."_lft" + 1 and "hu"."_rgt" inner join "hierarchy_user" on "hierarchy_user"."hierarchy_id" = "hu"."id" where "hierarchy_user"."user_id" = 1)
staudenmeir commented 2 years ago

How is your extended_hierarchies relationship defined? Are you overriding descendants?

G3z commented 2 years ago

it's the one you posted https://github.com/staudenmeir/eloquent-has-many-deep/issues/152#issuecomment-1035534848

staudenmeir commented 2 years ago

It's perfect!

Which query worked?

if you try dd($user->extended_hierarchies;) this error is returned

$user->extended_hierarchies works for me.

How is your extended_hierarchies relationship defined?

I asked because you changed the alias and maybe changed something else.

G3z commented 2 years ago

I'm very sorry, you are correct. I did remove this because $user->machines was working without it but now i understand why it's important

public function descendants()
    {
        return new DescendantsRelation((new $this)->newQuery(), $this);
    }

as you made it everything works, sorry again 🙇🏼‍♂️

GreenImp commented 2 years ago

@staudenmeir this looks good, is there any chance of getting it working with ascendants as well?

I have a Model, which has a node relationship. node is a nested set leaf. On my model, I then want another relationship for the top level parent of the node.

Using nested sets, I can do something like:

$model->node()->ancestors()->whereIsRoot()->first();

As a relationship, the query needs to look something like (Where ? is the ID of the model's node):

select *
from brands
         join brands as through
              on through._rgt between brands._lft and brands._rgt
where brands.id != ?
      and brands.parent_id is null;
staudenmeir commented 2 years ago

@G3z Did you find some time to look at any of the other nested set relationships?

staudenmeir commented 2 years ago

@GreenImp What Laravel version are you using?

How would you integrate ancestors in your deep relationship?