Accessors

Alumio uses accessors in transformers to focus the configuration on the data itself, not on the way it is structured.

Examples of data structures

An entity can have the following structure:

{
"sku": "tnt",
"attributes": {
"color": "red",
"fuse-length": "12cm"
}
}

Accessor:

{
"accessor": {
"prototype": "key",
"parameters": {
"root": "attributes"
}
}
}

An entity can also have a structure like this:

{
"sku": "tnt",
"attributes": [
{
"name": "color",
"value": "red"
},
{
"name": "fuse-length",
"value": "12cm"
}
]
}

Accessor:

{
"accessor": {
"prototype": "structure",
"parameters": {
"key": "name",
"root": "attributes",
"value": "value"
}
}
}

In both examples the result of the accessor is:

{
"color": "red",
"fuse-length": "12cm"
}

Accessing nested nodes

Accessors may use paths and patterns to access deeply nested data. Paths and patterns are delimited by a . (dot).

Entity:

{
"media_gallery_entries": [
{
"file": "foo.jpg",
"content_type": "image/jpeg"
},
{
"file": "bar.png",
"content_type": "image/png"
}
]
}

Accessor:

{
"accessor": {
"prototype": "pattern",
"parameters": {
"pattern": "media_gallery_entries.*.file"
}
}
}

Result:

[
"foo.jpg",
"bar.png"
]

Types

The following are accessors available by default.

If required, !! a custom accessor !! can be created to work with exotic data structure.

Key accessor

Type: key

Property Type Required Description
root string No The path to the node to access. When omitted, the root node will be used.

Entity:

{
"sku": "tnt",
"attributes": {
"color": "red",
"fuse-length": "12cm"
}
}

Accessor:

{
"accessor": {
"prototype": "key",
"parameters": {
"root": "attributes"
}
}
}

Result:

{
"color": "red",
"fuse-length": "12cm"
}

Structure accessor

Type: structure

Property Type Required Description
root string No The path to the node to access. When omitted, the root node will be used.
key string Yes The node to use as key.
value string Yes The node to use as value.

Entity:

{
"sku": "tnt",
"attributes": [
{
"name": "color",
"value": "red"
},
{
"name": "fuse-length",
"value": "12cm"
}
]
}

Accessor:

{
"accessor": {
"prototype": "structure",
"parameters": {
"key": "name",
"root": "attributes",
"value": "value"
}
}
}

Result:

{
"color": "red",
"fuse-length": "12cm"
}

Pattern accessor

Type: pattern

Property Type Required Description
pattern string No The pattern to the node(s) to access. When omitted, * will be used.

A pattern will be interpreted as in Mediact\DataContainer\DataContainerInterface::glob.

Entity:

{
"media_gallery_entries": [
{
"file": "foo.jpg",
"content_type": "image/jpeg"
},
{
"file": "bar.png",
"content_type": "image/png"
}
]
}

Accessor:

{
"accessor": {
"prototype": "pattern",
"parameters": {
"pattern": "media_gallery_entries.*.file"
}
}
}

Result:

[
"foo.jpg",
"bar.png"
]

Children accessor

Type: children

Property Type Required Description
accessor object Yes The accessor to apply on child nodes.
pattern string No The pattern to the node(s) path to read. When omitted, * will be used.
replacement string No The replacement to the node(s) path to read. When omitted, $1 will be used.

A pattern will be interpreted as in Mediact\DataContainer\DataContainerInterface::glob.

The replacement will be interpreted as in Mediact\DataContainer\DataContainerInterface::expand. Note that the replacement is only used when reading data with the accessor and disregarded when writing data using the accessor.

The children accessor is best used to distribute data from a parent entity to child entities, or to gather data from child entities for summaries in parent entities.

Entity:

{
"sku": "tnt-1000",
"name": "TNT",
"children": [
{
"sku": "tnt-1001",
"color": "red",
"price": 12.34
},
{
"sku": "tnt-1002",
"color": "blue",
"price": 13.50
},
{
"sku": "tnt-1003",
"color": null,
"price": 10.00
}
]
}

Transformer:

