Juergen Posted December 20, 2016 Share Posted December 20, 2016 Hello @ all, does anyone know how to copy all the field values of a repeater from a parent page to a child page via the API. I know how to do it with standard fields (fe textfields) but repeaters are more complicated. Scenario: I have a parent page (fe a page about an event) and via a hook I create several child pages (fe all the dates for this event). During the creation process I copy several field values from the parent page to the new created child pages. For standardfields I copy the value like this: $k = new Page(); $k->setOutputFormatting(false); $k->participantmaxnumber = $page->participantmaxnumber; //copy the field participantmaxnumber from the parent to the child page $k->save() But how can I achive the same by copying a repeater with several fields in it. This doesnt seem to work: $k = new Page(); $k->setOutputFormatting(false); foreach($page->eventpricerepeater as $repeateritem) { $repeateritem->earlybookingdeadline = ...; } $k->save() I have also tried $k->eventpricerepeater = $page->eventpricerepeater; but without luck. Has anyone done this in the past and knows how to copy repeaters and its items? Best regards Link to comment Share on other sites More sharing options...
kongondo Posted December 20, 2016 Share Posted December 20, 2016 If earlybookingdeadline is not an array itself, then this should work $k->earlybookingdeadline = $repeateritem->earlybookingdeadline;// earlybookingdeadline NOT an array; it just returns a value 1 Link to comment Share on other sites More sharing options...
Juergen Posted December 20, 2016 Author Share Posted December 20, 2016 Thanks @kongondo, but the repeateritem of the childpage contains no values with your code after creating the child page(s). It seems that the code doesnt add the value to a field INSIDE the repater (maybe if the field is outside). I got no errors, so the syntax is ok. Link to comment Share on other sites More sharing options...
kongondo Posted December 20, 2016 Share Posted December 20, 2016 Are you adding to a repeater field or a normal field? My code was for a normal field. If copying from REPEATER to REPEATER...then check out this thread 2 Link to comment Share on other sites More sharing options...
Juergen Posted December 20, 2016 Author Share Posted December 20, 2016 I want to add fields (values) from a repeater to fields of another repeater: So the repaters are the same (one in the parent and one in the child page). I want to copy the repeater of the parent page with all its values to the childpage. Link to comment Share on other sites More sharing options...
kongondo Posted December 20, 2016 Share Posted December 20, 2016 See my edited post above yours Link to comment Share on other sites More sharing options...
kongondo Posted December 20, 2016 Share Posted December 20, 2016 (edited) Edit @see post below...Use import instead! (duh!, early morning!) ------------------------------------------- The process then would be: Create the child page The child page's template should have the repeater field ready to receive items Add a repeater item(s) to the child page. See this (using API to add repeater items)....getting values from the parent page Maybe there's a different way of doing this....not sure...yes...import! Edited December 20, 2016 by kongondo better answer Link to comment Share on other sites More sharing options...
kongondo Posted December 20, 2016 Share Posted December 20, 2016 Hmm...or maybe import will be easier/faster? Link to comment Share on other sites More sharing options...
kongondo Posted December 20, 2016 Share Posted December 20, 2016 This works for me $c = $page->child;// page here is the parent page //$repeaterItems = $page->repeater_test;// repeater with 6 fields (email, file, integer, text, single image, multi-images) x 2 repeater items //$c->repeater_test->import($repeaterItems);// alternative import $c->repeater_test->import($page->repeater_test);// import (into an existing repeater field in the child page) $c->of(false); $c->save('repeater_test'); 1 Link to comment Share on other sites More sharing options...
Juergen Posted December 20, 2016 Author Share Posted December 20, 2016 Thank you @kongondo, I will try it out in the next time and post it here if it works. 1 Link to comment Share on other sites More sharing options...
Juergen Posted December 20, 2016 Author Share Posted December 20, 2016 Hello @kongondo your code works. I only get a notice from Tracy: but thats not a big problem. I am running Tracy in strict mode. A new problem occurs: Everytime I start to create the child pages with the API the "old" child pages including the repeater must be deleted. The child pages will be deleted but not the pages created by the repeater. These are the pieces of code that I have tried but without success. foreach ($page->children("include=all") as $child) { $child->delete(); //delete all children first } OR foreach ($page->children("include=all") as $child) { foreach($child->eventpricerepeater as $r) { $r->delete(); } $child->delete(); //delete all children first } The undeleted repeater pages lead to the SQL-error for duplicate entry. Link to comment Share on other sites More sharing options...
kongondo Posted December 20, 2016 Share Posted December 20, 2016 (edited) Repeaters pages need to be deleted a bit differently // delete repeater page: admin/repeaters/for-field-xxx $repeaterID = $fields->get('name_of_repeater_field')->id; // make sure you get the right repeater page $repeaterPage = $pages->get("parent.name=repeaters, name=for-field-$repeaterID"); if($repeaterPage->id) $pages->delete($repeaterPage, true); Regarding the error, not sure why you are getting it, but you can check like this: if($parent && $parent->id) { } Edited December 20, 2016 by kongondo add more code 1 Link to comment Share on other sites More sharing options...
Juergen Posted December 20, 2016 Author Share Posted December 20, 2016 Ok, my child pages need to be called in a foreach, cause there are more than one: foreach ($page->children("include=all") as $child) { $repeaterID = $child->get('eventpricerepeater')->id; $repeaterPage = wire('pages')->get("parent.name=repeaters, name=for-field-$repeaterID"); if($repeaterPage->id) $child->delete($repeaterPage, true); $child->delete(); } I run the code from a module, therefore I use "wire('pages')" instead of $pages. Unfortunately the code doesnt delete the repeater pages. Link to comment Share on other sites More sharing options...
kongondo Posted December 20, 2016 Share Posted December 20, 2016 I have re-read your post and I see where we went wrong. Each field has only one repeater page. Its child pages are the parents of the repeater items. Using true in the delete deletes the repeater parent page and its children. That's what my code would do. However, you want to delete only the children. So... Repeaters Hierarchy admin Repeaters repeater_field_name name_of_page_with_repeater 1234567890// repeater item #1 on "name_of_page_with_repeater" 1346798941// repeater item #2 You want to delete 'name_of_page_with_repeater' AND NOT the page 'repeater_field_name' The below (untested) should work (@see amended code in post below...$child has to be deleted first) $page = $this->wire('pages')->get(1234); $pages = $this->wire('pages'); $repeaterID = $this->wire('fields')->get('eventpricerepeater')->id; // this is the 'repeater_field_name' page $repeaterPage = $pages->get("parent.name=repeaters, name=for-field-$repeaterID"); foreach ($page->children("include=all") as $child) { // this is the 'name_of_page_with_repeater' $childRepeaterPage = $repeaterPage->child("name=for-page-{$child->id}"); // will also delete the repeate item pages, e.g. the '1234567890' if($childRepeaterPage->id) $pages->delete($childRepeaterPage, true); $child->delete(); } 1 Link to comment Share on other sites More sharing options...
Juergen Posted December 20, 2016 Author Share Posted December 20, 2016 You are right with your logic, but the code doesnt work . Deleting repeaters is more than tricky via the API . Maybe I should find another solution. Link to comment Share on other sites More sharing options...
kongondo Posted December 20, 2016 Share Posted December 20, 2016 My bad...The child page has to be deleted first: $page = $this->wire('pages')->get(1234); $pages = $this->wire('pages'); $repeaterID = $this->wire('fields')->get('eventpricerepeater')->id; // this is the 'repeater_field_name' page $repeaterPage = $pages->get("parent.name=repeaters, name=for-field-$repeaterID"); foreach ($page->children("include=all") as $child) { $id = $child->id; // first delete the child page $child->delete(); // this is the 'name_of_page_with_repeater' $childRepeaterPage = $repeaterPage->child("name=for-page-$id"); // will also delete the repeate item pages, e.g. the '1234567890' if($childRepeaterPage->id) $pages->delete($childRepeaterPage, true); } 1 Link to comment Share on other sites More sharing options...
Juergen Posted December 20, 2016 Author Share Posted December 20, 2016 Mmhh! The $childRepeaterPage->id returns always 0 if I output it via a message. Could this be the problem why its not working? Link to comment Share on other sites More sharing options...
kongondo Posted December 20, 2016 Share Posted December 20, 2016 (edited) Something funky going on there. I have tested the code and it definitely works. Any errors? Does a visual check reveal anything? e.g. is that child page there? Is the repeater page there? Does it work outside a module context? Do you have Tracy/Debug on? Edit Also...did you take note of my code changes? Edited December 20, 2016 by kongondo Stuff... Link to comment Share on other sites More sharing options...
adrian Posted December 20, 2016 Share Posted December 20, 2016 This sounds like a handy AdminAction Not specifically from the parent to child page, but a general one for copying/moving from one page to another. Not sure if it would be best as a new action, or incorporated into the logic of the "Copy Field Content To Other Page" action which currently only handles basic text based fields. I would like to extend this to support all field types. 2 Link to comment Share on other sites More sharing options...
kongondo Posted December 20, 2016 Share Posted December 20, 2016 @adrian....was just thinking about that 1 Link to comment Share on other sites More sharing options...
adrian Posted December 20, 2016 Share Posted December 20, 2016 1 minute ago, kongondo said: @adrian....was just thinking about that If you're keen, I'd love a PR! I think that adding it to that existing action would be the best approach, so I think I need to revisit the "Author" column so that everyone gets credit if there are multiple authors for an action. 1 Link to comment Share on other sites More sharing options...
adrian Posted December 20, 2016 Share Posted December 20, 2016 Getting a little OT here, but @Juergen - if this is incorporated into Admin Actions, you could call it via the API like this: $options = array( 'field' => 99, 'sourcePage' => 1131, 'destinationPage' => 1132 ); $modules->get("ProcessAdminActions")->CopyFieldContentToOtherPage($options); Yes, that action should be improved to also handle field names (not just IDs), but you get the idea. 2 Link to comment Share on other sites More sharing options...
Juergen Posted December 21, 2016 Author Share Posted December 21, 2016 Away from the deletion of the children repeater pages - it seems that there is still a problem by copying the repeater to the children pages $k->eventpricerepeater->import($page->eventpricerepeater); It seems that the repeater page will not be copied (created) to the children pages, because the page name is the same as in the parent (Duplicate-Entry). There exists only one page from the parent page (1482132297-8942-1) and it seems that the same page name will be copied to the children pages and therefore I always get the duplicate entry error. As a result the repater pages will not be created - so the deletion process will not take place. Here is the beginning of the code which creates the children pages foreach ($periods as $item) { // create new page $k = new Page(); $k->template = 'single-event'; // set template $k->parent = wire('pages')->get($page->id); // set the parent $k->setOutputFormatting(false); // Copy page fields from parent page to newly created child pages $k->date_start = $item; $k->eventpricerepeater->import($page->eventpricerepeater); $k->eventstatus = $page->eventstatus; .............. .............. This piece of code works because all fields except the repeater fields will be copied to the children pages. Link to comment Share on other sites More sharing options...
kongondo Posted December 21, 2016 Share Posted December 21, 2016 (edited) Quick BTWs... In a module context, output formatting is always off (i.e. false), so no need to set it yourself. No need to 'get' the same parent over and over in the loop. Do it outside the loop What is $page in your case? What PW version are you working on? The following code works for me as advertised, in ProcessWire 2.7 and 3.0.42. All parent repeater fields contents are copied over to the children repeater fields $children = array('What We Do', 'Our Mission', 'Our History', 'Our Staff'); $parent = $pages->get('/about-us/'); foreach ($children as $title) { $k = new Page(); $k->template = 'basic-page'; $k->parent = $parent; $k->title = $title; $k->repeater_test->import($parent->repeater_test); $k->save(); } The code above results in this tree under Home And this tree under repeaters. The crossed out repeater items are the unpublished 'ready to edit repeater items' As you can see, names only have to be unique under the same parent (not just for repeaters but throughout ProcessWire) Edited December 21, 2016 by kongondo Link to comment Share on other sites More sharing options...
Juergen Posted December 21, 2016 Author Share Posted December 21, 2016 Mmmhh! It works in your case. I run the latest dev version of PW (3.0.45) $page is the parent page. I run a hook to create the child pages. This is how $page is defined inside the hook function: $page = $event->arguments[0]; So $page is always the page that you are working on (the parent page). Only for better explanation: It is a complex children creating function that is running with the hook. It creates children with different start dates depending on the settings. So the start date array is called $periods in my case. It is responsible for creating the children. If fe 5 dates should be created with an interval of 1 week than the period array consists of the five calculated start dates. Here is an example: $periods = array('21.12.2016', '28.12.2016', '04.01.2017', '11.01.2016'); This is why my foreach loop doesnt look like foreach ($children as $title) { Instead of this it looks like foreach ($periods as $item) { Thats the only difference. This is the complete foreach loop which is responsible for creating the child pages and filling their fields with values from the parent page. foreach ($periods as $item) { // create new page $k = new Page(); $k->template = 'single-event'; // set template $k->parent = wire('pages')->get($page->id); // set the parent // Copy page fields from parent page to newly created child pages $k->date_start = $item; $k->eventpricerepeater->import($page->eventpricerepeater); $k->publish_until = $item + $diff; $k->eventstatus = $page->eventstatus; $k->participantmaxnumber = $page->participantmaxnumber; $k->eventmaxstatus = $page->eventmaxstatus; $k->eventmaxstatus = "1"; //get start date and create path name $eventstartmultiple = date("Y-m-d", $item); //get date for URL $k->title = $page->title; //check for other languages foreach ($this->languages as $lang) { $lname = $lang->isDefault() ? '' : $lang->id; $default = $this->languages->get("default"); if ($page->title->getLanguageValue($lang)) { $k->set("name$lname", $page->title->getLanguageValue($lang) . '-' . $eventstartmultiple); } else { $k->set("name$lname", $page->title->getLanguageValue($default) . '-' . $eventstartmultiple); } $k->set("status$lang", 1); //$k->of(false); $k->save(); } } I will try to adapt it to your code and post the result here. Link to comment Share on other sites More sharing options...
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