Jump to content

Create/Update a page programmatically


SwimToWin
 Share

Recommended Posts

How can I save a processwire page programmatically? That is, create a new page or update an existing page.

I tried the code below in a \site\templates\article.php...

<?php
// CREATE PAGE - howto?

// MODIFY PAGE
// Page /foo/bar/ exists.
$mypage = $wire->pages->get("/foo/bar/");
$mypage->title = "Foo Bar Test Page"; // Change the title.
$mypage->save();
print_r($mypage);
?>

... and then called a page using the Article template:

Error Exception: Can't save page 0: : Pages of type NullPage are not saveable (in \wire\core\Pages.php line 508)
#0 [internal function]: Pages->___save(Object(NullPage))
#1 \wire\core\Wire.php(271): call_user_func_array(Array, Array)
#2 \wire\core\Wire.php(229): Wire->runHooks('save', Array)
#3 \wire\core\Page.php(869): Wire->__call('save', Array)
#4 \wire\core\Page.php(869): Pages->save(Object(NullPage))
#5 \site\templates\article.php(10): Page->save()
#6 \wire\core\TemplateFile.php(92): require('...')
#7 [internal function]: TemplateFile->___render()
#8 \wire\core\Wire.php(271): call_user_func_array(Array, Array)
#9 \wire\core\Wire.php(229): Wire->runHooks('render', Array)
#10 \wire\modules\PageRender.module(236): Wire->__call('render', Array)
Link to comment
Share on other sites

Check this thread out:

http://processwire.c...-pages-via-api/

The error appears because your page was not found, thus the variable $mypage is an object of "NullPage".

//Note the change from $wire->pages to $pages...
$p = $pages->get("/foo/bar/");
if ($p->id) {
//Page was found, you can modify with API
} else {
//Page was not found... it is NullPage and has id = 0
}

Edit: Changed $page to $p, see Ryan's explanation below :)

Edited by Wanze
  • Like 1
Link to comment
Share on other sites

If you are doing this in a template file, better to use some name other than $page, just so that you don't overwrite the $page provided to the template.

$p = $pages->get('/about/');

if(!$p->id) { 
 // create new page, if not already there
 $p = new Page();
 $p->template = 'basic-page';
 $p->parent = '/';
 $p->name = 'about'; // URL name
}

$p->of(false); // turns off output formatting

$p->title = 'About SwimToWin';
$p->save();

  • Like 2
Link to comment
Share on other sites

How would you recommend creating two pages or more using CLI, including custom fields (summary, body, sidebar, etc.)?

I tried creating two+ pages using an array - the first page is correct, but the following pages fail. only the Name field is set, other fields are ignored / not set - I suspect this happens because the $wire object is destroyed somehow when I loop the array- no error message is displayed. Here is one variant of the code I have used:

$url_parent = "/myparent/";
$template = "basic-page";
$data = array(
array('title'=>'Foo title', 'body'=>'Text for foo.', 'name'=>'foo' ),
array('title'=>'Bar title', 'body'=>'Text for bar.', 'name'=>'bar' ),
);

foreach($data as $v) {
// Get page object.
$p = $wire->pages->get($url_parent . $v['name'] );

if(!$p->id) {
$p = new Page();
$p->template = $template;
$p->parent = $url_parent;
$p->name = $v['name'];
}

$p->of(false);

foreach($v as $kk=>$vv) {
$p->{$kk} = $vv;
}

$p->save();
}
Link to comment
Share on other sites

@jbroussia:

$url is now explained in the example above.

after you create the *first* page, that test: if(!$page->id) will always return false so the code below won't execute.

My update will explain why this is not the case ($page is defined in the foreach loop - before if(!$page->id) - it returns 0 in my tests when Name does not exist. Note that I use CLI.

Link to comment
Share on other sites

Check this thread out:

http://processwire.c...-pages-via-api/

The error appears because your page was not found, thus the variable $mypage is an object of "NullPage".

//Note the change from $wire->pages to $pages...
$p = $pages->get("/foo/bar/");
if ($p->id) {
//Page was found, you can modify with API
} else {
//Page was not found... it is NullPage and has id = 0
}

Edit: Changed $page to $p, see Ryan's explanation below :)

A cleaner way is to check it like this:

//Note the change from $wire->pages to $pages...
$p = $pages->get("/foo/bar/");
if (!($p instanceof NullPage)) {
//Page was found, you can modify with API
} else {
//Page was not found... it is NullPage and has id = 0
}
Link to comment
Share on other sites

Guys, he's bootstraping. CLI command line interface... index can also be used in command line.

Ok. Your code looks right. It's correct, you have to use $wire or wire("pages") in bootstrap.

I just tested ur code 1:1 and it works well. Name, Title and Body are all filled in on all pages.

A cleaner way is to check it like this:

//Note the change from $wire->pages to $pages...
$p = $pages->get("/foo/bar/");
if (!($p instanceof NullPage)) {
 //Page was found, you can modify with API
} else {
 //Page was not found... it is NullPage and has id = 0
}

Huh? Actually the cleanest and simplest way to use is if($p->id) ...

Link to comment
Share on other sites

  • 9 months later...

I want to dynamically change page template when a certain condition is satisfied.

For example:

if ($page->numChildren == 0) {
  //change template
}

I tried to set new template when such situation occures but I'm getting errors. 

Link to comment
Share on other sites

  • 7 months later...

Hey,

I am working on a hook, and when a page is viewed I would like to trigger an event and save something.

This is my hook

    public function init()
    {
        $this->addHook('Page::loaded', $this, 'pageViewed');
    }
    
    public function pageViewed($event)
    {
        $page = $event->object;
        if ($page->template == 'my-template') {
          // $field = $page->get("article_meta_score");
          // $page->save($field);
          $page->myfieldname = 'Something';
          $page->setOutputFormatting(false);
          $page->save();
          exit;
        }
    }

Now I am getting

( ! ) Fatal error: Exception: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '76021'
for key 'PRIMARY' (in /var/www/ProcessWire/wire/core/Pages.php line 794)
#0 /var/www/ProcessWire/wire/core/Pages.php(794): PDOStatement->execute()
#1 /var/www/ProcessWire/wire/core/Pages.php(716): Pages->savePageQuery(Object(Page), Array)
#2 [internal function]: Pages->___save(Object(Page), Array)
#3 /var/www/ProcessWire/wire/core/Wire.php(359): call_user_func_array(Array, Array)
#4 /var/www/ProcessWire/wire/core/Wire.php(317): Wire->runHooks('save', Array)
#5 /var/www/ProcessWire/wire/core/Page.php(1121): Wire->__call('save', Array)
#6 /var/www/ProcessWire/wire/core/Page.php(1121): Pages->save(Object(Page), Array)
#7 /var/www/ProcessWire/site/modules/SomeModule/SomeModule.module(45): Page->save()
#8 /var/www/ProcessWire/wire/core/Wire.php(381): SomeModule->pageViewed(Object(HookEvent))
#9 /var/www/ProcessWire/wire/core/Wire.php(317): Wire->runHooks('loaded', Array)
#10 /var/www/ProcessWire/wire/core/Page.php(1490) in /var/www/ProcessWire/index.php on line 218

The page is editable and all. But I am not sure why so.

Link to comment
Share on other sites

First off I don't think loaded is the right place to hook. Page::loaded is (I think) when a page is loaded but not have to be a rendered/viewed. It will in a autoload module also execute in the admin.

Second is you with addHook("Page::loaded" you add a new method, but it seems it would also work but better is addHookBefore addHookAfter

Next would be the exit() in such a  hook is most of the time not a very good idea, it will just stop any code that may come after your hook.

I tried your hook and get a similar error. Not looking further into why.

To hook into after a page was viewed/rendered you should hook into ProcessPageView::execute or Page::render

public function init() {
    $this->addHookAfter('ProcessPageView::execute', $this, 'pageViewed');
}

public function pageViewed(HookEvent $event){
    $page = wire("page");
    if ($page->template == 'basic-page') {
        $page->headline = 'Something';
        $page->save();
    }
}

ProcessPageView.module handles the viewing of a page on front-end

Since the ProcessPageView modifies/sets the current $page before rendering, you can get the current viewed page with wire("page"); So since you get the page this way you also don't need to set output formatting false.

public function init() {
    $this->addHookAfter('Page::render', $this, 'pageRendered');
}

public function pageRendered(HookEvent $event){
    $page = $event->object;
    if ($page->template == 'basic-page') {
      $page->of(false);
      $page->headline = 'Something2';
      $page->save();
    }
}

This does the same just with a hook after page being rendered. The current page can also be get through the hook event object. And since it is rendered there's output formatting on. So we set it to false here $page->of(false);

NOte the Page::render() isn't found in Page.php but wire/modules/PageRender.module and it's added by a method hook in that module.

  • Like 1
Link to comment
Share on other sites

Thank you @Soma.

I have tried both examples. First one still need the output formatting turned off. Else I got the error.

About the exit call : It will work. I do test whether the hook is called via exit :) .

Thank you once again for the help. I appreciate.

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

×
×
  • Create New...