Как грамотно построить дерево пользователей в Bitrix?
Пользователь A приглашает пользователя B на сайт и сохраняется для B как пригласивший. Пользователь B приглашает таким же образом пользователя C, а C приглашает D и E. Каким образом лучше построить структуру в Bitrix, чтобы удобно получать дерево приглашённых для любого пользователя до определённого уровня? То есть, для пользователя A дерево 3 уровней будет: B > C > D + E.
Если просто сохранять ID пригласившего, например, в пользовательском поле (UF), то в этом случае получение всего дерева потребует количества запросов, равного глубине дерева. То есть, мы получаем всех пользователей, у которых ID пригласившего равен текущему, затем собираем ID полученных и проделываем то же самое, и т.д. Может быть, есть какие-то способы сократить количество запросов?
Вопрос в том, что вы хотите от этих данных, кроме рисования дерева. Поиск по ним, например.
Так-то можно просто воткнуть пользовательское строковое поле, в котором через разделитель вписаны ID вышестоящих по дереву. Приглашенному просто вписывается это поле родителя + разделитель + его ID.
Нет, так не годится, потому что при таком подходе потребуется при добавлении каждого нового пользователя обновлять это поле у всех элементов дерева. А их может быть много.
> что вы хотите от этих данных,
Чисто от них - иметь возможность получить список:
1. Всех пригласивших выше меня.
2. Всех приглашённых мною и далее (как в примере в тексте вопроса).
По сути, только эти две функции и требуются. Но и процесс добавления элемента в дерево тоже должен быть простым.
photosho, если у каждого пользователя есть поле, в котором вписано #12345#12346#12555# - то дерево для него вы построите элементарно, а его потомков так же элементарно найдете по LIKE '%#13000#%'.
Вряд ли эти цепочки будут такими уж длинными, а необходимость их получать - такой уж частой, чтобы маяться ради этого рекурсией по базе или JSON-полями.
Обновлять это поле у элементов не требуется, оно пишется один раз.
Adamos, вчера уже плохо воспринимал информацию и почему-то подумал, что вы предлагаете хранить список нижестоящих. В этом случае без рекурсии при добавлении не обойтись. А при хранении вышестоящих новый пользователь получит ID пригласившего + список вышестоящих пригласившего - рекурсий не будет. Имеет место дублирование данных, но при относительно небольшом количестве пользователей это не должно стать проблемой. Во всяком случае, таким образом мы избежим рекурсий при получении данных.
Но остаётся один вопрос - если мы получаем список нижестоящих по "LIKE '%#13000#%'", то как мы можем понять уровень каждого полученного элемента относительно текущего? Это тоже важная часть задачи.
P.S. Для каждого полученного пользователя на уровне PHP разбить список вышестоящих на массив по "#" - и индекс элемента, в котором находится ID текущего пользователя - и будет его уровнем. В зависимости от порядка хранения пользователей, возможно, придётся обратить массив.
photosho, можно записывать числа строго в 6, 7 или, если вы большой оптимист, 8 цифр.
Тогда глубину можно будет определять простым сравнением длин строк, даже прямо в БД.
Я бы либо создал инфоблок и создавал разделы на основе связей между пользователями, либо сам написал реализацию для хранения дерева в базе. Вот статья о разных способах хранения дерева в реляционной бд.
Битрикс для разделов использует Nested sets.
Относительно идеи с разделами есть сомнения - где-то читал жалобы на скорость работы при большом количестве разделов. Спасибо за ссылку на статью. Думаю, в случае со статическими данными, как у меня, лучше подойдёт ответ выше - там есть дублирование данных, но все данные получаются без рекурсии, быстрыми запросами.
photosho, Проблема с разделами из за того что дерево нужно хранить в таблице. И на это есть накладные расходы. В nested set - это пересчет left, right margin. Да и проблемы эти начинаются где то в районе 20000-30000 разделов.