[SOLVED] Backend-manageable data-records

Hi there,
I am searching for a simple solution to manage data records in a central place of Neos. For example, an address, product-descriptions or -prices, which can be used as data in various places, but can still be changed / maintained at a central location.

The data records have to be create-able and maintainable by editors (manageable in the backend -> not in settings.yaml)!

I’ve worked out a cumbersome solution by myself. Unfortunately, it is not very practically.

Does anyone know a simple and crisp solution?
Is there anything for this challenge in Neos (out of the box)?

Hello Martin,

in general you have to choices:

  1. Use custom-documents that represent your data … recommended in 90% of the use cases
  2. Use custom Flow-Domain Objects and create a backend module

Quite often the first option is a good way. It enables out of the box to reference the managed items from your other nodes and with flow-query you can easily access the properties of those items. A problem that occurs in that case is that you sometimes end-up with dozens of nodes on a single level in the navigate component which is good not ui-wise but can be mitigated by clustering the items in a structure of collection nodes. An important benefit of this approach is that you automatically have the same dimensions as your site.

To help editors you can consider the following constraints:

  1. Allow the itemCollection only on root level and inside other itemCollections
  2. Allow itemDocuments only inside of itemCollections

If the document approach is not feasible writing a custom domain model and a backend module is not really difficult. You can look at the neos-backend modules for examples. On the rendering side you will have to create extra code to reference those items or to read them in your site but it is all well solvable.

Regards Martin

PS: There are other approaches that combine domain-models and cr-nodes or that store cr-nodes outside of your site but check out the other two ways first.

Hi @mficzel,
thank you for your help.

  1. sounds easier to implement. But I have problems to understand the Terms and Conceptuality.
    Maybe your «custom-documents» are close to my «cumbersome solution»?

My solution is a custom DocumentType:

'Vendor.Site:DataContainerPage':
  superTypes:
    'TYPO3.Neos:Document': true
   childNodes:
    record:
      type: 'TYPO3.Neos:ContentCollection'

####Would this correspondent to your custom-document?

This created data-set (as document) I can choose somewhere else with my nodeType «Collector».

'Vendor.Site:Collector':
  superTypes:
       'TYPO3.Neos:Content': true
  properties:
    internalLink:
      ui:
        reloadIfChanged: true
        inspector:
          editor: TYPO3.Neos/Inspector/Editors/ReferenceEditor

####The bigBIG draw-back of this approach: tree-flooding with a lot of small data-junks.

For ReferenceEditor I have to store one record in one DataContainerPage. Otherwise Editor’s can’t select each record for its own :disappointed: with an easy to understand solution (not with copy/paste identifier or something similar).
I tried to wrap the pieces as content in ONE ContentContainer named e.G. «DATA» in root-level (first document of website). But then I can’t choose the data-records/elements with ReferenceEditor. (See in Part ReferenceEditorFeeding.)


###Your 1. Approach could be the solution for …
…but, unfortunately I can’t transform your words in code.
Maybe you cold gave me a small piece of code for better understanding of the 1. solution?

- Is it possible with your 1. Approach to grasp the data-records with something “easy to understand” like inspector’s ReferenceEditor?

Would be great! (also to try your solution, even if this would not be selectable with ReferenceEditor)


ReferenceEditor Feeding:

####- Did someone know a fusion-solution to feed the ReferenceEditor with an Array within «readable-label <–> nodeIdentifier» pairs)?
(Label from nodeType property «dataRecordLabel» in children-«record(s)-nodeTypes within «DATA»-Wrapper-page)

With this “ArrayFeeding” of ReferenceEditor, it would be easy to select the desired record and also to use this one-WrapperDocument in the tree.

Yes, the thing i called “Custom Document” is indeed a “Custom DocumentType” as you correctly understood. Sorry for beeing not really specific here.

The things i see from your example code is that you manage companies and some records for each company. Additional you want to reference the companies in your seite.

That would lead me to something like this: BEWARE OF UNTESTED AND INCOMPLETE CODE

