Мне для проекта надо сделать систему разграничения доступа к контенту по стилю очень похожую на ВКонтакте. Т.е. у каждого «поста» (назовем так для упрощения) должны быть настройки вида: Видно только друзьям; Видно всем; Видно зарегистрированным; Видно следующим группам (кастомный выбор групп которые пользователь создал сам для себя из своих контактов).
Я крепко задумался как реализовать подобную вещь, чтобы когда я вытаскиваю «посты» из MySQL прямо в этом же запросе можно было наложить фильтр и вытаскивать только те «посты», которые доступны пользователю. Если бы не кастомные группы, то я думал в сторону маркеров типа:
Видно всем — 0
Видно зарегистрированным — 1
Видно друзьям — 2
Ну и соответственно для текущего пользователя предрассчитывать:
Зарегистрирован? +1 в индикатор.
А дальше на уровне SQL запроса определять дружбу с владельцем поста, и если друг, то делать еще +1 в индикатор.
А дальше накладывать фильтр вида WHERE post.access_level <= calculated_access_level
Но как в таком раскладе быть с кастомными группами — не понятно. На уровне инстинктов есть ощущение, что надо как-то завязываться на битовые маски, но оно ничем не находит свое подтверждение. Гугл помог не сильно.
Для меня ключевым является максимальная простота и скорость отбора «постов» в соответствии с установленным уровнем доступа.
Решать такую задачу не приходилось, так что другие подходы пока не представляю. Я бы пошёл куда-то в эту сторону:
Таблица:
post_id, access_level, access_value
access_level — это ваши «только друзьям», «всем» и т.д. Сюда же входит пункт «перечисленным группам» (тип поля — enum для удобочитаемости)
Для access_level = 'перечисленные группы' заполняются значения access_value соответственно по id группы на строку. Сюда же хорошо ляжет фильтр «только таким-то пользователям»
Соответственно, выбираем видимые посты:
select post_id from posts_access where (access_level = 'groups' and 'access_value' in (группы этого юзера)) or access_level='all' — и далее условия
интересно. но получается, что если автор выбрал доступ groups, то мне надо по id автора посмотреть какие группы у него есть, и в какие из них вхожу я (как человек открывающий страницу). Т.е. надо делать join между автор-группы_автора и группы_автора-члены_групп. Причем эти join'ы будут даже если уровень доступа стоит НЕ groups. Не хочу лишние два джойна каждый раз :) Возникает желание денормализовать отношение группы-членыучастники чтобы сразу наложить еще и фильтр по участник_id=мой_id
И я вообще не хочу делать этот join если пользователь незарегистрирован или не вошел в систему, так что буду отрезать лишнюю часть запроса в PHP если это так.
Не нравится только то, что если меня включили в две группы, и им обеим разрешен доступ, то я получу задубленные строки. Надо еще distinct добавлять =\ но, видимо, тут никуда не деться…
Абстрактно в худшем случае получается вот такой SQL:
select * from (
select p.*,
case when p.access_level in ('all') then 1
when p.access_level in ('registered') and $current_user_id>0 then 1
when p.access_level = 'groups' and pa.user_id is not null then 1
when p.access_level in ('friends') and uf.friend_id is not null then 1
else 0 end is_granted
from posts p,
left join posts_access pa
on p.post_id=pa.post_id
and p.access_level='groups'
and pa.user_id=$current_user_id
left join user_friends uf
on p.user_id=uf.user_id
and uf.friend_id=$current_user_id
) where is_granted=1
При условии что posts_access это денормализованная таблица групп-пользователей.
Ну и в зависимости от того вошел пользователь или нет, sql будет деформироваться. Если не вошел — будет простой запрос к posts с фильтрами и без лишних join'ов.
Да, так и сделаю, пожалуй) спасибо, ставлю «решение»