How to Programatically Add Entries to Structure Field

Hi all!

What is the easiest, most straight-forward way of programatically adding entries to a structure field? Do I have to parse and rewrite the contents of the entire page every time I want to add a new item, or is there an easier way to do it?

Since you can only update a field, not parts of a field, there is no change compared to how we did that in Kirby 2, I think. I.e. get the contents of the field as an array, add new item, store again.

This is what I do in cases like this:

$states = $page->states()->yaml();

$states[] = [
  'abbr' => 'SP',
  'name' => 'São Paulo',
  'capital' => 'São Paulo'
];

$page->update([
  'states' => Yaml::encode($states)
]);

It would be cool to have a field method to simplify this process:

$page->states()->addToStructure([
  'abbr' => 'SP',
  'name' => 'São Paulo',
  'capital' => 'São Paulo'
]);

EDIT: Since I need this one for myself a lot, here you go:

<?php // site/plugins/site/index.php

use Kirby\Cms\Field;

Kirby::plugin('site/methods', [
  'fieldMethods' => [
    'addToStructure' => function (Field $field, array $entry): void {
      $value = $field->yaml();

      if (isset($entry[0]) && is_array($entry[0])) {
        array_push($value, ...$entry);
      } else {
        array_push($value, $entry);
      }
      
      $field->parent()->update([
        $field->key() => Yaml::encode($value)
      ]);
    }
  ]
]);

EDIT 2: Just updated the method to allow adding multiple entries at once:

$page->states()->addToStructure([
  [ 'name' => 'Goiás' ],
  [ 'name' => 'São Paulo' ],
]);
6 Likes

Thank you both - that’s what I was thinking. Like @pedroborges, I was hoping for an addToStructure() method - I think I’m getting lazier and lazier…

@pedroborges beat me to it.

Note that you need to be authenticated (either logged in or as almighty) to be able to update the page.

I’d also wrap the update method in a try-catch block and emit an error message.

@texnixe, how do we impersonate the superuser, so we can do things like updating the page via code?

EDIT: I think I found it in another forum post:

kirby()->impersonate('kirby')

@luxlogica Yep, that’s it. We now call this “authenticate as almighty”:sunglasses:

2 Likes

I tried to use this, got a peculiar error, I may be making a silly mistake and can’t see it.

This is the exception I am getting:

TypeError

Argument 1 passed to Kirby\Cms\App::{closure}() must be an instance of Field, instance of Kirby\Cms\Field given, called in /home/jaume/public_html/dev/dietz/kirby/src/Cms/Field.php on line 79

plugins/field-methods/index.php :

<?php
Kirby::plugin('jaume/field-methods', [
	'fieldMethods' => [
		'addToStructure' => function (Field $field, array $entry): void {
			$value = $field->yaml();

			if (isset($entry[0]) && is_array($entry[0])) {
				array_push($value, ...$entry);
			} else {
				array_push($value, $entry);
			}
			
			$field->parent()->update([
				$field->key() => Yaml::encode($value)
			]);
		}			
	]
]);

I am testing it on a template, like this:

if (count($giftCards) > 0) {
	echo 'go!';
	try {
		// Auth
		kirby()->impersonate('kirby');

		$data;
		foreach ($giftCards as $gift) {
			$data = [
				'code' => createCode(),
				'order' => $page->id(),
				'price' => $gift['sum'],
				'spent' => 00.00,
				'total' => $gift['sum'],
				'status' => true,
			];
		}

		$site->giftcardhistory()->addToStructure($data);
	} catch (Exception $e) {
		echo $e->getMessage();
	}
}

This is the relevant structure field’s yaml:

  giftcards:
    label: Gift Cards
    icon: box
    sections:
      giftcardhistory:
        type: fields
        fields:
          giftcardhistory:
            label: Gift Cards History
            type: structure
            fields:
              code:
                type: text
              order: 
                type: pages
              price:
                type: number 
              spent:
                type: number
              total:
                type: number 
              status:
                type: toggle

What’s wrong?

Thank you

Remove the Field type hint or change to Kirby\Cms\Field or add a use statement at the top of the file.

1 Like

Thank you, it works. What would the use statement include here?

What is the difference between Field and Kirby\Cms\Field ?

Thanks

There is no class Field, or rather the full namespace is Kirby\Cms\Field.

use Kirby\Cms\Field;
1 Like

Ah I see, the namespace is used then it is possible to use the Field class like that, otherwise it is not found anywhere, just like @pedroborges was actually doing.

Understood, thank you