Migrating some data

In back end I have ‘authors’ and ‘contributors’ as pages:

Inside there are authors or contributors, depending which page in backend is opened:

And this is a single author or contributor:

Also in every post there is this field in which you assign authors and contributors:

It is possible for authors and contributors to assign them from existing authors or contributors:
Screenshot 2023-08-26 at 18.34.24

My client wants to migrate author to contributor(because that member left the team), which is not a problem. I just create one inside contributors and delete one in authors. But the problem is they have to go through all of the posts in which there was that author assigned as an author, remove it from the authors and add it to contributors. There are 75 posts.

Is it possible to create a script which would loop through all articles inside content/articles/ find article.txt file and inside that file replace some information?

article.txt file with field where you assign contributors and authors to the post looks like this:

----

Metacredits:

- 
  role: Autorė
  member: ""
  collaborator: ""
  external: Name Surname
- 
  role: Bendraautoris, vertėjas ir fotografas
  member: ""
  collaborator: collaborators/name-surname#1
  external: ""
- 
  role: Redaktorė
  member: authors/name-surname#2
  collaborator: ""
  external: ""
- 
  role: Kalbos redaktorė
  member: ""
  collaborator: collaborators/name-surname#3
  external: ""

----

member field is for Author and collaborator field is for Contributors. So i would need to to look for name-surname and replace line:
member: authors/name-surname
with:
member: ""
and
collaborator: ""
with
collaborator: collaborators/name-surname

Is this even possible? Where should i put this script? Is there something i am missing? Maybe there are other ways to solve this problem?

Thank you for any help!

Yes, definitely.

You can put it in the root of your project, or run it from a route. See examples of how to require the Kirby bootstrap file here: How to migrate file metadata | Kirby CMS

You would have have to convert your structure field to an array, find the wrong entry and remove it, add the new entry and then store the structure field again using $page->update().

currently this is my code inside root folder file update-credits.php:

<?php
require __DIR__ . '/kirby/bootstrap.php';

$kirby = new Kirby;

// authenticate as almighty
$kirby->impersonate('kirby');


$metacreditsArray = [];

$children = page('articles-lt')->children();

try {
    foreach ($children as $child) {
        if ($child->hasField('metacredits')) {
            $metacreditsArray[] = $child->metacredits()->toBuilderBlocks()->toArray();
        } 
    }

    //THIS IS JUST VISUAL REPRESENTATION
    echo '<br>';
    echo 'OLD ARRAY';
    echo '<br>';
    foreach($metacreditsArray as $index => $credit) {
        echo '<br>';
        $child = $children->nth($index);
        echo 'Page Title: ' . $child->title() . '<br>';
        foreach ($credit as &$c) {
            echo '<br>';
            print_r($c);
            echo '<br>';
        }
    }

    // Now, $metacreditsArray will contain the 'metacredits' structures from all child pages as an array
    foreach ($metacreditsArray as &$metacredits) { 
        foreach ($metacredits as &$credit) {
            // Split the 'member' field into an array
            $members = explode(', ', $credit['member']); 
            // Split the 'collaborator' field into an array
            $collaborators = explode(', ', $credit['collaborator']);
            
            // Check if 'authors/name-one' exists in members
            $keyToRemoveMember = array_search('authors/name-one', $members);
            if ($keyToRemoveMember !== false) {
                // Remove 'authors/name-one' from members
                unset($members[$keyToRemoveMember]);
                
                // Add 'collaborators/name-one' to collaborators
                if (!in_array('collaborators/name-one', $collaborators)) {
                    $collaborators[] = 'collaborators/name-one';
                }
            }
            
            // Rebuild the 'member' and 'collaborator' fields
            $credit['member'] = implode(', ', $members);
            $credit['collaborator'] = implode(', ', $collaborators);
        }
    }

    foreach ($children as $index => $child) {
        if (isset($metacreditsArray[$index])) {
            // Update the 'metacredits' field of the page
            $child->update(['metacredits' => $metacreditsArray[$index]]);
            // Save the page to persist the changes
            $child->save();
        }
    }

    echo '<br>';
    echo 'The meta info has been updated';
} catch (Exception $e) {
    echo 'An error occurred: ' . $e->getMessage();
}



//THIS IS JUST VISUAL REPRESENTATION
echo '<br>';
echo '<br>';
echo 'NEW ARRAY';
echo '<br>';
foreach($metacreditsArray as $index => $credit) {
    echo '<br>';
    
    $child = $children->nth($index);
    echo 'Page Title: ' . $child->title() . '<br>';

    foreach ($credit as &$c) {
        echo '<br>';
        print_r($c);
        echo '<br>';
    }
}

Inside ‘article1’ it correctly removes ‘authors/name-one’ from ‘member’ field and adds to ‘collaborator’ field ‘collaborators/name-one’:

I just dont understand what is happening with last array of ‘article2’ after update?

Also no data has changed inside panel and front end. How do i push this updated data to .txt file so it changes inside panel?

Sorry, I cannot quite follow what you are doing there. Also, I thought it was a structure field, not an old builder field.

But why don’t you do it on a per-article basis instead of collecting all data into a single array?

And while you are at it, you might want to first filter your articles by those that have this old author in the structure/builder field

I updated my code and it looks like it’s working on my local machine:

<?php
require __DIR__ . '/kirby/bootstrap.php';

$kirby = new Kirby;

// authenticate as almighty
$kirby->impersonate('kirby');



$children = page('articles-lt')->children();

try {
    foreach ($children as $child) {
        // check if the current article has a 'metacredits' field
        if ($child->hasField('metacredits')) {
            // get the current 'metacredits' data for this article
            $metacredits = $child->metacredits()->toStructure()->toArray();

            // loop through each 'metacredits' entry for this article
            foreach ($metacredits as &$credit) {
                // split the 'member' field into an array
                $members = explode(', ', $credit['member']);
                // split the 'collaborator' field into an array
                $collaborators = explode(', ', $credit['collaborator']);
                
                // check if 'authors/name-one' exists in members
                $keyToRemoveMember = array_search('authors/name-one', $members);
                if ($keyToRemoveMember !== false) {
                    // remove 'authors/name-one' from members
                    unset($members[$keyToRemoveMember]);
                    
                    // add 'collaborators/name-one' to collaborators
                    if (!in_array('collaborators/name-one-1', $collaborators)) {
                        $collaborators[] = 'collaborators/name-one-1';
                    }
                    
                    // rebuild the 'member' and 'collaborator' fields
                    $credit['member'] = implode(', ', $members);
                    $credit['collaborator'] = implode(', ', $collaborators);
                }
            }

            // update the 'metacredits' field for this article with the modified data
            $child->update(['metacredits' => $metacredits]);
            $child->save(); // save the article to persist the changes
        }
    }
    echo 'The meta info has been updated';
} catch (Exception $e) {
    echo 'An error occurred: ' . $e->getMessage();
}


?>

gonna test it further and will implement it the next day. Thank you very much for hints!