Skip to main content

Working with attributes

Centra supports adding custom attributes to many types of objects, like Product, Display, Category, Customer, Order, Voucher, etc. You can see the full list here.

Defining attribute types

Before attributes can be added to objects, they have to be defined in the Configuration section of Centra Admin Panel. Click here to find more details about custom attributes configuration.

Before you continue, please remember:

  • Custom attributes are very flexible, but make sure you aren’t implementing a feature which already exists in Centra, like translations, folders, categories, relations, external IDs, etc.
  • Some attributes are defined by plugins, for example Ingrid (order-level), Voyado (order, customer) or Google Merchant (product, variant).

From the Integration API’s perspective, one can always fetch all attribute types like this:

query attributeTypes($filter: AttributeTypeFilter) {
attributeTypes(where: $filter) {
name
description
isMapped
isMulti
objectType
elements {
key
description
kind
isMulti
paramsJSON
}
}
}

Mind the filter, which may be useful if your integration only handles a subset of object types.

warning

Make sure you know the difference between mapped and dynamic attributes, as a proper configuration can be crucial for efficient integrations. A limited, repeatable set of values is usually better represented by a mapped attribute, which is defined once, and then assigned by id. Dynamic attributes should be used for high-cardinality properties assigned to each object individually.

Assigning attributes

Integration API has a few ways of assigning attributes, suitable for different scenarios:

  1. The universal assignAttributes mutation, which can assign an arbitrary number of attributes (mapped and dynamic) to a single object of any type.
  2. The assignAttributesBatch, which can be used to do the same, but for up to 100 objects at once. It’s the most efficient way to update attributes on a lot of objects.
  3. Some create* and update* mutations: Product, ProductVariant, Category, Campaign and Voucher. These are useful if you want to combine attribute updates with other updates to reduce the number of mutations you need to run.
info

Note that in all the methods above, objects can be assigned all attributes and all dynamic element values at once. It’s the first and most important level of batching, which every integration should implement in their calls.

Example: assign attributes to two product variants at once

mutation multiAssignAttrs {
assignAttributesBatch(
input: [
{
objectType: ProductVariant
objectId: 1334
dynamicAttributes: [
{
attributeTypeName: "var_season_code"
attributeElementKey: "text"
attributeElementValue: "SUMMER"
}
]
mappedAttributes: [
{ attributeTypeName: "google_merchant_material", attributeId: 42 }
{ attributeTypeName: "google_merchant_pattern", attributeId: 82 }
]
}
{
objectType: ProductVariant
objectId: 1446
mappedAttributes: [
{ attributeTypeName: "google_merchant_material", attributeId: 43 }
{ attributeTypeName: "google_merchant_pattern", attributeId: 83 }
]
}
]
) {
userErrors {
message
path
}
userWarnings {
message
path
}
}
}

Now, that's efficient!

info

Setting “choice” elements (boolean and select) should be done using their defined value.

Fetching attribute values

It may be surprising that getting attribute values isn’t as easy as setting them, but that’s because elements have different types, and each type has its own fields. Look at these fragments for a universal way of fetching all values.

fragment attributes on ObjectWithAttributes {
attributes {
type {
name
}
description
... on MappedAttribute {
id
name
}
elements {
...attributeElement
}
}
}

fragment attributeElement on AttributeElement {
key
description
kind # specifies a UI element to display
... on AttributeStringElement {
value
}
... on AttributeChoiceElement {
selectedValue
selectedValueName
}
... on AttributeFileElement {
url
}
... on AttributeImageElement {
url
mimeType
width
height
}
}

And now you can use them to enhance your queries:

query singleProduct($myId: String!) {
product(externalId: $myId) {
id
name
status
...attributes

variants {
id
name
...attributes
}

displays(where: { storeId: [1] }) {
id
uri
...attributes
}
}
}

Translations

Every string element in attributes can be translated. And by the way, that’s one reason to prefer mapped over dynamic attributes – you only need to translate them once.

You can discover a list of translatable attribute elements by issuing the following query:

query translatable {
translatableFields {
objectType
fields
}
}

For each objectType you will find its translatable fields, including custom attributes – but only the dynamic ones. They are easy to recognize, as they have a form of attribute.attribute_name.element_key. For example:

{
"objectType": "Display",
"fields": [
"name",
"uri",
"description",
"attributes.prd_meta_title.text"
]
}

This means that you can (and probably should) set translations for them together, in a single call per object (a Display in this example):

mutation translateWithAttrs {
setTranslations(
input: {
language: { id: 2 }
objectType: Display
objectId: 341
translations: [
{ field: "name", value: "Mia bela robo" }
{ field: "uri", value: "bela-robo" }
{ field: "description", value: null } # delete this one
{ field: "attributes.prd_meta_title.text", value: "Ĉiuj envios vin" }
]
}
) {
userErrors {
message
path
}
userWarnings {
message
path
}
translatedObject {
... on Display {
name
uri
}
translations(where: { languageId: 2 }) {
fields {
field
value
}
}
}
}
}

Mapped attributes are entities with their own IDs; their translatable fields are returned together like this, under one MappedAttribute object type:

{
"objectType": "MappedAttribute",
"fields": [
"category_text.name",
"google_merchant_product_group.text",
"google_merchant_product_type.text",
"sh_custom_badge.name",
"sh_custom_badge.text_color",
"wash_instructions_general.desc"
]
}

In order to translate a mapped attribute, you would pick its fields from this list, and update them like so:

mutation translateMappedAttribute {
setTranslations(
input: {
language: { id: 2 }
objectType: MappedAttribute
objectId: 16 # this one is a "sh_custom_badge" type
translations: [
{ field: "sh_custom_badge.name", value: "Ruĝa" }
{ field: "sh_custom_badge.text_color", value: "#D43C20" }
]
}
) {
userErrors {
message
path
}
userWarnings {
message
path
}
translatedObject {
... on MappedAttribute {
id
}
translations(where: { languageId: 2 }) {
fields {
field
value
}
}
}
}
}