cviebrock / eloquent-sluggable

Easy creation of slugs for your Eloquent models in Laravel
MIT License
3.89k stars 458 forks source link

Generate slug from the same field if explicitly modified #610

Open artrz opened 6 months ago

artrz commented 6 months ago

In some cases it is useful to be able to generate the slug from the same field. Let's say I have a form where I set a title and optionally the slug. If the slug is sent it's still important to process it to make sure there are no funky chars, it is unique, etc.

This could be implemented it by adding a new config setting like honorSelf (false as default) and update SlugService::buildSlug() to generate the slug from the $attribute. Something along the next lines:

    public function buildSlug(string $attribute, array $config, bool $force = null): ?string
    {
        $slug = $this->model->getAttribute($attribute);

        if ($config['honorSelf'] && $slug && $this->model->isDirty($attribute)) {
            return $this->obtainSlug($slug, $config, $attribute);
        }

        if ($force || $this->needsSlugging($attribute, $config)) {
            $source = $this->getSlugSource($config['source']);

            if ($source || is_numeric($source)) {
                $slug = $this->obtainSlug($source, $config, $attribute);
            }
        }

        return $slug;
    }

    // ...

    protected function obtainSlug(string $source, array $config, string $attribute): string
    {
        $slug = $this->generateSlug($source, $config, $attribute);
        $slug = $this->validateSlug($slug, $config, $attribute);
        $slug = $this->makeSlugUnique($slug, $config, $attribute);

        return $slug;
    }

I'm not sure about collateral effects. If there's interest into this, I could dig deeper.

cviebrock commented 6 months ago

I'm not sure I fully understand the use-case here.

If you have a form where you can optionally provide a slug, then you can handle funky characters, uniqueness, etc. with form validation, I would think.

artrz commented 6 months ago

Yes, front side validation is always desirable even when it can be bypassed. Still, slugs should always be generated as near as possible to the persisting action for the best match at least in my case. E.g. user A fills the slug field with 'x' on his form, user B fills the slug field with 'x' and sends the form, user A sends his form. Maybe user A sends his form first but has higher network latency. Maybe an API endpoint is being used where it's not possible to rely on the 3rd party validation implementation.

Not a deal breaker, a couple of years ago I solved this by extending the trait iirc, and in my latest implementation I ended up skipping the observer reactivity by returning an empty sluggableEvent (would be cool to have a SluggableObserver::NONE) and manually calling SlugService::createSlug(). For another project I might extend SlugService and bind my custom class with the code in OP to see how it goes. Btw, in SlugService::createSlug() seems that the is_array() check is not really neaded as the agument is typehinted. Also, I'm not sure that requiring a config for the attribute should be obligatory when using this method as the source is explicitly sent.

Feel free to close.