mirror of
https://github.com/foomo/gocontentful.git
synced 2025-10-16 12:25:39 +00:00
feat: add GetOrInheritFieldValue function for hierarchical field retrieval
This commit is contained in:
parent
fb5ecfe7da
commit
7c5f3787dd
@ -6,6 +6,56 @@ Another thing you might want to know is the content type of an entry with a give
|
||||
(cc *ContentfulClient) GetContentTypeOfID(ctx, ID string) (contentType string)
|
||||
```
|
||||
|
||||
## Field inheritance
|
||||
|
||||
When working with hierarchical content structures, you may need to retrieve field values that can be inherited from parent entries. The `GetOrInheritFieldValue` function provides this functionality:
|
||||
|
||||
```go
|
||||
func GetOrInheritFieldValue(ctx context.Context, contentfulShop *ContentfulClient, entryID string, field string, parentContentTypes []string, locale Locale) (any, error)
|
||||
```
|
||||
|
||||
This function retrieves a field value from a GenericEntry, traversing up the full parent hierarchy if the field is not found in the current entry. It's particularly useful for content types that have parent-child relationships where certain fields should be inherited from parent entries.
|
||||
|
||||
### Parameters
|
||||
|
||||
- `ctx`: Context for the operation
|
||||
- `contentfulShop`: The ContentfulClient instance
|
||||
- `entryID`: The ID of the entry to start the search from
|
||||
- `field`: The field name to retrieve
|
||||
- `parentContentTypes`: List of content type IDs that should be considered as valid parents
|
||||
- `locale`: The locale to retrieve the field value in
|
||||
|
||||
### Return value
|
||||
|
||||
Returns the field value as `any` type, or an error if:
|
||||
- The entry cannot be retrieved
|
||||
- A circular reference is detected in the parent hierarchy
|
||||
- The field is not found in any parent entry
|
||||
- Any parent entry cannot be retrieved
|
||||
|
||||
### Example usage
|
||||
|
||||
```go
|
||||
ctx := context.Background()
|
||||
parentContentTypes := []string{"category", "section"}
|
||||
|
||||
// Try to get the "theme" field from the current entry or inherit it from parents
|
||||
theme, err := GetOrInheritFieldValue(ctx, cc, "my-entry-id", "theme", parentContentTypes, contentful.DefaultLocale)
|
||||
if err != nil {
|
||||
log.Printf("Theme not found: %v", err)
|
||||
} else {
|
||||
log.Printf("Theme value: %v", theme)
|
||||
}
|
||||
```
|
||||
|
||||
### Important notes
|
||||
|
||||
- The function first attempts to get the field value from the current entry
|
||||
- If not found, it recursively traverses up the parent hierarchy
|
||||
- Circular references are detected and will return an error to prevent infinite loops
|
||||
- The function respects the locale parameter for localized field values
|
||||
- Only entries with content types specified in `parentContentTypes` are considered as valid parents
|
||||
|
||||
## Caveats and limitations
|
||||
|
||||
- Avoid creating content types that have field IDs equal to reserved Go words (e.g. "type").
|
||||
|
||||
@ -568,7 +568,52 @@ func (ref EntryReference) GetParents(ctx context.Context, contentType ...string)
|
||||
}
|
||||
return candidateParents, nil
|
||||
}
|
||||
return nil, errors.New("GetParents: reference VO and CC are both nil")
|
||||
}
|
||||
|
||||
func GetOrInheritFieldValue(ctx context.Context, contentfulShop *ContentfulClient, entryID string, field string, parentContentTypes []string, locale Locale) (any, error) {
|
||||
entry, err := contentfulShop.GetGenericEntry(entryID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get entry %s: %v", entryID, err)
|
||||
}
|
||||
|
||||
// Try to get the field value from the current entry first
|
||||
val, err := entry.FieldAsAny(field, locale)
|
||||
if err == nil {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
return inheritFieldValueRecursive(ctx, contentfulShop, entry, field, parentContentTypes, locale, make(map[string]bool))
|
||||
}
|
||||
|
||||
func inheritFieldValueRecursive(ctx context.Context, contentfulShop *ContentfulClient, entry *GenericEntry, field string, parentContentTypes []string, locale Locale, visited map[string]bool) (any, error) {
|
||||
if visited[entry.Sys.ID] {
|
||||
return nil, fmt.Errorf("circular reference detected for entry %s", entry.Sys.ID)
|
||||
}
|
||||
visited[entry.Sys.ID] = true
|
||||
|
||||
parentRefs, err := commonGetParents(ctx, entry.CC, entry.Sys.ID, parentContentTypes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get parents for entry %s: %v", entry.Sys.ID, err)
|
||||
}
|
||||
|
||||
for _, parentRef := range parentRefs {
|
||||
parentEntry, err := entry.CC.GetGenericEntry(parentRef.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get parent entry %s: %v", parentRef.ID, err)
|
||||
}
|
||||
|
||||
val, err := parentEntry.FieldAsAny(field, locale)
|
||||
if err == nil {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
inheritedVal, err := inheritFieldValueRecursive(ctx, contentfulShop, parentEntry, field, parentContentTypes, locale, visited)
|
||||
if err == nil {
|
||||
return inheritedVal, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrNotSet
|
||||
}
|
||||
|
||||
func HtmlToRichText(htmlSrc string) *RichTextNode {
|
||||
|
||||
@ -597,7 +597,52 @@ func (ref EntryReference) GetParents(ctx context.Context, contentType ...string)
|
||||
}
|
||||
return candidateParents, nil
|
||||
}
|
||||
return nil, errors.New("GetParents: reference VO and CC are both nil")
|
||||
}
|
||||
|
||||
func GetOrInheritFieldValue(ctx context.Context, contentfulShop *ContentfulClient, entryID string, field string, parentContentTypes []string, locale Locale) (any, error) {
|
||||
entry, err := contentfulShop.GetGenericEntry(entryID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get entry %s: %v", entryID, err)
|
||||
}
|
||||
|
||||
// Try to get the field value from the current entry first
|
||||
val, err := entry.FieldAsAny(field, locale)
|
||||
if err == nil {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
return inheritFieldValueRecursive(ctx, contentfulShop, entry, field, parentContentTypes, locale, make(map[string]bool))
|
||||
}
|
||||
|
||||
func inheritFieldValueRecursive(ctx context.Context, contentfulShop *ContentfulClient, entry *GenericEntry, field string, parentContentTypes []string, locale Locale, visited map[string]bool) (any, error) {
|
||||
if visited[entry.Sys.ID] {
|
||||
return nil, fmt.Errorf("circular reference detected for entry %s", entry.Sys.ID)
|
||||
}
|
||||
visited[entry.Sys.ID] = true
|
||||
|
||||
parentRefs, err := commonGetParents(ctx, entry.CC, entry.Sys.ID, parentContentTypes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get parents for entry %s: %v", entry.Sys.ID, err)
|
||||
}
|
||||
|
||||
for _, parentRef := range parentRefs {
|
||||
parentEntry, err := entry.CC.GetGenericEntry(parentRef.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get parent entry %s: %v", parentRef.ID, err)
|
||||
}
|
||||
|
||||
val, err := parentEntry.FieldAsAny(field, locale)
|
||||
if err == nil {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
inheritedVal, err := inheritFieldValueRecursive(ctx, contentfulShop, parentEntry, field, parentContentTypes, locale, visited)
|
||||
if err == nil {
|
||||
return inheritedVal, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrNotSet
|
||||
}
|
||||
|
||||
func HtmlToRichText(htmlSrc string) *RichTextNode {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user