bernhard Posted November 15, 2024 Posted November 15, 2024 Hi everyone! Today I had to move some data stored in a repeater field to another field. I searched the forum and found an admin action by @adrian and came up with a new method in rockmigrations that can copy or move repeater items to new fields and/or new pages: https://github.com/baumrock/RockMigrations/blob/ad59425831c74e32034035afa9d2276f398cc2be/RockMigrations.module.php#L3536 Now I realised that there is a problem: The way this works is it creates new items and then removes the old items. But that also means that IDs change 😮 And I have referenced those repeater items from somewhere else. Does anybody have an idea of how to do that?
bernhard Posted November 16, 2024 Author Posted November 16, 2024 I solved this by renaming the field instead of moving content. In this case this was the even better (as more performant) solution, but I'd still be interested if anyone knows another way of doing this! 1
Jonathan Lahijani Posted December 30, 2024 Posted December 30, 2024 I was thinking about this as well recently. Here's what I came up with: function convertRepeaterPageToPage($repeaterPage, $newParent, $newTemplate, $newStatus) { // store for cleanup $forPage = $repeaterPage->getForPage(); $forField = $repeaterPage->getForField(); // convert $repeaterPage->set('parent_id', $newParent->id); $repeaterPage->set('templates_id', $newTemplate->id); $repeaterPage->set('status', $newStatus); $repeaterPage->set('published', time()); // make this adjustable as well? $repeaterPage->save(['noHooks'=>true]); // cleanup $forPage->save($forField, ['noHooks'=>true]); return $repeaterPage; } Note: It should be improved to make sure what's provided in the arguments is valid. Also maybe have the ability to set the 'name' field of the page as well instead of preserving the auto-generated one that a repeater item gets assigned. Also maybe use types for the arguments. --- Example: Let's say you have a repeater field called 'books'. Then you decide one day that it would be better that they existed as regular pages instead of repeater pages. Therefore, you would create a new template called 'book' making sure it has the same fields as the repeater fields, then do this: foreach($pages->get('/path/to/page/')->books as $book) { convertRepeaterPageToPage($book, wire('pages')->get('/foo/'), 'book', 1); } 2 1
Jonathan Lahijani Posted 15 hours ago Posted 15 hours ago @bernhard Here's a function that solves the original problem of how to MOVE (not copy!) repeater items from one page to another, which preserves IDs. I tested it and I believe I accounted for everything, but I recommend testing it more before using it in production. // move the repeater items from fromPage to toPage // the same repeater field must be assigned to both pages // note: fromPage and toPage can be repeater page items as well since they are technically pages function moveRepeaterItems(string $fieldName, Page|RepeaterPage $fromPage, Page|RepeaterPage $toPage): void { // checks if(!wire('fields')->get($fieldName)) { throw new WireException("Field '$fieldName' does not exist."); } if(!$fromPage->id) { throw new WireException("From page does not exist."); } if(!$toPage->id) { throw new WireException("To page does not exist."); } if(!$fromPage->hasField($fieldName)) { throw new WireException("From page does not have field '$fieldName'."); } if(!$toPage->hasField($fieldName)) { throw new WireException("To page does not have field '$fieldName'."); } if($toPage->get($fieldName)->count('include=all,check_access=0')) { throw new WireException("To page already has items in field '$fieldName'."); } // store the parent_id $parent_id = wire('database')->query("SELECT parent_id FROM field_{$fieldName} WHERE pages_id = '{$fromPage->id}'")->fetchColumn(); // delete potential (and likely) existing toPage data placeholder // prevents this error: Integrity constraint violation: 1062 Duplicate entry '1491109' for key 'PRIMARY' in /wire/core/WireDatabasePDO.php:783 // remember, this will be empty since we checked above that there are no items in the toPage field wire('database')->query("DELETE FROM `field_{$fieldName}` WHERE `pages_id` = '{$toPage->id}'"); // update the record in table 'field_$field' where pages_id=$fromPage->id and change the pages_id to $toPage->id wire('database')->query("UPDATE `field_{$fieldName}` SET `pages_id` = '{$toPage->id}' WHERE `pages_id` = '{$fromPage->id}'"); // update the record in table 'pages' where id=$parent_id: change name from 'for-page-{$fromPage->id}' to 'for-page-{$toPage->id}' wire('database')->query("UPDATE `pages` SET `name` = 'for-page-{$toPage->id}' WHERE `id` = '{$parent_id}'"); } // example moveRepeaterItems( fieldName: 'order_line_items', fromPage: $pages->get("/orders/foo/"), toPage: $pages->get("/orders/bar/") ); 3
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now