Jump to content

How to automatically create a bunch of child pages when saving?


titanium
 Share

Recommended Posts

I'm quite new to ProcessWire, but I'm already there where the fun part starts :grin:

I'm trying to build a module which fires when a page with a certain template is saved. It should create a set of four child pages of this page then. Each of this four pages has a template which has the same name as the page title. It works so far, but there is a problem when this page is deleted afterwards. The error message is: "Duplicate entry 'filialen-3235' for key 'name_parent_id'

I suppose that if a page is moved to the trash, it is saved again, therefore the function runs again. Somehow I have to suppress the trigger if the page is moved to trash, but I don't know how.

Thanks for any hints!

My code looks like this. I'm sure it's pretty inelegant. :(

public function init() {

$this->pages->addHookAfter('save', $this, 'createPages');

}

public function createPages($event) {

$page = $event->arguments[0];

// if template of page does not have id 46, do nothing

if($page->template->id !== 46)

{

return;

}

// four new pages should be created

$titles = array('child1', 'child2', 'child3', 'child4');

foreach($titles as $title)

{

// if page already exists: break

if($page->children('title=' . $title)->count() !== 0)

{

break;

}

// else: create new page

$newPage = new Page();

$newPage->template = $this->templates->get(strtolower($title));

$newPage->parent = $page;

$newPage->title = $title;

$newPage->save();

}

}

  • Like 1
Link to comment
Share on other sites

Welcome aboard :)

If we assume that these will be the only four child pages then you could do this maybe (untested):

if ($page->children()->count() > 0) {
   break;
} else ...

Or if there could be more child pages then the other way would be:

$skipchildcreation = 0;
foreach ($page->children as $child) {
   if (in_array($child->name, $titles) {
       $skipchildcreation = 1;
   }
}

this way you could use the variable $skipchildpagecreation to skip page creation or not - the important thing is you need to iterate through the child pages to determine the page titles.

EDIT: I think there's a third way that is a bit like you're trying to do iterating through the $titles array (which looking at it now should work), but off the top of my head you could delete that foreach and do something like:

if ($page->children('title=' . implode('|', $titles)) {
   ...

the | symbol in selectors acts like an OR from an SQL query.

One thing I've run into recently that I thought was just me is a selector bug in the Dev branch but I'm pretty sure that doesn't apply here anyway.

  • Like 1
Link to comment
Share on other sites

Change this:

foreach($titles as $title) {
if($page->children('title=' . $title)->count() !== 0) break;
...
}

to this:


$children = $page->children("include=all");
foreach($titles as $title) {
if($children->has("title=" . wire('sanitizer')->selectorValue($title))) continue;
...
}

That "include=all" in the children call ensures that the children are included even if in the trash.

It's also better to just do one $page->children() call here, because every time you pass in a unique selector to children(...) it executes another query. This way, it's only executing one query.

The wire('sanitizer') call isn't technically necessary for the page title's you've given, but I put that there just in case your actual page titles might contain commas in them. But honestly it would be better to use the page "name" property rather than title, since it is limited to a small set of characters, guaranteed to be unique for the parent, and more appropriate for this type of thing.

If you use $page->name, you could optimize it further by changing the first line to:

$children = $page->children("name=" . implode('|', $names)); 

You could do this with title too, like Pete was mentioning. But it's a little harder to account for the page titles in OR selectors since there is such a broad set of characters allowed in titles.

  • Like 2
Link to comment
Share on other sites

Thank you all so much for your really fast support! What an amazing community this is!

Yesterday evening I came up with

if($page->status === 8193 || $page->status === 10241) ...

as a temporary solution, because I wasn't aware of

$page->isTrash

Thanks, Soma!

At the end, I followed ryan's advice:

$children = $page->children("include=all");

That "include=all" in the children call ensures that the children are included even if in the trash.

I'll stick with $page->title, because $page->title is anyway needed when the new pages are created. In my case, that's not problematic, because the four child pages should always have the same names/titles, without any spaces or special characters.

The reason why I set up this module is: I have a client who needs a bunch of microsites. Every microsite has the same pages (home, imprint, contact... etc.). Because some of the placeholders are used on one or more of these pages, it's more comfortable for the client just to edit one page (the parent of the four childs). With this plugin, the client has not to take care of creating the child pages himself (in fact, I have set appropriate permissions, therefore he even can't create or edit child pages). Now, he is able to create all of the microsites with just one form - with the help of ProcessWire! Great solution in just two days altogether. I like this system so much, I'm sure it will have a bright future.

  • Like 1
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...