eloquent-has-many-deep icon indicating copy to clipboard operation
eloquent-has-many-deep copied to clipboard

Support custom relations

Open G3z opened this issue 3 years ago • 17 comments

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 ?

G3z avatar Feb 04 '22 10:02 G3z

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?

staudenmeir avatar Feb 04 '22 21:02 staudenmeir

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

G3z avatar Feb 07 '22 10:02 G3z

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

Can you share this class?

staudenmeir avatar Feb 07 '22 19:02 staudenmeir

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

G3z avatar Feb 08 '22 09:02 G3z

Thanks, I'll take a look.

staudenmeir avatar Feb 08 '22 21:02 staudenmeir

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

staudenmeir avatar Feb 08 '22 23:02 staudenmeir

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.

G3z avatar Feb 09 '22 08:02 G3z

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.

staudenmeir avatar Feb 10 '22 21:02 staudenmeir

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

G3z avatar Feb 11 '22 08:02 G3z

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)

G3z avatar Feb 11 '22 16:02 G3z

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

staudenmeir avatar Feb 12 '22 16:02 staudenmeir

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

G3z avatar Feb 12 '22 17:02 G3z

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.

staudenmeir avatar Feb 12 '22 19:02 staudenmeir

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 🙇🏼‍♂️

G3z avatar Feb 12 '22 22:02 G3z

@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;

GreenImp avatar Jun 22 '22 12:06 GreenImp

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

staudenmeir avatar Jun 22 '22 17:06 staudenmeir

@GreenImp What Laravel version are you using?

How would you integrate ancestors in your deep relationship?

staudenmeir avatar Jun 25 '22 21:06 staudenmeir