<p>I finally use this simple solution, which works pretty fine:</p>

<pre><code>Event::on(
    Element::class,
    Element::EVENT_BEFORE_SAVE,
    function(craft\events\ModelEvent $event) {
        $entry = $event-&gt;sender;
        if (!\craft\helpers\ElementHelper::isDraftOrRevision($entry)) {
            if (!empty($entry-&gt;id)) {
                $entry-&gt;slug = $entry-&gt;title;
            }
        }
    },
    false
);
</code></pre>

<p>Where <code>$entry-&gt;slug = $entry-&gt;title</code> syncs the slug to the title if the entry is not new (<code>!empty($entry-&gt;id)</code>).</p>

<p><strong>UPDATE:</strong></p>

<pre><code>$entry-&gt;slug = ($entry-&gt;title !== null) ? \craft\helpers\StringHelper::slugify($entry-&gt;title) : $entry-&gt;slug;
</code></pre>

<p>This prevents to get a wrong slug on first save.</p>

CC BY-SA 4.0
2019-10-02T08:32:15.537Z
Due to the draft changes, this is the right approach for Craft 3.2 and later.
Fred Carlsen
CC BY-SA 4.0
2019-09-23T10:58:13.797Z
2019-10-02T08:35:50.137Z
2019-10-02T08:35:50.137Z
Romain P.
<p>You can change the value in the DB directly instead of using the <code>Elements</code> service</p>

<pre class="lang-PHP prettyprint-override"><code>$entry = $event-&gt;element;
$elementId = $entry-&gt;id;
$siteId = $entry-&gt;siteId;
Craft::$app-&gt;getDb()-&gt;createCommand()
    -&gt;update(
        '{{%elements_sites}}',
        ['slug' =&gt; ElementHelper::createSlug('new-slug')],
        [
            'elementId' =&gt; $elementId,
            'siteId'    =&gt; $siteId
        ]
    )-&gt;execute();
</code></pre>

<p>By the way the following condition <code>[] !== $entry</code> won't be any good when you fetch an element with <code>one()</code> because <code>one</code> returns an object of instance <code>ElementInterface</code> or <code>null</code> but never an empty array. You can check <code>!== null</code> instead and if you fetch things via <code>all</code> you can perform an empty check <code>if( empty($entries) === false)</code></p>

CC BY-SA 4.0
2019-02-13T13:28:19.447Z
Awesome, thank you! I had never thought of the DB way. Thank you also for the other explanations and advice, I really appreciate.
Romain P.
CC BY-SA 4.0
2019-02-13T12:51:23.680Z
2019-02-13T12:56:38.673Z
2019-02-13T12:56:38.673Z
Robin Schambach
<p>Another shorter and probably slightly more performant solution, using EVENT_BEFORE_SAVE:</p>

<pre><code>/*
* Automatically update Element slugs to match the title if updated.
*/
Event::on(
    Element::class,
    Element::EVENT_BEFORE_SAVE,
    function(craft\events\ModelEvent $event) {
        $entry = $event-&gt;sender;
        $entry-&gt;slug = ElementHelper::createSlug($entry-&gt;title);
    },
    false
);
</code></pre>

