How to save Repeater Set data from API?

Hi There!

I’m new in Cockpit, I’d like to create a simple shopping cart, I can save simple field but having trouble to save Repeater-Set field type.
This field is simply a repeater with fields Quantity (number) and Product ( collectionlink )

{
  "fields": [
    {
      "type": "set",
      "label": "Products",
      "options": {
        "fields": [
          {
            "type": "text",
            "name": "qty",
            "label": "Qty",
            "options": {
              "type": "number"
            }
          },
          {
            "type": "collectionlink",
            "name": "product",
            "label": "Product",
            "options": {
              "link": "product",
              "display": "title",
              "multiple": false,
              "limit": false
            }
          }
        ]
      }
    }
  ]
}

I’m trying to call the API with below data:

{
	"data": {
	    ....
	    "productlist": [
	        {
	            "qty": 10,
	            "product": {
	                "_id": "5d2a1ad86336354f540000e1"
	            }
	        },
	        {
	            "qty": 20,
	            "product": {
	                "_id": "5d2a1ad86336354f540000e1"
	            }
	        }
	    ],
	    ....
	}
}

The data is saved but it seems incorrect, I can’t see it from admin page, the product display name is not displayed correctly.

Kindly advice on how to save this kind of data.

Thank you

Repeaters are complicated… They store all field definitions for each entry, so you have to send them with your api requests, too. They are more for the ui usage.

If you want to keep the structure, create an entry with your admin ui, get the data via api and have a look at the structure. Then build a request with the same structure.

The problem is, that repeaters can have all types of fields in different orders and they need a mechanism to know the field definitions for each value.

This worked for me, but it’s ugly: /api/collections/save/reptest?token=xxtokenxx
json:

{"data":{
  "title":"test",
  "productlist": [
    {
      "field": {
        "type": "set",
        "label": "Products",
        "options": {
          "fields": [
            {
              "type": "text",
              "name": "qty",
              "label": "Qty",
              "options": {
                  "type": "number"
              }
            },
            {
              "type": "collectionlink",
              "name": "product",
              "label": "Product",
              "options": {
                "link": "pages",
                "display": "title",
                "multiple": false,
                "limit": false
              }
            }
          ]
        }
      },
      "value": {
        "qty": 5,
        "product": {
          "_id": "5cbf16e1333862118000010e",
          "link": "pages",
          "display": "page title"
        }
      }
    }
  ]
}}

I would lean back and think about a structure without repeaters, e. g. with multiple collections and collectionlinks.

Or I would built a custom field for my needs. Have a look at the simple-repeater field, that I built a while ago for inspiration: .tag file, usage

If you can’t find a smaller structure, you can build your own api endpoint in /config/bootstrap.php, that adds the field definitions automatically. If you need help with this approach, feel free to ask again.

1 Like

@raffaelj Thank you for the solution!
It is working and you’re right, it’s ugly
:joy:

Finally, I’m using a simpler structure, just repeater, it still has unnecessary field “VALUE” but It’s acceptable.

this is the request body example:

{
	"data": {
	    ...
	    "productlist": [
            {
                "value": {
                    "qty": 1,
                    "product": {
                        "_id": "5d2a1ad86336354f540000e1",
                        "link": "product",
                        "display": "Product1"
                    }
                }
            },
            {
                "value": {
                    "qty": 2,
                    "product": {
                        "_id": "5d2a1ad86336354f540000e1",
                        "link": "product",
                        "display": "Product1"
                    }
                }
            }
        ],
	    ...
	}
}

By the way, how to change the repeater display label for individual item?

I’m looking the code for repeater, there is Label or Display field, but where do I put it in the API request?

{
  "fields": [
    {
      "type": "text",
      "label": "Text",
      "display": "$value"
    },
    {
      "type": "text",
      "label": "Quantity",
      "display": "$value"
    }
  ]
}

cp_repeater_display_value

edit: It only works for new items, because the existing ones don’t have the "display":"$value" setting.

2 Likes

Hi @raffaelj, thanks for your quick answer.

I’m trying your solution, but it still not working.

this is my field definistion for product lis ( a repeater )

{
  "field": {
    "type": "set",
    "label": "Products",
    "options": {
      "fields": [
        {
          "type": "text",
          "name": "qty",
          "label": "Qty",
          "display": "$value",
          "options": {
            "type": "number"
          }
        },
        {
          "type": "collectionlink",
          "name": "product",
          "label": "Product",
          "display": "$value",
          "options": {
            "link": "product",
            "multiple": false,
            "limit": false
          }
        }
      ]
    }
  }
} 

after changing the sturcture, I add new entries from cockpit, save it and refresh, but the label still the same

any thoughts about this ??

Ah, I see. This is complicated again.

The repeater has an option to display it’s values if you define "display":"$value" for the set field.

The set field has a templating mechanism, that doesn’t support nested keys, yet.

So this would work:

{
  "field": {
    "type": "set",
    "label": "Products",
    "display": "$value",
    "options": {
      "display":"{qty} x {product}",
      "fields": [
        {
          "type": "text",
          "name": "qty",
          "label": "Qty",
          "options": {
            "type": "number"
          }
        },
        {
          "type": "text",
          "name": "product",
          "label": "Product"
        }
      ]
    }
  }
}

But you have a collection link field and your products title is stored in the nested key product.display. I don’t see a way to display that with the current setup, sorry.

But as always - there are multiple ways to achieve the goal.

  1. Use a third field inside set for displaying in the admin ui and send/change the product title manually.
{
  "field": {
    "type": "set",
    "label": "Products",
    "display": "$value",
    "options": {
      "display":"{qty} x {producttitle}",
      "fields": [
        {
          "type": "text",
          "name": "qty",
          "label": "Qty",
          "options": {
            "type": "number"
          }
        },
        {
          "type": "text",
          "name": "producttitle",
          "label": "Product title"
        },
        {
          "type": "collectionlink",
          "name": "product",
          "label": "Product",
          "display": "$value",
          "options": {
            "link": "product",
            "multiple": false,
            "limit": false
          }
        }
      ]
    }
  }
}

You can set the producttitle automatically in the background with something like this in /config/bootstrap.php (not tested):

$app->on('collections.save.before.products', function($name, &$entry, $isUpdate) {
    $entry['productlist']['value']['producttitle'] = $entry['productlist']['value']['product']['display'];
});
  1. Write a custom field for your special needs. Here is an example:
  1. Write a custom renderer for the set field or for your custom field. Example:
1 Like

Wow you’re the best :+1:
I got it working with the first solution.

Thank you for your support :pray: