Jump to content

Doing additional logic after saving a new page.


Brmm

Recommended Posts

Hi, I have a simple question.

I need to do additional logic right after a new page has been created/saved, to be more specific I need to call an API with the value of some of its fields, is this possible in process wire?

Thank you!

  • Like 1
Link to comment
Share on other sites

You'd want to hook after $pages->save(); If you want your function to execute on every page save, any time, then you'd want to create an autoload module. http://wiki.processwire.com/index.php/Module_Creation

If you want to capture newly added pages separately, then you'd also want to hook $pages->added(), which is called after a new page has been added. This example shows both:

/site/modules/HelloWorld.module

class HelloWorld extends WireData implements Module {
 public static function getModuleInfo() {
   return array(
     'title' => 'Hello World',
     'summary' => 'Just an example module class.',
     'version' => 1 
     'autoload' => true,
   );
 }
 public function init() {
   $this->pages->addHookAfter('save', $this, 'hookSave'); 
   $this->pages->addHookAfter('added', $this, 'hookAdded'); 
 }
 public function hookSave(HookEvent $event) {
   // this function called any time a page is saved (whether new or not)
   $page = $event->object; 
   // now you can access anything from the $page
 }
 public function hookAdded(HookEvent $event) {
   // this function only called after a new page added
   $page = $event->object; 
 }
}
  • Like 4
Link to comment
Share on other sites

  • 5 weeks later...

I'm working on a small project which happens to be a photographer portfolio.

I have template which represents an album containing image field with unlimited items with some additional description fields.

Is it possible to automatically create an unpublished blog post (announcing new photos) every time an album is saved with new images added?

Link to comment
Share on other sites

Is it possible to automatically create an unpublished blog post (announcing new photos) every time an album is saved with new images added?

This should be possible. You'd use a Pages::saveReady hook. You'll want to have your hook do something along these lines:

if($page->template == 'album' && $page->isChanged('images')) {
 $newItems = $page->images->getItemsAdded(); 
 if(count($newItems)) {
   // images were added, create your new unpublished blog post
   // the $newItems WireArray contains the images that were added
 }
}
  • Like 3
Link to comment
Share on other sites

Greetings,

Glad to find this! I was searching for an answer to a similar question.

Would this same technique be effective in a ProcessWire-based e-commerce application? I'm looking to use ProcessWire to build product pages, and allow customers to place orders as pages, then send the details of that order to FoxyCart.

I'm looking at other techniques as well.

Thanks,

Matthew

Link to comment
Share on other sites

Is it possible to manipulate the fieldgroup of an template before it's saved?

Like so:

public function addProcessField(HookEvent $event) {
	$template = $event->object;
	$template->fields->add('process');
}
Link to comment
Share on other sites

Is it possible to manipulate the fieldgroup of an template before it's saved?

You can hook before Fieldgroups::save or Templates::save. 

public function init() {
  $fieldgroups->addHookBefore('Fieldgroups::save', $this, 'addProcessField'); 
}
public function addProcessField(HookEvent $event) {
  $fieldgroup = $event->arguments[0];
  if(!$fieldgroup->hasField('process')) $fieldgroup->add('process'); 
} 
  • Like 5
Link to comment
Share on other sites

  • 9 months later...

How about if you wan't to add logic before saving new page?

I'd like to prefill page title and name fields when new product page is added. I've tried hook it to ProcessPageAdd::execute but don't know how to set field values here before form is rendered. If I hook before Inputfield::render I'm able to pre-set values for both title and _pw_page_name but this doesn't look right...

public function createItem(HookEvent $event) {
	
	// limit action to admin pages only
	if (wire('page')->template->flags & Template::flagSystem) {
		
		// get parent page
		$parent = isset($_GET['parent_id']) ? wire('pages')->get($_GET['parent_id']) : null; 

		// add this to item pages only
		if ($parent && $parent->template == 'items') {

			// get the current field
			$field = $event->object;

			// proceed on (empty) title and page name fields only
			if (($field->name == 'title' || $field->name == '_pw_page_name') && $field->value == '' ) {

				// finally, set the value
				$field->set('value', 'some_val');
			}
		}
	}
}
Link to comment
Share on other sites

I think what you want is to hook after ProcessPageAdd::buildForm. That returns an InputfieldForm object, that you could manipulate with an 'after' hook. The only problem is that method isn't currently hookable. But you can make it hookable by editing the file and prepending 3 underscores to it, i.e. "buildForm" => "___buildForm". I will make this same change in the core source and you'll see it in the next batch of dev commits. 

  • Like 2
