I would like to prevent a user to create two identical items in a structure field. My structure field has a simple text field.
I’ve made a function to check if a multidimensional array has two identical values:
function search_identical_value( $array , $field ) {
$flag = false;
foreach ( $array as $current_key => $current_array ) {
foreach ( $array as $search_key => $search_array ) {
if ( $search_array[$field] == $current_array[$field]) {
if ( $search_key != $current_key ) {
$flag = true;
break;
}
}
}
if ( $flag ) {
break;
}
}
return $flag;
}
This function works well and return true if two identical items has been found. I put this function in the plugin folder to be accessible from the config file.
This is my blueprint
fields:
questions:
label: Questions or instructions
help: Start your question or instruction with a numbering
type: structure
fields:
questionsLabel:
label: Question
type: textarea
default: '1- ... (Start your question or instruction with a numbering)'
required: true
To check and prevent two identical items in my structure field, I put this in my config file:
return [
'debug' => true,
'hooks' => [
'page.update:after' => function ( $newPage, $oldPage ) {
$questions = array();
$questions = $newPage->questions()->yaml();
if ( search_identical_value( $questions , 'questionslabel' ) ) :
throw new Exception('Check your questions, you cannot create two identical questions.');
endif;
},
]
];
The Exception pop-up if I create two identical items but when I delete the second item from the structure field, the save orange bar disappears and the two items stay in the txt file. So if I reload my panel page, the two items are visible.
Any idea how I can avoid this?
You can test with this starter kit > go to the about page, the structure field is below the textarea. (delete the account folder to reinstall a new admin user)
Hm, yes, you are right, neither option works as expected.
And why don’t you remove duplicate items from the array instead of throwing an error message?
The problem is that throwing the error actually prevents the action from being executed. So the duplicate entry is not stored and so if you remove the item again, there is no difference to before, so no orange bar (unless there are more changes than a single added duplicate item).
With your default entry to make it very likely that duplicate entries happen in the first place.
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
<?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.
@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.
'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
'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.