{
"prototype": "data",
"parameters": {
"transformers": [
{
"prototype": "pattern-copy",
"parameters": {
"pattern": "sku",
"replacement": "parent"
}
},
{
"prototype": "accessor-move",
"parameters": {
"source": {
"prototype": "key",
"parameters": {
"keys": [
"parent"
]
}
},
"destination": {
"prototype": "children",
"parameters": {
"pattern": "children.*",
"accessor": {
"prototype": "key"
}
}
}
}
},
{
"prototype": "accessor-copy",
"parameters": {
"source": {
"prototype": "key",
"parameters": {
"keys": [
"name"
]
}
},
"destination": {
"prototype": "children",
"parameters": {
"pattern": "children.*",
"accessor": {
"prototype": "key",
"parameters": {
"root": "name"
}
}
}
}
}
},
{
"prototype": "pattern-copy",
"parameters": {
"pattern": "children.*.color",
"replacement": "children.$1.name.color"
}
},
{
"prototype": "value-mapper",
"parameters": {
"mappers": [
{
"prototype": "standard",
"parameters": {
"to": "default",
"from": "~"
}
}
],
"accessor": {
"prototype": "pattern",
"parameters": {
"pattern": "children.*.name.color"
}
}
}
},
{
"prototype": "value-mapper",
"parameters": {
"mappers": [
{
"prototype": "list-implode",
"parameters": {
"glue": " - "
}
}
],
"accessor": {
"prototype": "pattern",
"parameters": {
"pattern": "children.*.name"
}
}
}
},
{
"prototype": "accessor-copy",
"parameters": {
"source": {
"prototype": "children",
"parameters": {
"pattern": "children.*",
"accessor": {
"prototype": "key",
"parameters": {
"keys": [
"price"
]
}
},
"replacement": "$1"
}
},
"destination": {
"prototype": "key"
}
}
},
{
"prototype": "value-mapper",
"parameters": {
"mappers": [
{
"prototype": "operator",
"parameters": {
"operator": "min"
}
}
],
"accessor": {
"prototype": "key",
"parameters": {
"keys": [
"price"
]
}
}
}
}
]
}
}

Result:

{
"sku": "tnt-1000",
"name": "TNT",
"children": [
{
"sku": "tnt-1001",
"color": "red",
"price": 12.34,
"parent": "tnt-1000",
"name": "TNT - red"
},
{
"sku": "tnt-1002",
"color": "blue",
"price": 13.5,
"parent": "tnt-1000",
"name": "TNT - blue"
},
{
"sku": "tnt-1003",
"color": null,
"price": 10,
"parent": "tnt-1000",
"name": "TNT - default"
}
],
"price": 10
}

The following transformations have been applied:

  1. All children now have an attribute parent, which is the same as the top level attribute sku.
  2. All children now have an attribute name, which is the same as the name of the parent, followed by -, followed by their corresponding color attribute. Whenever the color attribute was set to null, the last part of the name regresses to the value default.
  3. The price attribute of all children has been gathered. The lowest of those prices has been set as the price attribute of the parent entity.

Fragments

Accessors can be configured to only return fragments of the matched data. This is achieved by combining the accessor configuration with the keys property.

Given the following entity:

{
"sku": "tnt",
"attributes": {
"color": "red",
"fuse-length": "12cm",
"timer": "digital"
}
}

And the following accessor:

{
"accessor": {
"prototype": "key",
"parameters": {
"root": "attributes"
}
}
}

The result of the accessor is:

{
"color": "red",
"fuse-length": "12cm",
"timer": "digital"
}

Now, when the fuse-length attribute should not be present in the result, the following configuration will ensure only the expected data will be used:

{
"accessor": {
"prototype": "key",
"parameters": {
"keys": [
"color",
"timer"
],
"root": "attributes"
}
}
}

The result of the accessor becomes:

{
"color": "red",
"timer": "digital"
}

The keys attribute can be added to any type of accessor. Using the following entity:

{
"sku": "tnt",
"attributes": [
{
"name": "color",
"value": "red"
},
{
"name": "fuse-length",
"value": "12cm"
},
{
"name": "timer",
"value": "digital"
}
]
}

Accessor:

{
"accessor": {
"prototype": "structure",
"parameters": {
"key": "name",
"keys": [
"color",
"timer"
],
"root": "attributes",
"value": "value"
}
}
}

The result of the accessor becomes:

{
"color": "red",
"timer": "digital"
}