May I delete a page programmatically if it has subpages?


I try in a hook to programmatically delete pages which has also subpages and delete them all (page & subpages):

foreach ( $proposals as $proposal ) :
   $proposal->delete( true );

But pages are not deleted :-/
Is it allowed in Kirby to delete a page programmatically if it has subpages?

Hm, at least some of the pages should get deleted though (if they are not drafts). Do you get any error messages? Or have you set deleting of pages too false? Which hook are you using?

What do you mean by “if they are not drafts”?
We can’t delete drafts pages programmatically?

I don’t have error message and all my delete are set to true.

I use the “page.duplicate.after” hook

And the purpose is to delete the subpages once the page is duplicated? Could you please post the complete hook?

And the purpose is to delete the subpages once the page is duplicated?
Yes, I want to delete subpages even if the user set the “Copy pages” toggle to “On” when duplicating the page.

This is my hook:

'page.duplicate:after' => function ( $duplicatePage ) {
        if ( $duplicatePage->template() == 'partnership' ) :

            $campaigns = null;
            $campaigns  = $duplicatePage->childrenAndDrafts()->filterBy( 'template', 'campaign' );
            foreach ( $campaigns as $campaign ) :
                // delete proposals pages
                $proposals = $campaign->childrenAndDrafts()->filterBy( 'template', 'proposal' );
                foreach ( $proposals as $proposal ) :
                    $proposal->delete( true );



The pages hierarchy is: partnership > campaign > proposal > subpages > sub-subpages

subpages and sub-subpages are drafts pages

I also have “partnership > campaign > evaluator” pages and I have no problem to delete them in the hook

Do these templates partnership.php, campaign.php and proposal.php actually exist? If they do not exist, you have to use intendedTemplate instead.

Yes they all exist.

Hm, I tried to set up a similar structure and in my local setup, the subpages are deleted, while the campaign pages are preserved (using intendedTemplate, because I couldn’t be bothered to set up new templates).

While there are those issues quoted by @ahmetbora with deleting pages in hooks, at least some pages should actually get deleted.

Could you provide the project or a stripped down version with only the relevant parts for testing?

Ok, I will send you the project as soon as possible, I must leave my house right now.

May I send you a link to download the zip by email? If yes which email?
sonja[at]getkirby[dot]com ?

Yes, that’s fine.

Just before to leave, I tried to delete “proposal” in the “parnership” template and it works, proposal and subpages are deleted.


$campaigns = $page->childrenAndDrafts()->filterBy( 'template', 'campaign' );
foreach ($campaigns as $campaign) {
    $proposals = $campaign->childrenAndDrafts()->filterBy( 'template', 'proposal' );
    foreach ( $proposals as $proposal ) :
                    try {

                        $proposal->delete( true );

                        } catch(Exception $e) {

                        echo $e->getMessage();


Hi Sonja, you should receive my application in the next minutes …

Hey Gilles, just to let you know: I haven’t received anything yet.

Mhmm weird :thinking:

I 've resent the email …

Got it!

Hm, the only way to reliably remove everything I’ve found, is using low level methods:

    'page.duplicate:after' => function ( $duplicatePage ) {
        if ( $duplicatePage->template()->name() === 'campaign' ) {
            foreach ($duplicatePage->childrenAndDrafts() as $child) {

Thank you Sonja for looking at my code, I will use your solution.

Should I create an issue on GitHub?

Yes, please create an issue referencing the other issues.

I’ve also created a feature idea to prevent copying of the tree rather then having to delete those pages afterwards: