Jump to content

How to copy field values of a repeater from the parent page to a child page?


Juergen
 Share

Recommended Posts

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

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

Edit @see post below...Use import instead! (duh!, early morning!)

-------------------------------------------

The process then would be:

  1. Create the child page
  2. The child page's template should have the repeater field ready to receive items
  3. 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 by kongondo
better answer
Link to comment
Share on other sites

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');

 

  • Like 1
Link to comment
Share on other sites

Hello @kongondo

your code works. I only get a notice from Tracy:

Screenshot_3.jpg

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.

Screenshot_4.jpg

Link to comment
Share on other sites

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 by kongondo
add more code
  • Like 1
Link to comment
Share on other sites

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

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();
}

 

  • Like 1
Link to comment
Share on other sites

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);

}

 

  • Like 1
Link to comment
Share on other sites

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 by kongondo
Stuff...
Link to comment
Share on other sites

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. 

  • Like 2
Link to comment
Share on other sites

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.

  • Like 1
Link to comment
Share on other sites

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.

  • Like 2
Link to comment
Share on other sites

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).

repeaterpage.jpg

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.

Screenshot_4.jpg

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

Quick BTWs...

  1. In a module context, output formatting is always off (i.e. false), so no need to set it yourself.
  2. 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

about-us-tree.png

 

And this tree under repeaters. The crossed out repeater items are the unpublished 'ready to edit repeater items'

repeaters-tree.png

As you can see, names only have to be unique under the same parent (not just for repeaters but throughout ProcessWire)

 

Edited by kongondo
Link to comment
Share on other sites

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

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...