Custom Fields
Create custom field types for specialized data.
Field Structure​
Custom fields are built on the schema.Field struct:
type Field struct {
Name string
Type FieldType
DBType string
Required bool
Validators []Validator
}
Creating a Custom Field​
Basic Custom Field​
package fields
import (
"github.com/forgego/forge/schema"
)
type IPAddressField struct {
name string
required bool
}
func IPAddress(name string) *IPAddressField {
return &IPAddressField{
name: name,
}
}
func (f *IPAddressField) Required() *IPAddressField {
f.required = true
return f
}
func (f *IPAddressField) Build() schema.Field {
opts := []schema.FieldOpt{}
if f.required {
opts = append(opts, schema.Required())
}
field := schema.StringField(f.name, opts...)
field.DBType = "INET"
field.Validators = append(field.Validators, IPAddressValidator{required: f.required})
return field
}
type IPAddressValidator struct {
required bool
}
func (v IPAddressValidator) Validate(value interface{}) error {
if value == nil {
if v.required {
return errors.New("IP address is required")
}
return nil
}
ip, ok := value.(string)
if !ok {
return errors.New("expected string")
}
if net.ParseIP(ip) == nil {
return errors.New("invalid IP address")
}
return nil
}
Using Custom Field​
func (Server) Fields() []schema.Field {
return []schema.Field{
schema.Int64Field("id", schema.Primary(), schema.AutoIncrement()),
schema.StringField("name", schema.Required()),
fields.IPAddress("ip_address").Required().Build(),
fields.IPAddress("gateway").Build(),
}
}
Advanced Custom Field​
JSON Field with Validation​
type JSONSchemaField struct {
name string
required bool
schema map[string]interface{}
}
func JSONSchema(name string, schema map[string]interface{}) *JSONSchemaField {
return &JSONSchemaField{
name: name,
schema: schema,
}
}
func (f *JSONSchemaField) Required() *JSONSchemaField {
f.required = true
return f
}
func (f *JSONSchemaField) Build() schema.Field {
opts := []schema.FieldOpt{}
if f.required {
opts = append(opts, schema.Required())
}
field := schema.JSONField(f.name, opts...)
field.Validators = append(field.Validators, JSONSchemaValidator{
required: f.required,
schema: f.schema,
})
return field
}
type JSONSchemaValidator struct {
required bool
schema map[string]interface{}
}
func (v JSONSchemaValidator) Validate(value interface{}) error {
if value == nil {
if v.required {
return errors.New("field is required")
}
return nil
}
// Validate against JSON schema
data, _ := json.Marshal(value)
if err := validateJSONSchema(data, v.schema); err != nil {
return err
}
return nil
}
Field Options​
Add options to your custom field:
type IPAddressField struct {
name string
required bool
version int // IPv4 or IPv6
}
func (f *IPAddressField) IPv4() *IPAddressField {
f.version = 4
return f
}
func (f *IPAddressField) IPv6() *IPAddressField {
f.version = 6
return f
}
Best Practices​
- Validate Input - Always validate field values
- Handle NULL - Properly handle NULL values
- Use Appropriate SQL Types - Choose the right SQL type
- Document Fields - Document your custom fields
- Test Fields - Write tests for custom fields
See Also​
- Fields Reference - Built-in field types
- Models Guide - Using fields in models