Changeslug does not refresh the url in cms (with database)

I’m working with the code from this great tutorial from the website.

I have 1 problem. When i change the slug of a comment item, the slug is correctly updated in the database, but the url in the cms does not change. So if you do a change on that page you get an error.

Is there a way i can fix this?

(working with Kirby 3.3.5)

In the Panel or frontend?

Hi Texnixe,

It’s in the cms.

1/ i create a comment item ‘mycomment’. Browse-url is:

Schermafbeelding 2020-04-04 om 18.46.10

2/ I change the slug to ‘updatedcomment’.

3/ I change a field on that page and then you get an error because the url is not changed to the new slug.

Hi Texnixe,
Do you think this is a bug in Kirby?

After changing the slug, are you redirected to the new URL?

No, you are not redirected to the new URL.

What do you do in your changeSlug() model method?

 public function changeSlug(string $slug, string $languageCode = null)
    {
        // always sanitize the slug
        $slug = Str::slug($slug);
        $data['slug'] = $slug;
        if ($comment = Db::first('comments', '*', ['slug' => $this->slug()])) {
            if (Db::update('comments', $data, ['slug' => $this->slug()])) {
                return $this;
            };
        } 
        return $this;
    }

Hm, there’s a lot more stuff in the original method (removing the old page from the parent model etc), so I wonder if something is missing here.

This is the code that’s used in the example of the kirby site… :thinking:

Oh, ok :blush:. Then it should have worked at one point in time when I tested it. Are you in a multi-lang context maybe?

I have 1 default language. But when i remove this and test it without any languages it doesn’t work either.

Ok, I’ll test myself as soon as I get round to it.

1 Like

Ok, the changeSlug() method in the current documentation doesn’t make sense. We have to base it on the original changeSlug() method, and I got to the point where the URL is actually changed but still getting an error message. So some more playing around ahead…

I came up with this, which seems to work:

public function changeSlug(string $slug, string $languageCode = null)
  {
    // always sanitize the slug
    $slug = Str::slug($slug);
 

    // if the slug stays exactly the same,
    // nothing needs to be done.
    if ($slug === $this->slug()) {
      return $this;
    }

    return $this->commit('changeSlug', [$this, $slug, $languageCode = null], function ($oldPage, $slug){
      

      $newPage = Page::factory([
        'slug' => $slug,
        'parent' => $oldPage->parent()
      ]);
      if (Db::first('comments', '*', ['slug' => $oldPage->slug()])) {
   
        // update the database
        $data['slug'] = $slug;
        if (Db::update('comments', $data, ['slug' => $oldPage->slug()]) === false) {
          throw new LogicException('The comment could not be updated');
        };


        // remove from the siblings
        $oldPage->parentModel()->children()->remove($oldPage);

      }

      // overwrite the new page in the parent collection
      if ($newPage->isDraft() === true) {
        $newPage->parentModel()->drafts()->set($newPage->id(), $newPage);
      } else {
        $newPage->parentModel()->children()->set($newPage->id(), $newPage);
      }

      return $newPage;
    });
  }

Please let me know if this works for you as well, I will then update the docs.

1 Like

Yes! This works! You are the best! :wink:

Hi, I upgrade a project to kirby 4.2. But the code about (changeslug) doesn’t seem to work anymore.
This is my error:
UserPage::changeSlug(): Return value must be of type UserPage, Kirby\Cms\Page returned
Do you know what i need to adjust?
Thanks a lot!

Any method that overrides a parent class method needs to have the same signature, i.e. you need to add a return type hint, check out the original method.

I changed the $newPage variable creation in the code above like this. Now it works! Thanks

OLD
$newPage = Page::factory([
   'slug' => $slug,
  'parent' => $oldPage->parent()
 ]);

NEW   
$newPage = $oldPage->clone([
     'slug'   => $slug,
     'dirname' => null,
     'root'    => null
]);

Hi, I bumped on a new problem with the isDraft function

public function isDraft(): bool
    {
        return in_array($this->content()->status(), ['listed', 'unlisted']) === false;
    }

This gives a problem when creating a new item via panel.
Then I cannot call the $this->content(). I tried to change it like this but it is still not working. Any ideas?

public function isDraft(): bool
    {
        $bo = null !== $this->content()->status() ; 
        if($bo){
          return in_array($this->content()->status(), ['listed', 'unlisted']) === false;       
        }else{
            return true;
        }
}