| name | panel-go-relationship |
| description | Implement database relationships for Panel.go with BelongsTo, HasMany, HasOne, BelongsToMany, and polymorphic relationships. Use when adding relationships between entities or configuring eager loading. See panel-go-plugin-scaffold for comprehensive examples with --with-example flag. Use when this capability is needed. |
| metadata | {"author":"ferdiunal"} |
Panel.go Relationship Expert
Expert in implementing database relationships for Panel.go with BelongsTo, HasMany, HasOne, BelongsToMany, and polymorphic relationships.
Expertise
- BelongsTo: One-to-one, child to parent (Post belongs to User)
- HasOne: One-to-one, parent to child (User has one Profile)
- HasMany: One-to-many (User has many Posts)
- BelongsToMany: Many-to-many (Post belongs to many Tags)
- MorphTo: Polymorphic relationships (Comment belongs to Commentable)
- MorphToMany: Polymorphic many-to-many (Tag belongs to many Taggables)
- Eager Loading: Preload, Joins to prevent N+1
- Entity.go Pattern: All entities in one file to prevent circular dependency
- Registry Pattern: Resource access without direct imports
Quick Patterns
BelongsTo
type Post struct {
ID uint
UserID uint
User *User `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
}
fields.BelongsTo("User", "user_id", resources.GetOrPanic("users")).
DisplayUsing("name").
WithSearchableColumns("name", "email").
WithEagerLoad()
HasMany
type User struct {
ID uint
Posts []Post `gorm:"foreignKey:UserID"`
}
fields.HasMany("Posts", "posts", resources.GetOrPanic("posts")).
ForeignKey("user_id").
OwnerKey("id").
WithEagerLoad()
HasOne
type Organization struct {
ID uint
BillingInfo *BillingInfo `gorm:"foreignKey:OrganizationID"`
}
type BillingInfo struct {
ID uint
OrganizationID uint `gorm:"not null;unique;index"`
Organization *Organization
}
fields.HasOne("BillingInfo", "billing_info", resources.GetOrPanic("billing-info")).
ForeignKey("organization_id").
OwnerKey("id").
WithEagerLoad()
BelongsToMany
type Post struct {
ID uint
Tags []Tag `gorm:"many2many:post_tags;"`
}
type Tag struct {
ID uint
Posts []Post `gorm:"many2many:post_tags;"`
}
fields.BelongsToMany("Tags", "tags", resources.GetOrPanic("tags")).
PivotTable("post_tags").
WithEagerLoad()
MorphTo (Polymorphic)
type Comment struct {
ID uint
CommentableID uint `gorm:"index:idx_commentable"`
CommentableType string `gorm:"index:idx_commentable"`
Content string
}
MorphToMany (Polymorphic Many-to-Many)
type Tag struct {
ID uint
Name string
}
Eager Loading
func (r *PostResource) With() []string {
return []string{"User", "Category", "Tags"}
}
Entity.go Pattern
Tüm entity'leri tek dosyada tut (circular dependency önlenir):
package entity
type Organization struct {
ID uint
Addresses []Address
BillingInfo *BillingInfo
}
type Address struct {
ID uint
OrganizationID uint
Organization *Organization
}
type BillingInfo struct {
ID uint
OrganizationID uint `gorm:"unique"`
Organization *Organization
}
Registry Pattern
Resource'lar birbirini import etmeden erişir:
var registry = make(map[string]func() resource.Resource)
func Register(slug string, factory func() resource.Resource) {
registry[slug] = factory
resource.Register(slug, factory())
}
func GetOrPanic(slug string) resource.Resource {
if r := Get(slug); r != nil {
return r
}
panic("resource not found: " + slug)
}
fields.HasMany("Addresses", "addresses", resources.GetOrPanic("addresses"))
Key Rules
- Always index foreign keys
- Use Preload to prevent N+1 queries
- Define cascade rules (CASCADE, RESTRICT, SET NULL)
- Set DisplayKey for relationships
- Add With() method for eager loading
- Use entity.go pattern for all entities
- Use registry pattern for resource access
Usage
When user asks to add relationships:
- Add foreign key to entity in
entity/entity.go
- Add relationship field with GORM tags
- Add relationship field to field resolver using
resources.GetOrPanic()
- Set DisplayUsing, WithSearchableColumns, WithEagerLoad
- Add eager loading in With() method
Comprehensive Examples
For comprehensive examples with all relationship types:
panel plugin create example --with-example
This creates 9 entities and 7 resources demonstrating:
- BelongsTo, HasMany, HasOne, BelongsToMany
- MorphTo, MorphToMany (entity level)
- Entity.go pattern
- Registry pattern
See panel-go-plugin-scaffold skill for details.
Anti-Patterns
❌ Don't forget foreign key indexes - Always index foreign keys
❌ Don't skip eager loading - Use With() to prevent N+1 queries
❌ Don't forget DisplayUsing - Set DisplayUsing for relationship fields
❌ Don't skip cascade rules - Define OnUpdate and OnDelete behavior
❌ Don't use wrong field types - BelongsTo for belongs_to, HasMany for has_many, etc.
❌ Don't separate entities - Use entity.go pattern to prevent circular dependency
❌ Don't import resources directly - Use registry pattern
Sharp Edges
⚠️ N+1 Queries: Always use WithEagerLoad() for relationships
⚠️ Cascade Deletes: Be careful with CASCADE on delete
⚠️ Many-to-Many: Requires junction table (many2many tag)
⚠️ DisplayUsing: Must match actual field name in related entity
⚠️ Polymorphic: MorphTo/MorphToMany fields not yet implemented in panel.go
⚠️ Circular Dependency: Use entity.go pattern to prevent import cycles
⚠️ Registry Timing: init() functions run on import, order matters
Converted and distributed by TomeVault — claim your Tome and manage your conversions.