Связи между полями
Пример:
_hierarchy.define(Vector<Field>({
Field::Text("name", MinLength(3)),
Field::Integer("id", Flags::Indexed),
Field::Object("root", _hierarchy, Linkage::Manual, ForeignLink("sections")),
Field::Set("sections", _hierarchy, Linkage::Manual, ForeignLink("root")),
Field::Set("all_pages", _pages)
}));
База данных предполагает, что схемы данных будут связаны друг с другом и взаимно контролироваться. Это реализовано с помощью полей типа Object и Set.
По силе связи, их можно разделить на:
- Сильные:
RemovePolicy::Cascade
,RemovePolicy::Restrict
- объект может существовать и быть удалён только вместе с другим объектом. Оба объекта имеют какого-то вида ссылки друг на друга (двусторонняя связь). - Слабые:
RemovePolicy::Null
- объект может существовать отдельно от связи, в случае удаления связь обнуляется. Связь двусторонняя. - Внешние сильные - односторонняя связь, при которой удаление связанного объекта ведёт к удалению связанных с ним (
RemovePolicy::StrongReference
). - Внешние слабые - односторонняя связь, при которой удаление объекта ничего не меняет (
RemovePolicy::Reference
). По отношению, связи можно разделить на: - один к одному (поля
Object
), - один ко многим (связанные поля
Object
иSet
, полеSet
c внешними ссылками), - многие ко многим.
Мы не рекомендуем использовать последний вид связи (многие ко многим) и не реализуем такую связь автоматически (но пользователь сам может реализовать её через промежуточную таблицу), поскольку это ведёт к множеству ошибок. Чаще всего, такую связь имеет смысл заменить отражениями (View
) или другими высокоуровневыми подходами.
Для простых связей SDK находит связанные поля автоматически по взаимным ссылкам на схемы, однако, это невозможно, когда ссылка ведёт на свою же схему. В таком случае, ссылки можно определить явно по имени поля в целевой схеме.
За способ реализации связей отвечает непосредственно интерфейс БД. Для PostgreSQL они реализуются с помощью внешних ключей, и, в некоторых случаях (например, для внешней сильной связи), с помощью дополнительных таблиц и триггеров.
Связи высокого уровня
_auto.define(Vector<Field>({
Field::View("pages", _pages, ViewFn([] (const Scheme &, const Value &obj) -> bool {
return obj.getBool("hidden") ? false : true;
}), Vector<String>({ "hidden" })),
Field::Text("data", AutoFieldDef{
Vector<AutoFieldScheme>({
AutoFieldScheme{ _auto, {"text", "key"} }
}),
DefaultFn([this] (const Value &data) -> Value {
// content generation
}),
Vector<String>({"text", "key"}),
}),
}));
В ряде случаев стандартной связи полей недостаточно, необходимо наложить на эту связь некие условия, либо связать объекты нестандартным способом. Для таких случаев предназначены связи высокого уровня: отражения (View
) и автоматические поля связей (AutoFieldDef
).
Ключевой момент для этих полей - возможность установить связь. Для случая отражения, это может быть сделано автоматически, по аналогии с полем типов Set
или Object
. Однако, в общем случае это невозможно, потому необходима функция линковки (ViewLinkageFn
). Эта функция, вызванная для объекта подчинённой схемы, должна определить идентификатор основной схемы. Функция вызывается, когда отражение или автоматическое поле должно быть обновлено.
Далее, автоматические поля и отражения действуют различным образом.
Отражение является списком объектов другой схемы, связанным с текущим объектом по некоторому условию. Это условие определяется параметром-функцией ViewFn. SDK проверяет, соответствует ли объект условию после того, как схема получила результат модифицирующего запроса. Пользователь может обозначить список полей, требуемых для этой функции, аналогично тому, как это делается для несвязных автоматических полей. Если состояние соответствия объекта изменилось, то есть, объект стал соответствовать или перестал соответствовать отражению, об этом информируется адаптер и операция на обновление отражения добавляется в асинхронную очередь.
Автоматическое связанное поле — это поле, имеющее свой характерный тип (не выделенный тип, как View
), однако, вычисляется автоматически в зависимости от значений других объектов. Для того, чтобы оно было обновлено, система должна обновить одно из указанных в списке requiresForAuto
полей.