Structure linking issues for duplicated pages

In my panel, I am allowing the creation of pages along with their respective subpages. Within the parent pages, I have a structure field that enables the selection of subpages from all the existing ones. This approach is essential for sorting and displaying the subpages according to the specific preferences of my client.

Recently, the client expressed a need to duplicate these parent pages in order to create private ā€œhidden versionsā€ with minor content modifications, meant for internal sharing purposes.

On duplicating, new versions of the pages and its subpages are generated, resulting in new slugs as desired. However, an issue arises with the structure field of these new duplicates, as the links in the structure field still point to the original subpages instead of the duplicated ones, which I assume is logical from a technical standpoint.

Iā€™m seeking a solution or alternative that would allow the new structure links to point to the newly duplicated subpages.

If there is a straightforward approach or any suggestions on this topic, I would greatly appreciate it!

Maybe use a page.duplicate:after hook to replace the link in the structure when the page is duplicated.

Thanks for the answer.
So far, I managed to access the field in question in the structure that needs to be updated.
What I am not sure about is how to alter it so it reflects the new duplicated page url structure:

// Update structure field on duplicate
'page.duplicate:after' => function ($duplicatePage, $originalPage) {
    $projects = $duplicatePage->projects()->toStructure()->toArray(); 

    foreach ($projects as &$project) {
        $project['project'] = '' // Do something here
    }

    $duplicatePage->update([
        'projects' => $projects,
    ]);
},

So letā€™s say the original structure field value points to:
../pages/projects+project-one+subproject

It needs to point to:
../pages/projects+project-one-copy+subproject

Any idea on how can I do that dynamically so it updates on each of the items in the structure field?

Thanks

A bit more of progress, but now the values show empty.

// Update structure field on duplicate
'page.duplicate:after' => function ($duplicatePage, $originalPage) {
    $projects = $duplicatePage->projects()->toStructure()->toArray(); // Get the projects structure field

    foreach ($projects as &$project) {
        $originalProjectName = $project['project']; // Get the original project name
        $duplicatedProjectPage = $duplicatePage->children()->find($originalProjectName); // Find the corresponding project page in the duplicated page
        $project['project'] = $duplicatedProjectPage->id(); // Assign the new project page object

    }

    $duplicatePage->update([
        'projects' => $projects, // Update the projects structure field
    ]);
},

Could you please post the yaml for your structure field? It seems to have a project field, but what exactly is stored there?

On a side note, when updating the duplicate page, you have to yaml encode the array.

Please find below the yaml for the ā€˜projectā€™ field structure ā€“ which is the field I need to update on duplicating the page. The structure field in question is so extense that I prefered to simply share the important content related to this issue.

        fields:
          projects:
            label: Projects
            type: structure
            empty: No projects yet
            fields:
              project:
                label: Project
                type: pages
                query: page.children.filterBy('template', 'in', ['default'])
                multiple: false
                subpages: false
                ...

You didnā€™t answer my second question: And what does the project subfield store? The id or the UUID (donā€™t know what Kirby version you are using and if you are using UUIDs or not)

And here, you have to store the id as array

$project['project'] = [$duplicatedProjectPage->id()];

Sorry, I thought I answered by sharing the yaml code.
I am using Kirby 3.5.4 and by ā€˜what does the project subfield storeā€™ what do you mean exactly?

Hereā€™s the updated code with the yaml encoding and the store of the id as array.

// Update structure field on duplicate
'page.duplicate:after' => function ($duplicatePage, $originalPage) {
    $projects = $duplicatePage->projects()->toStructure()->toArray(); // Get the projects structure field

    foreach ($projects as &$project) {
        $originalProjectName = $project['project']; // Get the original project name
        $duplicatedProjectPage = $duplicatePage->children()->find($originalProjectName); // Find the corresponding project page in the duplicated page
        $project['project'] = [$duplicatedProjectPage->id()]; // Assign the new project page object

    }
    $duplicatePage->update([
        'projects' => Yaml::encode($projects),
    ]);
}, 

If I, for example, do something like $project['project'] = $duplicatePage->find('standard-project')->id(); to test, that would work, and turn each of the project values to point to that specific project.

The value that ends up in the content file for the selected page. With that old version of Kirby, definitely not UUIDs yet, so should be the page id.

That is how the content txt file would store it. Do you mean this?

  project:
    - projects/project-one/standard-project

Yes, thatā€™s what I meant. If you now use this value to find the page in the newly generated $duplicate page,

this will not work. You will have to use the last part of this id (standard-project) to find the child page.

Oh, okay!
Iā€™ve tried basename($project['project'])but I get ā€˜Argument #1 ($path) must be of type string, array givenā€™ error. Do you know how I could achieve it? Itā€™s getting pretty close now.

Whatā€™s Iā€™d to

$duplicatedProjectPage = ($page = page($project['project'])) ? $duplicatePage->children()->find($page->slug()) : null;

Ok, thanks. I really appreciate the time you put helping me out!
Unfortunately, I get ā€˜Call to a member function id() on nullā€™ now after trying your snippet.

I think you need to debug this a bit to check where itā€™s failing, I cannot do this remotely.

If $duplicatedProjectPage is null, then of course the subsequent call to $duplicatedProjectPage->id() will fail.

I think this is the problem, because the value contains the preceding dash. Maybe simply try and trim it

page(ltrim($project['project'], '- '))

Would be easier if we had a field object here, but since the structure is simply an array, itā€™s a bit more complicated.