Link to comment
Share on other sites

Muchas gracias Ryan! I'm just bit lost how to continue from here.. How can I get fields from this specific form:

public function createItem(HookEvent $event) {
	$form = $event->object;
	die(var_dump($form->fields));
}

I get data from all existing fields. I've also tried:

// to get from
$form = $event->object;
$form = $event->arguments(0);

// to get fields
$fields = $form->getInputfieldsForm();
$fields = $form->processor()->getInputfieldsForm();
Link to comment
Share on other sites

OK, had some time to get hands dirty and here's what came up:

module init:

public function init() {
	$this->addHookAfter('ProcessPageAdd::buildForm', $this, 'newItem');
}

function to prefill values on new items:

public function newItem(HookEvent $event) {
			
	// get parent page
	$parent = wire('pages')->get((int)$_GET['parent_id']);
	
	// continue only if new item page
	if ($parent->template->name != 'items') return;
			
	// hook to page inputfields
	$this->addHookBefore('Inputfield::render', function($event) {
		
		$field = $event->object; // current field
		$fields = array('title','_pw_page_name'); // fields to modify
		
		if (in_array($field->name, $fields) && empty($field->value)) {
			$field->set('value', 'some_val');
		}
		
	});
	
}

Looks much better now - doesn't it?

  • Like 1
Link to comment
Share on other sites

  • 1 year later...

The hook should work whether it's in the admin or via the API, but if the code that sets up the hook is in a module that isn't autoloaded or called via the API then it won't work. So it would really help to see exactly how you have things set up.

Link to comment
Share on other sites

class AbcDatabase extends WireData implements Module {
	
	public static function getModuleInfo() {
		return array(
		        'title' => 'ABC Database', 
		        'version' => 001,
		        'summary' => 'Base module for the Database features',
		        'singular' => true,
		        'autoload' => true,
		        'installs' => array(),
		        'requires' => array(),
		        );
	}
	public function __construct() {
	}
	public function init() {
		$this->pages->addHookBefore('Pages::save', $this, "hookAutoPageName");
	}
	
	public function ready() { // PW API is ready
		// Controllers go here		
	}
	
	public function hookAutoPageName(HookEvent $event) {
	  $pageid = $this->sanitizer->selectorValue($this->input->get->id);
	  $page = $this->pages->get($pageid);
	  
	  // Bases the name of the company template off of the company_name field and keeps them in sync
	  if($page->template == 'company') {
	  	if($page->company_name != '') {
			  $newName = wire('sanitizer')->pageName($page->company_name, true);
			  if($page->name == $newName) return;
			  
			  $pageMatch = $page->parent->child("name=$newName,include=all") ?: '';
			  if($pageMatch->id && $page->id != $pageMatch->id) { //This is a duplicate name, so we have to add an increment to avoid throwing error
			  	
				  while($pageMatch->id) {
				  	$newName = $pageMatch->name . "-1";
				  	$pageMatch = $page->parent->child("name=$newName,include=all") ?: '';
				  }
				  
				  $page->name = $newName;
				}
			  else {
			  	$page->name = $newName;
			  }
			}
		}
	}

Below that is the install and uninstall functions and closing brace.

 
Link to comment
Share on other sites

Replace:

$pageid = $this->sanitizer->selectorValue($this->input->get->id);
$page = $this->pages->get($pageid);

with:

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

Remember the id get variable won't be available on the front-end via the API.

EDIT: Actually, better yet, don't use $page at all, use $p instead - you don't want to overwrite the current $page with the one being saved.

  • Like 2
Link to comment
Share on other sites

There's also this:

$this->pages->addHookBefore('Pages::save', $this, "hookAutoPageName");
// to either/or
$this->addHookBefore('Pages::save', $this, "hookAutoPageName");
$this->pages->addHookBefore('save', $this, "hookAutoPageName");
  • Like 1
Link to comment
Share on other sites

  • 5 months later...

Sorry if this is mentioned well elsewhere, but just in case not and in case someone else might find it handy, this example helped me, in a few minutes, get emails sent out, with custom messages, when certain pages were saved.

https://processwire-recipes.com/recipes/extending-page-save-process/

I am repeatedly amazed by the elegance and power of ProcessWire... To Ryan and all fellow PWers—THANK YOU.

  • 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
×
×
  • Create New...