Documentation
¶
Overview ¶
Package customfields implements GoatKit PaaS Core universal custom fields.
Custom fields provide an EAV (Entity-Attribute-Value) store that works across all GoatKit entities. Plugins declare fields at registration; the platform handles storage, validation, UI rendering, and querying.
Index ¶
- Constants
- func IsValidEntityType(et string) bool
- func IsValidFieldType(ft string) bool
- func MigrateLegacyFields(logger *slog.Logger) error
- func StorageColumn(fieldType string) string
- func ValidEntityTypes() []string
- func ValidFieldTypes() []string
- func ValidFilterOperators() []string
- func ValidateFieldDef(f *FieldDef) error
- func ValidateValue(def *FieldDef, val any) error
- func ValidateValues(defs map[string]*FieldDef, values map[string]any) map[string]string
- type Address
- type FieldConfig
- type FieldDef
- type FieldFilter
- type FieldValue
- type LatLng
- type Repository
- func (r *Repository) CreateDef(f *FieldDef, userID int) (int64, error)
- func (r *Repository) GetDef(id int64) (*FieldDef, error)
- func (r *Repository) GetDefByEntityAndName(entityType, name string) (*FieldDef, error)
- func (r *Repository) GetValues(entityType string, objectID int64, fieldNames []string) (map[string]any, error)
- func (r *Repository) HardDeleteDef(id int64) error
- func (r *Repository) ListDefs(entityType, ownerType, ownerName string, activeOnly bool) ([]FieldDef, error)
- func (r *Repository) QueryByFields(entityType string, filters []FieldFilter) ([]int64, error)
- func (r *Repository) SetValues(entityType string, objectID int64, values map[string]any) error
- func (r *Repository) SoftDeleteDef(id int64, userID int) error
- func (r *Repository) UpdateDef(f *FieldDef, userID int) error
- type SelectOption
Constants ¶
const ( EntityTicket = "ticket" EntityArticle = "article" EntityContact = "contact" EntityAgent = "agent" EntityGroup = "group" EntityCustomerGroup = "customer_group" EntityQueue = "queue" EntityOrganisation = "organisation" )
Supported entity types.
const ( FieldText = "text" FieldTextArea = "textarea" FieldInteger = "integer" FieldDecimal = "decimal" FieldBoolean = "boolean" FieldDate = "date" FieldDateTime = "datetime" FieldSelect = "select" FieldMultiSelect = "multi_select" FieldURL = "url" FieldEmail = "email" FieldPhone = "phone" FieldPoint = "point" FieldPolygon = "polygon" FieldAddress = "address" )
Supported field types.
const ( OwnerPlugin = "plugin" OwnerAdmin = "admin" OwnerLegacy = "legacy" )
Owner types.
Variables ¶
This section is empty.
Functions ¶
func IsValidEntityType ¶
IsValidEntityType checks whether the given entity type is supported.
func IsValidFieldType ¶
IsValidFieldType checks whether the given field type is supported.
func MigrateLegacyFields ¶
MigrateLegacyFields copies legacy dynamic_field definitions and values into the gk_custom_field_* tables. This is idempotent and safe to run on every startup. Legacy tables are never modified.
func StorageColumn ¶
StorageColumn returns the primary storage column name for a given field type.
func ValidEntityTypes ¶
func ValidEntityTypes() []string
ValidEntityTypes returns all supported entity type keys.
func ValidFieldTypes ¶
func ValidFieldTypes() []string
ValidFieldTypes returns all supported field type keys.
func ValidFilterOperators ¶
func ValidFilterOperators() []string
ValidFilterOperators returns all supported filter operators.
func ValidateFieldDef ¶
ValidateFieldDef validates a custom field definition before create/update.
func ValidateValue ¶
ValidateValue validates a single value against its field definition. Returns nil if valid.
Types ¶
type Address ¶
type Address struct {
Line1 string `json:"line1,omitempty"`
Line2 string `json:"line2,omitempty"`
City string `json:"city,omitempty"`
Region string `json:"region,omitempty"`
Postcode string `json:"postcode,omitempty"`
Country string `json:"country,omitempty"`
Lat float64 `json:"lat,omitempty"`
Lng float64 `json:"lng,omitempty"`
}
Address represents a structured address stored in val_json for address fields.
type FieldConfig ¶
type FieldConfig struct {
// text / textarea
MaxLength *int `json:"max_length,omitempty"`
Regex string `json:"regex,omitempty"`
RegexError string `json:"regex_error,omitempty"`
// select / multi_select
Options []SelectOption `json:"options,omitempty"`
AllowEmpty bool `json:"allow_empty,omitempty"`
MinSelected *int `json:"min_selected,omitempty"`
MaxSelected *int `json:"max_selected,omitempty"`
// integer / decimal
Min *float64 `json:"min,omitempty"`
Max *float64 `json:"max,omitempty"`
Step *float64 `json:"step,omitempty"`
// date / datetime
MinDate string `json:"min_date,omitempty"`
MaxDate string `json:"max_date,omitempty"`
FutureOnly bool `json:"future_only,omitempty"`
// point
DefaultZoom *int `json:"default_zoom,omitempty"`
DefaultCenter *LatLng `json:"default_center,omitempty"`
// address
Countries []string `json:"countries,omitempty"`
GeocodeProvider string `json:"geocode_provider,omitempty"`
// url
AllowedSchemes []string `json:"allowed_schemes,omitempty"`
// full-text search opt-in
Searchable bool `json:"searchable,omitempty"`
}
FieldConfig holds type-specific configuration stored in the config JSON column. Not all fields apply to all types — each field type uses a subset.
type FieldDef ¶
type FieldDef struct {
ID int64 `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Label string `json:"label" db:"label"`
EntityType string `json:"entity_type" db:"entity_type"`
FieldType string `json:"field_type" db:"field_type"`
OwnerType string `json:"owner_type" db:"owner_type"`
OwnerName *string `json:"owner_name,omitempty" db:"owner_name"`
MigratedFrom *int64 `json:"migrated_from,omitempty" db:"migrated_from"`
Section string `json:"section" db:"section"`
FieldOrder int `json:"field_order" db:"field_order"`
Description *string `json:"description,omitempty" db:"description"`
Placeholder *string `json:"placeholder,omitempty" db:"placeholder"`
Required bool `json:"required" db:"required"`
Config *json.RawMessage `json:"config,omitempty" db:"config"`
ValidID int `json:"valid_id" db:"valid_id"`
CreateTime time.Time `json:"create_time" db:"create_time"`
CreateBy int `json:"create_by" db:"create_by"`
ChangeTime time.Time `json:"change_time" db:"change_time"`
ChangeBy int `json:"change_by" db:"change_by"`
}
FieldDef represents a custom field definition from gk_custom_field_def.
func (*FieldDef) ParsedConfig ¶
func (f *FieldDef) ParsedConfig() (*FieldConfig, error)
ParsedConfig returns the parsed type-specific configuration.
type FieldFilter ¶
type FieldFilter struct {
Field string `json:"field"`
Operator string `json:"operator"` // eq, neq, gt, lt, gte, lte, like, in, between, near
Value any `json:"value"`
Value2 any `json:"value2,omitempty"` // second value for between (upper bound) and near (radius km)
}
FieldFilter is a query filter for CustomFieldsQuery.
type FieldValue ¶
type FieldValue struct {
ID int64 `json:"id" db:"id"`
FieldID int64 `json:"field_id" db:"field_id"`
ObjectID int64 `json:"object_id" db:"object_id"`
ValText *string `json:"val_text,omitempty" db:"val_text"`
ValInt *int64 `json:"val_int,omitempty" db:"val_int"`
ValDecimal *float64 `json:"val_decimal,omitempty" db:"val_decimal"`
ValDecimal2 *float64 `json:"val_decimal2,omitempty" db:"val_decimal2"`
ValDate *time.Time `json:"val_date,omitempty" db:"val_date"`
ValDatetime *time.Time `json:"val_datetime,omitempty" db:"val_datetime"`
ValJSON *json.RawMessage `json:"val_json,omitempty" db:"val_json"`
}
FieldValue represents a stored value from gk_custom_field_value.
type Repository ¶
type Repository struct {
// contains filtered or unexported fields
}
Repository provides CRUD operations for custom field definitions and values.
func NewRepository ¶
func NewRepository() (*Repository, error)
NewRepository creates a new custom fields repository using the global DB connection.
func NewRepositoryWithDB ¶
func NewRepositoryWithDB(db *sql.DB) *Repository
NewRepositoryWithDB creates a new custom fields repository with an explicit DB connection.
func (*Repository) CreateDef ¶
func (r *Repository) CreateDef(f *FieldDef, userID int) (int64, error)
CreateDef inserts a new field definition. Returns the new row ID.
func (*Repository) GetDef ¶
func (r *Repository) GetDef(id int64) (*FieldDef, error)
GetDef retrieves a single field definition by ID.
func (*Repository) GetDefByEntityAndName ¶
func (r *Repository) GetDefByEntityAndName(entityType, name string) (*FieldDef, error)
GetDefByEntityAndName retrieves a field definition by entity type and name.
func (*Repository) GetValues ¶
func (r *Repository) GetValues(entityType string, objectID int64, fieldNames []string) (map[string]any, error)
GetValues retrieves all custom field values for an entity, joined with definitions. If fieldNames is non-empty, only those fields are returned.
func (*Repository) HardDeleteDef ¶
func (r *Repository) HardDeleteDef(id int64) error
HardDeleteDef removes a field definition and all its values permanently.
func (*Repository) ListDefs ¶
func (r *Repository) ListDefs(entityType, ownerType, ownerName string, activeOnly bool) ([]FieldDef, error)
ListDefs retrieves field definitions with optional filters.
func (*Repository) QueryByFields ¶
func (r *Repository) QueryByFields(entityType string, filters []FieldFilter) ([]int64, error)
QueryByFields finds entity object IDs matching the given custom field filters.
func (*Repository) SetValues ¶
SetValues stores custom field values for an entity. values is field_name → value. The caller is responsible for validation.
func (*Repository) SoftDeleteDef ¶
func (r *Repository) SoftDeleteDef(id int64, userID int) error
SoftDeleteDef marks a field as invalid (valid_id=2). Data is preserved.
type SelectOption ¶
SelectOption represents a single option in a select or multi_select field.