Jump to content
VeiJari

How to programmatically change page name after saving it?

Recommended Posts

Hello forum!

I've yet again stumbled on a head-scratching situation. We have enabled the option on our articles template and events template that it skips the title adding part and goes straight to the form. This is what our customer wants. So when you add a new article or event it automatically names it temporary to "article-0000000" and same with event. Now the problem is that obviously after saving the form we want to change to page url or "name" to the title, like it's normally. 

Now here's the code for the hook:

wire()->addHookBefore("Pages::saved(template=tapahtuma|artikkeli)", function($hook) {
  $page = $hook->arguments(0);
  $newUrl = wire()->sanitizer->pageName($page->title); // give it a name used in the url for the page
  wire()->log->message($page->name);
  $page->setAndSave('name', $newUrl);
});

I get the correct page and the name and path changes when I log them, but when I try to save it. It just loads and then I get: 

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 262144 bytes) This happens in sanitizer.php

and then another error: Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 262144 bytes) in Unknown on line 0

 

What is happening? Am I not suppose to use sanitizer in this way? When we made a temporary page object in out other hook, the sanitizer worked perfectly.

Thanks for the help!

Share this post


Link to post
Share on other sites

You're creating an infinite loop. You hook into "after save" change something and save again, which again triggers your "after save" hook and so on. Either look for another hook to do your work (potentially change things last minute before the actual save) or add some way for your hook to only run once and not for the saves it initiated on it's own.

  • Like 2

Share this post


Link to post
Share on other sites

@VeiJari maybe hook after pages::saveReady is better for this. But also it is possible in a pages::saved hook to save the page again without running into an infinite loop with, for example, using a temporary property:

wire()->addHookBefore("Pages::saved(template=tapahtuma|artikkeli)", function($hook) {
	$page = $hook->arguments(0);
	if($page->skipMyHook) {             // if the page already has our temporary property, we skip further processing
		return;
	}
	$newUrl = wire()->sanitizer->pageName($page->title); // give it a name used in the url for the page
	wire()->log->message($page->name);
	$page->skipMyHook = true;           // add a temporary property to the page (it is only in memory)
	$page->setAndSave('name', $newUrl);
});

 

  • Like 1

Share this post


Link to post
Share on other sites
2 hours ago, LostKobrakai said:

You're creating an infinite loop. You hook into "after save" change something and save again, which again triggers your "after save" hook and so on. Either look for another hook to do your work (potentially change things last minute before the actual save) or add some way for your hook to only run once and not for the saves it initiated on it's own.

Ah, silly me 😄 Of course I didn't think the obvious.

It now works and I'm using this module: https://modules.processwire.com/modules/page-rename-options/ for all the pages at the moment!

Thanks anyway!

37 minutes ago, horst said:

@VeiJari maybe hook after pages::saveReady is better for this. But also it is possible in a pages::saved hook to save the page again without running into an infinite loop with, for example, using a temporary property:


wire()->addHookBefore("Pages::saved(template=tapahtuma|artikkeli)", function($hook) {
	$page = $hook->arguments(0);
	if($page->skipMyHook) {             // if the page already has our temporary property, we skip further processing
		return;
	}
	$newUrl = wire()->sanitizer->pageName($page->title); // give it a name used in the url for the page
	wire()->log->message($page->name);
	$page->skipMyHook = true;           // add a temporary property to the page (it is only in memory)
	$page->setAndSave('name', $newUrl);
});

 

Ah I see! Good to know, might be handy someday for preventing those pesty infinite loops 😄

Thanks horst

  • Like 1

Share this post


Link to post
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

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Gadgetto
      Hi there,
      I'd like to prevent duplicate values in a specific page field (FieldtypeText) across all pages. The hook should also prevent saving of the page if a duplicate value is detected. Therefore I created two hook methods but I can't get it to work properly.
      Here are both hooks placed in init() method of an autoloader module:
      The first hook is to initially preset the field with the page id. The field value can be customized by the admin before saving.
      The second hook should check if the value is unique.
      If I create a new page and try to save, the WireException is already triggered in first step of page creation, where you enter the page title: SKU [] is already in use
      If I check the value of $sku (with Tracy) it's always empty!
      Any idea what could be wrong?
      EDIT: if I remove the second hook, the field value is correctly preset with the page id!
      $this->addHookAfter('Pages::added', $this, 'presetProductFields', ['priority' => 99]); $this->addHookAfter('Pages::saveReady', $this, 'checkSKUUnique', ['priority' => 101]); And this are the hook methods:
      public function presetProductFields(HookEvent $event) { $snipwire = $this->wire('snipwire'); if (!$snipwire) return; $page = $event->arguments(0); if ($snipwire->isProductTemplate($page->template)) { if ($page->hasfield('snipcart_item_id')) $page->setAndSave('snipcart_item_id', $page->id); } } /** * Check if the SKU value is unique across all product pages. * (Method triggered after Pages saveReady -> just before page is saved) * * @throws WireException * */ public function checkSKUUnique(HookEvent $event) { $snipwire = $this->wire('snipwire'); if (!$snipwire) return; $page = $event->arguments(0); if ($snipwire->isProductTemplate($page->template)) { $field = $page->getField('snipcart_item_id'); $sku = $page->snipcart_item_id; // SKU field value bd($sku); if ($page->isChanged('snipcart_item_id')) { $exists = $this->wire('pages')->get("snipcart_item_id=$sku"); if ($exists->id) { // value is not unique! $error = $this->_('SKU must be unique'); $exception = sprintf( $this->_('SKU [%s] is already in use'), $sku ); $inputfield = $page->getInputfield($field); $inputfield->error($error); throw new WireException($exception); // Prevent saving of non-unique value! } } } }  
    • By t0b1
      Hello there, and thanks for ProcessWire!
      I'm getting to know ProcessWire while doing my first project using it and I really like it so far.
      The challenge I'm facing right now is the following:
      I have a One-Pager using fullpage.js, realized as a single PW-Page containing a Repeater Field where each Repeater Item is one Section.
      Some Sections are supposed to have a little menu at the Top which references/links to different Sections of the Website, so I wanted to use a Checkbox Field "Top Menu" to decide if a Section gets a Menu and a Page Reference Field to choose the different Sections (Repeater Items) it should contain.
      I've already accomplished this by pasting the following code into /site/ready.php:
       
      $wire->addHookAfter('InputfieldPage::getSelectablePages', function($event) { if($event->object->hasField == 'top_menu_entries') { $page = $event->arguments('page'); if($page instanceof RepeaterPage) $page = $page->getForPage(); $event->return = $page->; } });  
      The only problem that still remains is that when I select the Entry of the Repeater Item itself it doesnt save the selection, meaning after I saved it's unselected again.
      On some Sections I do want a Menu-Entry for the Section itself though (which would be styled differently and not link anywhere) for Continuity-Reasons, any ideas on how to achieve that?
    • By EyeDentify
      Hello Dear PW Gurus.

      Hope you fair well in these Corona Crisis times.

      Anyhow, i have a problem with a Cryptic error message that shows up when i am trying to Delete images out of a Images field.
      The Error message does say a little but it does not make sense to me why i can´t delete the images because of it.

      Is it some permission issue perhaps?

      I will attach screenshots of it and what i did prior to it.
      I am from sweden so ignore the funny words here and there.

      Hope you all can send me on the right track.
      i am running PW 3.0.139 on this install.

      Step1:

      Step 2: Select the images with the trashcan symbol

       
      Step 3: Cryptic Error message

      I am scratching my head on this one, have not seen this before.

      Thankfull for all help.
      /EyeDentify
    • By Gadgetto
      Hi guys (didn't see any gals here),
      I have a module which has some custom helper classes. One of these classes has methods which are hookable. The class is derived from WireData. An additional complication is that this class has its own namespace. What would be the correct way to provide the hookable methods via API?
      Here is a very simplified code sample of the class:
      <?php namespace SnipWire\Services; use ProcessWire\WireData; use ProcessWire\WireException; class Webhooks extends WireData { ... public function ___handleOrderCompleted() { if ($this->debug) $this->wire('log')->save( self::snipWireWebhooksLogName, '[DEBUG] Webhooks request: handleOrderCompleted' ); $this->responseStatus = 202; // Accepted } public function ___handleOrderStatusChanged() { if ($this->debug) $this->wire('log')->save( self::snipWireWebhooksLogName, '[DEBUG] Webhooks request: handleOrderStatusChanged' ); $this->responseStatus = 202; // Accepted } ... } The class itself is invoked/used by a hook from within the main module:
      Depending of the event, one of the methods of the Webhooks class above is triggered. A developer should now be able to use one of the ___handle* methods to do further things.
      /** * Check for webohook request and process them. * (Method triggered before ProcessPageView::execute) * */ public function checkWebhookRequest(HookEvent $event) { if ($webhooksEndpoint = $this->get('webhooks_endpoint')) { if ($this->sanitizer->url($this->input->url) == $webhooksEndpoint) { /** @var Webhooks $webhooks Custom ProcessWire API variable */ $this->wire('webhooks', new Webhooks()); $this->wire('webhooks')->process(); $event->replace = true; // @note: Tracy Debug won't work from here on as normal page rendering is omitted! } } } Should I provide a custom API variable e.g. $webhooks? Or how is the best way to do this?
    • By Flashmaster82
      Hi, can you guys help a beginner with a problem..
      On my template (profile_page) i have a dropdown (page reference) where i can choose a sports team (team_page) that is related to that profile which is also its parent. Then on my Competition1 page (competition_page) I have page reference field (profiles) a dropdown that i want to display only profiles that has choose a specific sports team (template=team_page) the page parent to be specific.
      Structure/Template
      Sports_team1 (team_page)
           Profile1 (profile_page)
           Profile2 (profile_page)
           Profile3 (profile_page)
           Competition1 (competition_page)
       
      ready.php
      <?php $wire->addHookAfter('InputfieldPage::getSelectablePages', function($event) { if($event->object->hasField == 'profiles') { $relative = $page->parent->name; $event->return = $event->pages->find("template=profile_page, sports_team=$relative"); } }); ?> This returns with no results in the dropdown. If i remove sports_team=$relative then it displays all profiles that have profile_page as template, so it works almost. But i will have more sports teams so this is just an example. I only want to display the profiles that has choosen the parent team on there profile page in admin not front end.
      I hope i was able to explain it so you guys can understand a little bit. Need some help please! /Thanks
       
       
×
×
  • Create New...