CC BY-SA 4.0
2019-04-29T15:53:40.587Z
This solution is not working, you should review the suggested code.
Romain P.
CC BY-SA 4.0
2019-04-29T16:57:33.700Z
Works fine for me (it's tested and being used). Did you actually try it?
Udo
CC BY-SA 4.0
2019-04-29T17:03:44.300Z
@RomainPoirier it will not work with EVENT_AFTER_SAVE_ELEMENT but it does with EVENT_BEFORE_SAVE_ELEMENT
Udo
CC BY-SA 4.0
2019-04-30T08:10:52.627Z
Yep, I tried but but too quickly, I'm sorry. I was missing the loading of `craft\base\Element` and `craft\helpers\ElementHelper`.
Would you use `EVENT_BEFORE_SAVE_ELEMENT` instead of `EVENT_BEFORE_SAVE`? I can't find official reference of `EVENT_BEFORE_SAVE` in the Craft 3 docs.
If you know, can you explain the utility of `false` at the end? Thank you!
Romain P.
CC BY-SA 4.0
2019-09-23T09:23:29.443Z
Since recent Craft updates, `$entry->slug = ElementHelper::createSlug($entry->title);` doesn't works as expected: the slug is sometimes saved as empty. Delete `$entry->slug =` and only keep `ElementHelper::createSlug($entry->title);` seems to fix it.
Romain P.
CC BY-SA 4.0
2019-04-29T15:42:49.530Z
2019-04-29T19:43:48.320Z
2019-04-29T19:43:48.320Z
Udo
<p>My entries' titles are auto-generated from several fields values on first save.</p>

<p>When one of these fields values change, I would like to update the auto-generated slug also:</p>

<pre class="lang-php prettyprint-override"><code>Event::on(
    \craft\services\Elements::class,
    \craft\services\Elements::EVENT_AFTER_SAVE_ELEMENT,
    function (Event $event) {
        $entry = Entry::find()
            -&gt;id($event-&gt;element-&gt;id)
            -&gt;one();

        if ([] !== $entry) {
            $title = craft\helpers\StringHelper::toAscii($entry-&gt;id.'-'.$entry-&gt;title);
            $title = craft\helpers\StringHelper::toLowerCase($title);
            $title = str_replace(' ', '-', $title);

            //  I tried this below, but none works
            $entry-&gt;slug = $title; // ⬅️ Not working
            $entry-&gt;setFieldValue('slug', $title); // ⬅️ Error
            Craft::$app-&gt;elements-&gt;saveElement($entry); // ⬅️ Infinite loop
            $event-&gt;element-&gt;slug = $event-&gt;element-&gt;id; // ⬅️ Not working
        }
    }
);
</code></pre>

<p>Unfortunately, none of these methods works. Any idea how to achieve this?</p>

<events><slug><modules><onsaveentry>
How to change an entry's slug on EVENT_AFTER_SAVE_ELEMENT?
# How to change an entry's slug on EVENT_AFTER_SAVE_ELEMENT?

(Asked by: username_0 on 2019-02-13)

My entries' titles are auto-generated from several fields values on first save.
When one of these fields values change, I would like to update the auto-generated slug also:
Event::on(
    \craft\services\Elements::class,
    \craft\services\Elements::EVENT_AFTER_SAVE_ELEMENT,
    function (Event $event) {
        $entry = Entry::find()
            ->id($event->element->id)
            ->one();

        if ([] !== $entry) {
            $title = craft\helpers\StringHelper::toAscii($entry->id.'-'.$entry->title);
            $title = craft\helpers\StringHelper::toLowerCase($title);
            $title = str_replace(' ', '-', $title);

            //  I tried this below, but none works
            $entry->slug = $title; // ⬅️ Not working
            $entry->setFieldValue('slug', $title); // ⬅️ Error
            Craft::$app->elements->saveElement($entry); // ⬅️ Infinite loop
            $event->element->slug = $event->element->id; // ⬅️ Not working
        }
    }
);

Unfortunately, none of these methods works. Any idea how to achieve this?

**Tags:** events, slug, modules, onsaveentry


---

## 3 answers

### Answer by username_0 on 2019-09-23. Score: 1 | Accepted answer

I finally use this simple solution, which works pretty fine:
Event::on(
    Element::class,
    Element::EVENT_BEFORE_SAVE,
    function(craft\events\ModelEvent $event) {
        $entry = $event->sender;
        if (!\craft\helpers\ElementHelper::isDraftOrRevision($entry)) {
            if (!empty($entry->id)) {
                $entry->slug = $entry->title;
            }
        }
    },
    false
);

Where $entry->slug = $entry->title syncs the slug to the title if the entry is not new (!empty($entry->id)).
UPDATE:
$entry->slug = ($entry->title !== null) ? \craft\helpers\StringHelper::slugify($entry->title) : $entry->slug;

This prevents to get a wrong slug on first save.

> **Comments:**
> **username_1**: Due to the draft changes, this is the right approach for Craft 3.2 and later.  

---

### Answer by username_2 on 2019-02-13. Score: 4

You can change the value in the DB directly instead of using the Elements service
$entry = $event->element;
$elementId = $entry->id;
$siteId = $entry->siteId;
Craft::$app->getDb()->createCommand()
    ->update(
        '{{%elements_sites}}',
        ['slug' => ElementHelper::createSlug('new-slug')],
        [
            'elementId' => $elementId,
            'siteId'    => $siteId
        ]
    )->execute();

By the way the following condition [] !== $entry won't be any good when you fetch an element with one() because one returns an object of instance ElementInterface or null but never an empty array. You can check !== null instead and if you fetch things via all you can perform an empty check if( empty($entries) === false)

> **Comments:**
> **username_0**: Awesome, thank you! I had never thought of the DB way. Thank you also for the other explanations and advice, I really appreciate.  

---

### Answer by username_3 on 2019-04-29. Score: 2

Another shorter and probably slightly more performant solution, using EVENT_BEFORE_SAVE:
/*
* Automatically update Element slugs to match the title if updated.
*/
Event::on(
    Element::class,
    Element::EVENT_BEFORE_SAVE,
    function(craft\events\ModelEvent $event) {
        $entry = $event->sender;
        $entry->slug = ElementHelper::createSlug($entry->title);
    },
    false
);

> **Comments:**
> **username_0**: This solution is not working, you should review the suggested code.  
> **username_3**: Works fine for me (it's tested and being used). Did you actually try it?  
> **username_3**: @RomainPoirier it will not work with EVENT_AFTER_SAVE_ELEMENT but it does with EVENT_BEFORE_SAVE_ELEMENT  
> **username_0**: Yep, I tried but but too quickly, I'm sorry. I was missing the loading of `craft\base\Element` and `craft\helpers\ElementHelper`. Would you use `EVENT_BEFORE_SAVE_ELEMENT` instead of `EVENT_BEFORE_SAVE`? I can't find official reference of `EVENT_BEFORE_SAVE` in the Craft 3 docs. If you know, can you explain the utility of `false` at the end? Thank you!  
> **username_0**: Since recent Craft updates, `$entry->slug = ElementHelper::createSlug($entry->title);` doesn't works as expected: the slug is sometimes saved as empty. Delete `$entry->slug =` and only keep `ElementHelper::createSlug($entry->title);` seems to fix it.  

---