# a collection of companies that can contain either more collections or companies   
# by creating this collections you can cluster the records to avoid spamming the 
# node tree on a single level too much     
'Vendor.Site:Document.CompanyCollection':
  superTypes:
    'TYPO3.Neos:Document': true
  constraints:
    childNodes:
      nodeTypes: 
         # the following line is overwriting a rule that is inherited from neos 
         'TYPO3.Neos:Document': false 
         'Vendor.Site:Document.CompanyCollection': true
         'Vendor.Site:Document.Company': true

# company records that can be linked in your site       
'Vendor.Site:Document.Company':
  # the following line specifies how the label in the sidebar and select boxes is rendered
  label: "${String.cropAtWord(q(node).property('name') + ' ' + q(node).property('legalForm') , 100, '...')}"
  superTypes:
    'TYPO3.Neos:Document': true
  childNodes: 
    records: 
      type: 'TYPO3.Neos.ContentCollection'  
      constraints:
        nodeTypes:
          'Vendor.Site:Content.CompanyRecord': true
          '*': false
  properties:
    ...    
    
# a data record for a company 
'Vendor.Site:Content.CompanyRecord':
  superTypes:
    'TYPO3.Neos:Content': true
  properties:
    ...      

Please note that this will only work if you have to reference the single company and not one of their records.

1 Like

Thank you @mficzel,
have change some points to use it independently of Company or so…

Maybe some others can use it as boilerplate:
##Working Solution:

# root.yaml or indluded Data.yaml

# Collection of data, no data in childNode -> only for less messy nodeTree (also simple nestable).
'Vendor.Site:DataCollection':
  superTypes:
    'TYPO3.Neos:Document': TRUE
  label: '${(q(node).property(''title'') ? q(node).property(''title'') : '''') + ''[DataCollection'' +  (q(node).property(''nodeDescription'') ? '' ('' + q(node).property(''nodeDescription'') + '')'' : '''' ) + '']''}'
  ui:
    label: Data-Collection
    icon: icon-database
    group: yourGroup
    inlineEditable: true
    position: 200
  childNodes:
    no-data:
      type: 'TYPO3.Neos:ContentCollection'
      constraints:
        nodeTypes:
          '*': FALSE
  constraints:
    nodeTypes:
      'TYPO3.Neos:Document': FALSE
      'Vendor.Site:DataCollection': TRUE
      'Vendor.Site:DataRecord': TRUE
  properties:
    _hiddenInIndex:
      type: boolean
      defaultValue: TRUE

#Record for store some Data and use it at multiple place. Only Text for simpler use. Easy to use with TYPO3.Neos/Inspector/Editors/ReferenceEditor
'Vendor.Site:DataRecord':
  superTypes:
    'TYPO3.Neos:Document': TRUE
  label: '${(q(node).property(''title'') ? q(node).property(''title'') : '''') + ''[DataRecord'' +  (q(node).property(''nodeDescription'') ? '' ('' + q(node).property(''nodeDescription'') + '')'' : '''' ) + '']''}'
  ui:
    label: Data-Record
    icon: icon-circle-o-notch
    group: yourGroup
    inlineEditable: true
    position: 210
  childNodes:
    data:
      type: 'TYPO3.Neos:ContentCollection'
      constraints:
        nodeTypes:
          '*': FALSE
          'TYPO3.Neos.NodeTypes:Text': TRUE

TemplatePath and content definition in root.ts2

# root.ts2

#Data Object template 
root.dataCollection {
    condition = ${q(node).is('[instanceof Vendor.Site:DataCollection]')}
    renderPath = 'dataCollection'
}

dataCollection = Vendor.Site:DefaultPage {
    body{
        templatePath = 'resource://Vendor.Site/Private/Templates/Page/Data.html'
        content = ContentCollection {
            attributes.class.@process.collectionClass >
            nodePath = 'no-data'
        }
    }
}

root.dataRecord {
    condition = ${q(node).is('[instanceof Vendor.Site:DataRecord]')}
    renderPath = '/dataRecord'
}

dataRecord = Vendor.Site:DefaultPage {
    body{
        templatePath = 'resource://Vendor.Site/Private/Templates/Page/Data.html'
        content = ContentCollection {
            attributes.class.@process.collectionClass >
            nodePath = 'data'
        }
    }
}

Most minimal fluid-template

#Data.html

{content -> f:format.raw()}

Works fine:

Cheers Martin