首页 > 解决方案 > laravel 递归按级别/深度显示推荐的用户

问题描述

因此,我正在与会员合作,并在注册和保存谁推荐用户方面做得很好,现在我正在努力向这些用户展示按级别推荐的用户。

级别未保存在数据库中,我认为它是在逻辑区域中递增?

用户表结构

id |  name  | sponsor_id |
 1 |  John  |    NULL    |
 2 |  Jane  |      1     |
 3 |  Jess  |      1     |
 4 |  Joe   |      2     |

所以我想要的输出应该是这样的:

I am John,level 1 是谁的sponsor_id 是我的,level 2 谁的sponsor_id 是我邀请的 id。

ID |  Name  |  Level  |
2  |  Jane  |    1    | 
3  |  Jess  |    1    |
4  |  Joe   |    2    |

其中Jane & Jess的赞助商 ID 是我的,而Joe赞助商 ID 是Jess

用户有赞助商

public function sponsor()
{
    return $this->belongsTo('App\User', 'sponsor_id');
}

用户有推荐

public function referrals()
{
    return $this->hasMany('App\User', 'sponsor_id');
}

标签: phpmysqllaravel

解决方案


如果 MySQL 8(非常建议使用像您这样的分层数据),您可以使用递归 CTE 执行此操作:

WITH RECURSIVE CTE_users_levels(id, name, level) AS 
    ( 
          SELECT id, name, 0 as level
          FROM users 
          WHERE sponsor_id IS NULL 
          UNION ALL 
          SELECT u.id, u.name, cte.level+1 
          FROM CTE_users_levels cte 
          JOIN users u ON cte.id=u.sponsor_id
    ) 

-- To output all users' levels:
SELECT * FROM CTE_users_levels ORDER BY level;

现在您有了包含所有用户列的虚拟表level,您可以查询它

| id  | name | level |
| --- | ---- | ----- |
| 1   | John | 0     |
| 2   | Jane | 1     |
| 3   | Jess | 1     |
| 4   | Joe  | 2     |

DBFiddle


进一步...

从您的 CTE 中查看

CREATE VIEW VIEW_User_Levels AS
(
WITH RECURSIVE CTE_users_levels(id, name, level) AS 
    ( 
          SELECT id, name, 0 as level
          FROM users 
          WHERE sponsor_id IS NULL 
          UNION ALL 
          SELECT u.id, u.name, cte.level+1 
          FROM CTE_users_levels cte 
          JOIN users u ON cte.id=u.sponsor_id
    ) 
SELECT * FROM CTE_users_levels ORDER BY level   
  
 )

现在很容易获得所有用户级别(无需 CTE 声明):

SELECT * FROM VIEW_User_Levels

然后,如果您有很多用户,那么一直重新计算整个树(VIEW 就是这样做的)就有点过时了

然后,您可以在新列中修复用户的级别。

ALTER TABLE users ADD level INT;

并在您的视图的帮助下为所有用户填充该列:

UPDATE users u, VIEW_User_Levels v
SET  u.level=v.level
WHERE u.id=v.id;

这使

SELECT * FROM users

id  name    sponsor_id  level
1   John    null        0
2   Jane    1           1
3   Jess    1           1
4   Joe     2           2

DBFiddle

如果您有很多用户,并且您的目标是大量查询级别列,请索引 IT。

请注意,您也可以仅更新一个用户的级别值(例如,如果其赞助商_id 更改,或者如果用户是新用户)

UPDATE users u, VIEW_User_Levels v
SET  u.level=v.level
WHERE u.id=v.id AND u.name='Jane';

推荐阅读