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)
|
(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
|
## Caveats and limitations
|
||||||
|
|
||||||
- Avoid creating content types that have field IDs equal to reserved Go words (e.g. "type").
|
- 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 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 {
|
func HtmlToRichText(htmlSrc string) *RichTextNode {
|
||||||
|
|||||||
@ -597,7 +597,52 @@ func (ref EntryReference) GetParents(ctx context.Context, contentType ...string)
|
|||||||
}
|
}
|
||||||
return candidateParents, nil
|
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 {
|
func HtmlToRichText(htmlSrc string) *RichTextNode {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user