Jump to content

Create non duplicate page name via API


evanmcd
 Share

Recommended Posts

Hi,

I've been using PW as the back end for a number of contest sites in which users enter a short bit of text as an answer to a question.  Each "entry" ends up as a PW Page.

In this case, it's quite common for people to provide the same answer, which of course results in a duplicate page error (Exception: Duplicate entry).

I'm wondering if there is a way to have PW automatically create a unique page name when it creates a new page that would have a duplicate name - like this-is-a-test becoming this-is-a-test-2.

Any help is great appreciated!

Thanks.

Link to comment
Share on other sites

I'm not 100 % sure right now, but I think if you don't set a name, Pw takes already care of this problem.

Edit: Yep it does, found the code in Pages::___setupNew()

protected function ___setupNew(Page $page) {

		if(!$page->name && $page->title) {
			$n = 0;
			$pageName = $this->fuel('sanitizer')->pageName($page->title, Sanitizer::translate); 
			do {
				$name = $pageName . ($n ? "-$n" : '');
				$child = $page->parent->child("name=$name"); // see if another page already has the same name
				$n++;
			} while($child->id); 
			$page->name = $name; 
		}
//...

So just set the title. If the same title appears multiple times, the API generates a name with -1,-2,-3... :)

  • Like 6
Link to comment
Share on other sites

  • 3 months later...

I tried to create a page via the API without a name and it displayed an error saying the name had to be set.  This is my solution based off of the the code Wanze posted.

/*
 *  Clean URL
 */
function clean_string($string)
{
    return trim(preg_replace('~[^0-9a-z]+~i', '-', html_entity_decode(preg_replace('~&([a-z]{1,2})(?:acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i', '$1', htmlentities($string, ENT_QUOTES, 'UTF-8')), ENT_QUOTES, 'UTF-8')), '-');
}

/*
 * Check for duplicate name
 */
function check_name ($title, $path) {
    $parent_page = wire("pages")->get($path);
    $n = 0;
    $pageName = strtolower(clean_string($title));
    do {
        $name = $pageName . ($n ? "-$n" : '');
        $child = $parent_page->child("name=$name"); // see if another page already has the same name
        $n++;
    } while($child->id);
    return $name;
}
Link to comment
Share on other sites

@kyle: Just tried creating pages via API in PW 2.2.9 and 2.3.1 and in both setting template, title and parent was enough, name wasn't needed.. and like @Wanze and others pointed out above that way PW automagically creates unique names. Then again, I can't really remember how this stuff worked in earlier versions, so perhaps it hasn't always been quite as simple.. :)

Anyway, even if setting name and making it unique yourself is necessary, I'd still suggest using built-in $sanitizer (in API context wire('sanitizer')) for sanitizing your page name instead of own custom code. This way you'll get page names that are 100% compatible with PW (and with less code.)

  • Like 1
Link to comment
Share on other sites

Teppo is right that all recent versions of PW will take care of creating a name for you, so long as you've at least set a title. It will also number-increment the name (i.e. "page-name-2") if one already exists. But if you want to go with a different way of setting the name, perhaps some other format, then it's always fine to set it yourself too. 

Link to comment
Share on other sites

Greetings,

I am preparing another forum post about different ways to deal with duplicate titles in front-end forms. But in the meantime...

What about appending the date/time onto the titles? That would ensure unique titles, and give you useful information right in the title. Of course, this would only be good in cases where you are not going to display the page to the public (which is the case here).

Thanks,

Matthew

  • Like 1
Link to comment
Share on other sites

What about appending the date/time onto the titles? That would ensure unique titles, and give you useful information right in the title. Of course, this would only be good in cases where you are not going to display the page to the public (which is the case here).

There are name collision possibilities with date/time (two being added at same exact time), but it's reasonably reliable other than that. ProcessWire actually uses microtime when creating pages internally that aren't named (or don't yet have a name), like the pages it creates for repeaters. I could see using the same method for unnamed pages anywhere.

Link to comment
Share on other sites

  • 2 years later...

I was using Soma's lovely example of how to create a page via the API and began AOK then started getting

Fatal error: Exception: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'mynewpage_url3-1075' for key 'name_parent_id' (in 
/wire/core/Pages.php line 2103) #0 
/wire/core/Pages.php(2103): PDOStatement->execute() #1 
/wire/core/Pages.php(1161): Pages->executeQuery(Object(PDOStatement)) #2 
/wire/core/Pages.php(1055): Pages->savePageQuery(Object(Page), Array) #3 [internal function]: Pages->___save(Object(Page), Array) #4 /index.php on line 248 

Then I found Wanze's super comment

but I think if you don't set a name, Pw takes already care of this problem.

Problem solved! :D Posted this just in case a Google search for that error helps someone else.

  • Like 1
Link to comment
Share on other sites

Another thing you can do is hook Pages::added (only called for new pages, not when re-saving existing ones) and in your hooked function set the page name to the page id, which is already part of the page at that point.

  • Like 2
Link to comment
Share on other sites

  • 5 years later...
On 7/14/2013 at 8:33 AM, teppo said:

@kyle: Just tried creating pages via API in PW 2.2.9 and 2.3.1 and in both setting template, title and parent was enough, name wasn't needed.. and like @Wanze and others pointed out above that way PW automagically creates unique names. Then again, I can't really remember how this stuff worked in earlier versions, so perhaps it hasn't always been quite as simple.. :)

Anyway, even if setting name and making it unique yourself is necessary, I'd still suggest using built-in $sanitizer (in API context wire('sanitizer')) for sanitizing your page name instead of own custom code. This way you'll get page names that are 100% compatible with PW (and with less code.)

Sorry for resurrecting this topic. But I think my question is related yet (not exactly the same). If I clone a page, set new title and save it under the target parent, that already has a page with a same name as generated from that title, the name doesn't receive an increment. But it rather is set to the name of the default page, that was cloned, with an increment.

Let me explain on example:

  1. I've got page with a name template under Templates parent page.
  2. Then I clone it, set it's name to an empty string, set some title (e.g. example) and save under another page Destination.
  3. The cloned page under Destination gets name example.
  4. Then I clone another page from template, set it the same title (example), and save it under Destination.
  5. I am hoping to see it receive a name example-2, but it is saved as template-2 instead.

I guess that is happening because when I clone a page it runs that setupNew method before I assign a title to it.

The question is: is there a way to use the same in-built logic for handling duplicate names when cloning pages?

 

Link to comment
Share on other sites

Several ways to do it:

$page_to_clone = pages(1051);
$destination_parent = pages(1016);
$new_title = 'Example';

$pages->clone($page_to_clone, $destination_parent, false, [
    'set' => [
        'title' => $new_title,
        'name' => ''
        ]
    ]);

////////////////

wire()->addHookAfter('Pages::cloneReady', function ($event) {
    $pages = $event->object;
    $pagesNames = $pages->names();
    $oldPage = $event->arguments(0);
    $newPage = $event->arguments(1);
    $sanitizer = $this->wire()->sanitizer;

    $newName = $sanitizer->pageNameTranslate($newPage->get('title'));
    $newPage->name = $pagesNames->uniquePageName($newName, $newPage->parent);
});

////////////////

$page_to_clone = pages(1051);
$destination_parent = pages(1016);
$new_title = 'Example';

$pages->clone($page_to_clone, $destination_parent, false, [
    'set' => [
        'title' => $new_title,
        'name' => $pages->names()->uniquePageName($sanitizer->pageNameTranslate($new_title), $destination_parent)
        ]
    ]);

 

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