Generate a random unique ID/hash for a structure field entry

Is there a way to do this?

I’m currently using AutoID to generate a unique hash to many pages across the content structure. Some of these posts have structure items that need a unique ID so I can store it somewhere else and, later, link to that post and specific structure item.

If this is a no-no, well, I’ll have to change from structure to regular pages with an additional autoID for each page. :slight_smile:

Although this could be achieved with hooks, I can’t think of a practical way for finding structure items by id. You would need to know in which page they are located anyway.

What exactly are you trying to do? From what I understood, I’d use subpages.

I’m developing a small educational web-app with some scheduling functions.

All Lectures (pages) have Individual Challenges (pages), like this:

Each challenge page has different Schedule Blocks (structure field items):

Once you add that block to your calendar, the website creates a new page in your very own calendar folder with the unique Challenge ID and the basic info of the block (date, time and location) through the array position of the structure field item. It’s all stored in a unique folder to each user that can be displayed in a calendar section like this:

You can go back to that challenge with no problems if you tap on that item. The problem I’m facing right now is that only the parent challenge ID is safe from changes. If the admin edits or deletes an individual schedule block then the Calendar will not work as expected since I’m only storing an array position that might be able to change.

After giving it a thought I’ve decided to switch to individual subpages for the timetable blocks, although, to be honest, the structure field looked and felt way more natural for this because of the visual relation to timetables:

Anyway, suggestions are really appreciated. :slight_smile:

I agree, the structure field does indeed give you more context that subpages. I think you could in fact use a hidden field to store a unique ID, you would only have to be careful not to overwrite an entry when updating the page. As long as you know the page where to find the structure entry, that shouldn’t be a problem. Even finding an entry in all structure fields should be doable with some more processing overhead.

1 Like

Since you need to protect challenges from being deleted, it could be easily done with subpages. To get a better a experience on the panel, you could use the Modules plugin + Modules Field plugin. I have used it on a project to easily manage subpages on the panel. It worked great!

With the Modules Field allows you to display more subpage info. For displaying them on the template I didn’t use the $page->modules() method provided by the plugin. I fetched them by template using $page->children()->visible()->filterBy('template', 'module.event').

This might give you some ideas on how to accomplish what you need. That’s sounds like a very interesting project by the way!

2 Likes

@lukaskleinschmidt is working on a new version of the Modules Field plugin that works with subpages. You may want to take a look and try it out.

1 Like

Thanks for the heads-up Pedro!

I’m working with subpages at the moment, I still have to see how the Modules Field plugins work. I’m a bit confused at the different structures (mind if you share that little example you showed me on your previous message? hah :grin:). I’ll check those plugins in the following days.

Gonna mark this as Solved but will come back with any insights I find as I finish this project.

Hi !

Do you have an idea how to do that ? I mean, generate an unique ID for a blueprint field. I think of something similar to the AutoID, but not related to pages, only for generate unique hash.

Thanks !

Let’s assume you had a simple structure field like this in your blueprint:

structurefield:
  label: Structure Field
  type: Structure
  style: table
  fields:
      hash_id:
        label: Readonly field for hash
        type: text
        readonly: true
      field2:
        label: Some content
        type: text

Then you could do something like this:

<?php
function generateHash() {
  // code from AutoID plugin
  $elements[] = microtime();
  $elements[] = session_id();
  // Concatenate Elements
  $idString = implode('', $elements);
  // Build Hash
  $idHash = md5($idString);
  return $idHash;
}

// callback function
function addId(&$item, $key, $hashField) {
  if($item == '' && $key == $hashField) {
      $item  = generateHash();
  }
}

// function that updates page
function addHashToStructure($page, $structureField, $hashField) {
  $entries = $page->$structureField()->yaml();
  $data = [];
  foreach($entries as $entry) {
    array_walk($entry, 'addId', $hashField);
    $data[] = $entry;
  }

  $data = yaml::encode($data);

  try {
    $page->update(
      [$structureField => $data]
    );

  } catch(Exception $e) {
    return $e->getMessage();
  }
}

You would use this code inside a panel.page.update hook.

kirby()->hook(['panel.page.update'], function($page) {
  if(/* Some condition to  check if the page is the page with the structure field */) {
   // call function with parameters
    addHashToStructure($page, 'structurefield', 'hash_id');
  }
  
});
3 Likes

Perfect, thank you so much ! I played with the PHP uniqueid() function inside my templates, but your solution is much more elegant. It would be nice to make a plugin out of it.

Thanks again :grinning:

EDIT : I guess you made a tiny mistake at this line :
addHashToStructure($page, 'structurefield', 'hash_id');

If you blueprint field is id_hash, it should be :
addHashToStructure($page, 'structurefield', 'id_hash');

Yep, I probably will.

Edit: Yes, thanks, corrected above.

2 Likes

I’ve created a plugin from it: https://github.com/texnixe/kirby-structure-id

Let me know if it works for you.

2 Likes

I just tried it on a project, works like a charm ! Thanks. :grinning:


NOTE : on the README, I found the config part a bit confusing :

c::set('structure.id.data', [
  'home' => ['addresses'],
  'projects/(:any)' => ['field1', 'field2']
]);

Maybe add some comments :

c::set('structure.id.data', [
/* 'home' is the page, 'addresses' is the structure field with hash id */
  'home' => ['addresses'],
 /* target any subpage of projects, with 'field 1' and 'field 2' as structure fields */
  'projects/(:any)' => ['field1', 'field2']
]);

But maybe it will add confusion, I don’t know… Perhaps saying that your code is just an example is enough.

1 Like

Hi. I’d like to give this plugin a go. I’m running Kirby 3.5.3 at the moment. I have a page named ‘locations’ and within that page I have a structure field named ‘locations’. The following is what I have in my config. This is correct. Right?

<?php
return [
  'structure.id.data' => [
    'locations' => ['locations']
  ]
];

@thewebprojects This is an old plugin for Kirby 2, it won’t work with Kirby 3. If you need IDs for structure field, check out @bnomei’s AutoID plugin. which also works for structure fields.

I see. Yes. I believe I will have to give @bnomei 's plugin a go. I kinda like how your plugin was only for structure fields and was quite lightweight. Thanks

Wonder if I could pull it off with a page model that loops through the structure field and drops in uniqid() a into a readonly field. Wait. Guess that won’t work with someone editing the page.

It wouldn’t be too hard to update the old plugin, feel free to do so.