dubr
@dubr
пыхарь

Как хранить значения, зависящие от контекста?

Привет! Мне то там то тут попадается задачка, когда нужно сохранять (в реляционной базе данных) состояние сущности, зависящее от некоторого контекста.

Ставлю тег "Drupal", потому что в Друпале стопудово есть что-то такое )) Друпальщики, подскажите!

Приведу пример для наглядности.

Есть поле формы для ввода текста новости. Мы хотим для него настраивать описание-подсказку и набор кнопок wysiwyg-редактора, например. На сайте есть две новостных ленты, и при добавлении в каждую из них нужно показывать свою подсказку. А набор кнопок может зависеть от группы, в которой состоит текущий юзер. Итого, при отображении поля нам нужно получить контекст (группа юзера и номер новостной ленты) и собрать соответствующие этому контексту значения.

Первый кандидат - вот такая конструкцию:

field_id | parent_id | user_group   | newsfeed_id | label  | description          | buttons 
--------------------------------------------------------------------------------------------
1        | null      | null         | null        | 'Текст' | 'Текст новости'     | 'basic'
2        | 1         | 'admin'      | null        | null    | null                | 'full'
3        | 1         | null         | 2           | null    | 'Текст пресс-релиза'| null
4        | 3         | 'sekretarsha'| null        | null    | null                | 'none'


Храним все в одной таблице, контекст задается значениями колонок group_id и newsfeed_id. Свойства, заполненные NULL-ом берутся из строчки, на которую ссылаемся по parent_id. Что тут не нравится:

1. если выяснится, что на контекст влияет что-то еще, придется добавлять колонку (менять схему).
2. магическое деление строк на "настоящие" и "доопределяющие".
3. контекст можно задавать только равенством (если надо что-то типа "регистрация > 3 месяцев" - опять идем колдовать со схемой).

Развиваю мысль дальше. Окей, раз мы не знаем точно, как устроен контекст, сделаем для него что-то типа Entity-Attribute-Value:

field:
field_id | context_id | label  | description | buttons

context:
context_id | parent_context_id

context_definition:
context_id | property | operator | value


Теперь наследуются не поля, а контексты (хм, хорошо ли это?). Контекст определяется набором атрибутов, причем для атрибута мы можем задавать не только условие равенства, но и что-то более хитрое. Например, определить секретарш, зарегистрированных дольше 3х месяцев:

context_id | property   | operator  | value
--------------------------------------------------
1          | 'reg_age'  | >         | 7776000
1          | 'group'    | =         | 'sekretarsha'


Понятно, что можно составить список всех атрибутов и вместо кода атрибута хранить id.

Можно и на этом не останавливаться, а оставить в табличке field только то, что точно не может быть переопределено, а все прочие значения тоже хранить в EAV с фильтром по контексту - избавимся от "магических" (ненастоящих) строчек в основной таблице, ну и вообще сможем использовать эту конструкцию для всех таких случаев (чтобы не трогать схему никогда). О ужас, что-то такое:

value_definition:
entity_type | entity_id | context_id | property | value


Но блин... все это сильно пахнет велосипедом :)

Хочу спросить уважаемое сообщество: существует ли какой-то паттерн под этот класс задач, или продукт, где это реализовано в реюзабельном виде? Или хоть по каким словам гуглить :)

Похожие задачи:
- настроить внешний вид блока на сайте в зависимости от текущего раздела / юзера / источника перехода / запущенного A/B-теста и т.д.
- настроить права на операции над объектом в зависимости от групп юзера / свойств объекта / принадлежности объекта юзеру / фазы луны и т.д.
- задать скидку на товар в зависимости от...

Данных мало, можно все вытянуть двумя запросами и раскидать как надо уже в памяти.
  • Вопрос задан
  • 296 просмотров
Пригласить эксперта
Ответы на вопрос 2
FuN_ViT
@FuN_ViT
веб-разработчик
Если касательно блока ввода текста новости - то проще всего к модели добавить TypeId, а потом хранить в БД таблицу NewsTypes с настройками в виде сериализованных данных (да хоть json).

Вообще пытаться хранить бизнес-логику с использованием структурных фич/полей БД - то еще веселье. Опыт у меня есть. Приводит это к тормозам и сложностям проверки/тестирования...

Возможно на начальном этапе будет проще описать бизнес логику в коде (отдельный класс), чем бросаться на амбразуру.
Ответ написан
@nozzy
Symfony, Laravel, SQL
NoSQL не рассматриваете?
Можно использовать Postgre и хранить все в JSONB.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы