Prevent a user to create two identical items in a structure field

I use the Exception pop-up because I want to inform the user that he/she has created two identical items and he/she can’t do this.

Not sure to understand, why the two identical items are written in the txt file if the action is not executed?

Sorry, I was talking nonsense.

:wink:

Yes, the changes are saved but the orange bar doesn’t disappear after clicking ok in the pop-up. So I assume the content store and what is actually stored in the file are out of sync at this time.

So as you suggested above, the solution would be to delete the duplicate value in the array and update my structure field with the new array state, then throw the error to inform the user :thinking:

I will try to do some test …

Or tell the user in the help text that this is what will happen without throwing the error.

On a side note, you could simplify your logic:

<?php 

$questions = A::pluck(page('about')->questions()->yaml(), 'questionslabel');
$unique = array_unique($questions);

if (count($questions) !== count($unique)) {
  echo "There are duplicate entries";
}
?>

Which has the additional advantage that you can store $unique as your new value.

But what if your users use different numbers but the same questionlabel? Wouldn’t it make more sense to leave the numbers out and only add them in the template. Or if they by mistake use the same number for a different questionlabel? Then your check will find that they are different but they will still come out wrong on the frontend.

Thanks Sonja, it’s an interesting shorten solution.

I ask for the numbering because there was a bug with the sortBy options, but I think the last version has fixed this bug, so maybe I don’t need to ask the user to numbering there questions anymore.

Did you manage to sort this out? I’m current looking for a similar solution and wonder if you have any code you can share?

@texnixe I’m trying to implement a similar function to the panel by following the function you wrote about here: Structure field update
But I can’t get i to work.

My function looks like this:

function addToStructure($pageURI, $field, $data = array()) {
  kirby()->impersonate('kirby');
  $fieldData = page($pageURI)->$field()->yaml();
  $fieldData[] = $data;
  $fieldData = yaml::encode($fieldData);
  try {
    page($pageURI)->update(array(
      $field => $fieldData
      )
    );
    return true;
  } catch(Exception $e) {
    return $e->getMessage();
  }
}

And my hook like this:

'page.update:before' => function ($page, $values, $strings) {

    if ($page == page('tag-manager')):
      $globalTags = A::pluck($page->global_tags()->yaml(), 'tag');
      $unique = array_unique($globalTags);

      if (count($globalTags) !== count($unique)):
        // There are duplicate entries
        addToStructure($pageURI = 'tag-manager', $field = 'global_tags', $data = $unique);
      endif;
    endif;
  }

And the blueprint with the structure field looks like this:

tags_section:
  type: fields
  fields:
    global_tags:
      label: Globala taggar
      type: structure
      sortBy: tag asc
      fields:
        tag:
          label: Tag
          type: text

If I enter two identical values to the structure field from the panel and hit save, the data is just saved, and the duplicates are still there. I’m using Kirby v3.2.3

What am I missing?

@felixbridell I’ll look into that later, don’t have time now.

You are so kind. Thank you.

I got i working if I run the function from a page but still not from the panel via the hook.

I changed the function to this:

function addToStructure($pageURI, $field, $data = array()) {
  kirby()->impersonate('kirby');
  $fieldData = yaml::encode($data);

  try {
    page($pageURI)->update(array(
      $field => $fieldData
      )
    );
    return true;
  } catch(Exception $e) {
    return $e->getMessage();
  }
}

And the hook to this:

'page.update:before' => function ($page, $values, $strings) {

    if ($page == page('tag-manager')):
      $globalTags = A::pluck($page->global_tags()->yaml(), 'tag');
      $unique = array_values(array_unique($globalTags));

      if (count($globalTags) !== count($unique)):
        // There are duplicate entries
        $uniqueTags = [];
        foreach ($unique as $tag) {
          $uniqueTags[] = [
            'tag' => $tag
          ];
        }
        
        addToStructure($pageURI = 'tag-manager', $field = 'global_tags', $data = $unique);
      endif;
    endif;
  }

I think the problem is that you are trying to do this from a before hook?

If I change

'page.update:before' => function ($page, $values, $strings) {

to

'page.update:after' => function ($page, $values, $strings) {

I get this error from panel:
Exception: ArgumentCountError
Too few arguments to function Kirby\Cms\App::{closure}(), 2 passed and exactly 3 expected.

And If I change to:

'page.update:after' => function ($page) {

Nothing happen on save…

This works


    'hooks' => [
        'page.update:after' => function ($page) {

            if ($page == page('tag-manager')):
              $globalTags = A::pluck($page->global_tags()->yaml(), 'tag');
              $unique = array_values(array_unique($globalTags));
        
              if (count($globalTags) !== count($unique)):
                // There are duplicate entries
                $uniqueTags = [];
                foreach ($unique as $tag) {
                  $uniqueTags[] = [
                    'tag' => $tag
                  ];
                }
                
                addToStructure('tag-manager', 'global_tags', $uniqueTags);
              endif;
            endif;
          },
    ]

Note that I changed the array you passed to the addToStructure() method.
But note the changes the hook makes will only be visible in the Panel if you reload the page.

Thank you! But I only get it to work if I remove the first if state:

if ($page == page('tag-manager')):

How can I be sure that this only run when that page is updated? I realize that I send the tag-manager page to the function so no other page should be affected, but still. feels better to only run this when the page tag-panager is saved.

Maybe the path to the tag-manager page is not correct? But assuming that this page has a unique blueprint, you could change your if statement like this:

if ($page->intendedTemplate()->name() === 'tag-manager') :

Did not work, but this one did:

if ($page->uri() === 'tag-manager'):

Strange.

Sorry… My bad.

if ($page->intendedTemplate()->name() === 'tag-manager') :

did of course work. Just needed to change to the right template name :wink:

if ($page->intendedTemplate()->name() === 'tagmanager') :
1 Like