EntityBase
The foundational class for all entities in the Vyuh Entity System. Every business object managed by the system extends EntityBase.
Class Signature
Properties
| Property | Type | Default | Description |
|---|---|---|---|
id | String | -- | Unique identifier for the entity (read via readFieldValue) |
name | String | '' | Display name (read via readFieldValue, falls back to title) |
Display Getters
| Property | Type | Default | Description |
|---|---|---|---|
displayTitle | String | returns name | Override to provide a custom title |
displaySubtitle | String | returns '' | Override to provide a subtitle |
displayProperties | Map<String, String> | returns {} | Key-value pairs for compact display contexts (mobile rows, related-entity cards, search results) |
Abstract Methods
toJsonMap<String, dynamic> toJson()Serialize the entity to JSON. Every concrete entity must implement this — typically generated by json_serializable with build_runner.
EntityBase extends ContentItem from vyuh_core, inheriting the content-item infrastructure (schemaType, layout, modifiers).
id and name use @JsonKey(readValue: readFieldValue) so they support configurable JSON key names via FieldKeyMap.
Audit, version, draft fields are mixin-based
EntityBase only carries identity (id, name). createdAt / updatedAt / createdBy / updatedBy come from the Auditable mixin. Versioning fields come from Versionable. Draft state comes from Draftable. The vyuh_entity_generator produces a $EntityBase superclass that mixes in the appropriate capabilities with their concrete field declarations.
Overriding Display Getters
class Equipment extends EntityBase {
final String code;
final String location;
@override
String get displayTitle => '$code - $name';
@override
String get displaySubtitle => location;
@override
Map<String, String> get displayProperties => {
'Code': code,
'Location': location,
};
}FieldKey Enum
Defines the standard field names for entity properties — used by readFieldValue and FieldKeyMap to map camelCase Dart fields to configurable JSON keys.
enum FieldKey {
// Identity
id,
name,
title,
description,
// Auditable
createdAt,
updatedAt,
createdBy,
updatedBy,
// Versionable
versionNumber,
isActive,
}FieldKeyMap
A final class static mapper that converts FieldKey values to their JSON/database column name. The default mapping uses snake_case:
| FieldKey | Default JSON Key |
|---|---|
id | 'id' |
name | 'name' |
title | 'title' |
description | 'description' |
createdAt | 'created_at' |
updatedAt | 'updated_at' |
createdBy | 'created_by' |
updatedBy | 'updated_by' |
versionNumber | 'version_number' |
isActive | 'is_active' |
Customizing the Key Map
Override the key map when your backend uses different column names:
FieldKeyMap.keyMap = {
FieldKey.createdAt: 'created_date',
FieldKey.updatedAt: 'modified_date',
};Unmapped keys fall back to the defaults.
Methods
| Method | Signature | Description |
|---|---|---|
key | static String key(FieldKey field) | Returns the JSON key for a field |
keyMap (setter) | static set keyMap(Map<FieldKey, String> keyMap) | Overrides key mappings |
readFieldValue
Top-level helper used as the readValue callback in @JsonKey annotations on entity fields:
Object? readFieldValue(Map<dynamic, dynamic> json, String key);Reads a value by trying:
- The camelCase
keydirectly (json['createdAt']) - The configured snake_case column name via
FieldKeyMap
Special case: name falls back to title for backward compatibility.
Entity Mixins
Mixins provide type-safe access to common properties without relying on JSON conversion or reflection.
Auditable
mixin Auditable on EntityBase {
DateTime? get createdAt;
DateTime? get updatedAt;
String? get createdBy;
String? get updatedBy;
}Members are abstract — for hand-written entities, declare the fields with @override and @JsonKey(readValue: readFieldValue) in the concrete class. The generator emits concrete declarations in the $EntityBase superclass.
HasCode
mixin HasCode {
String get code;
}HasDescription
mixin HasDescription {
String? get description;
}HasActiveStatus
mixin HasActiveStatus {
bool get isActive;
}HasStatus
mixin HasStatus {
String get status;
}CommonEntityProperties
A convenience mixin combining HasCode, HasDescription, and HasActiveStatus:
mixin CommonEntityProperties implements HasCode, HasDescription, HasActiveStatus {}Versionable
Adds version tracking (lives in versioning_audit/models/versioned_entity.dart):
mixin Versionable on EntityBase {
int get versionNumber;
bool get isActive;
}Draftable
Adds draft support to versionable entities (lives in drafts/draftable.dart):
mixin Draftable on Versionable {
DraftMetadata? get draftMetadata;
bool get isEntityDraft;
bool get hasModificationDraft;
bool get showDraftIndicator;
}Extension: DraftStatusAccess
extension DraftStatusAccess on EntityBase {
bool get isDraft;
}Returns true if the entity implements Draftable and showDraftIndicator is true.
Extension: EntityPropertyAccess
extension EntityPropertyAccess<T extends EntityBase> on T {
String? get codeIfSupported;
String? get descriptionIfSupported;
bool? get isActiveIfSupported;
String? get statusIfSupported;
}Safe accessors that return null when the entity does not implement the corresponding mixin.
EntityReference
A lightweight reference to another entity, used for relationships.
@JsonSerializable(createToJson: true)
class EntityReference {
final String id;
final String name;
final String? description;
const EntityReference({
required this.id,
required this.name,
this.description,
});
factory EntityReference.fromJson(Map<String, dynamic> json);
Map<String, dynamic> toJson();
}| Property | Type | Description |
|---|---|---|
id | String | Unique identifier of the referenced entity |
name | String | Display name of the referenced entity |
description | String? | Optional description |
Equality is based on id only — two references to the same entity are equal regardless of name changes.
Usage Example
@JsonSerializable(fieldRename: FieldRename.snake)
class Area extends EntityBase
with Auditable, Versionable, Draftable, HasCode {
@override
final String code;
final String location;
@override
final bool isActive;
@override
final int versionNumber;
// Auditable
@override
@JsonKey(readValue: readFieldValue)
final DateTime? createdAt;
@override
@JsonKey(readValue: readFieldValue)
final DateTime? updatedAt;
@override
@JsonKey(readValue: readFieldValue)
final String? createdBy;
@override
@JsonKey(readValue: readFieldValue)
final String? updatedBy;
// Draftable
@override
DraftMetadata? draftMetadata;
Area({
required super.id,
required super.name,
required this.code,
required this.location,
this.isActive = true,
this.versionNumber = 1,
this.createdAt,
this.updatedAt,
this.createdBy,
this.updatedBy,
this.draftMetadata,
}) : super(
schemaType: 'lms.area',
layout: null,
modifiers: null,
);
factory Area.fromJson(Map<String, dynamic> json) => _$AreaFromJson(json);
@override
Map<String, dynamic> toJson() => _$AreaToJson(this);
@override
String get displayTitle => '$code - $name';
@override
String get displaySubtitle => location;
}In practice, these Auditable / Versionable / Draftable field declarations are emitted by the code generator into a $EntityBase superclass — your hand-written entity only needs to declare its domain-specific fields and mix in the capabilities.
See Also
- EntityConfiguration — Registering entity types
- EntityApi — CRUD operations for entities
- Editors — Create/edit forms for entities