Sluggable

← Sluggable

Ashterix

docs: Doctrine section with Doctrine Behaviors and Doctrine Helper (uk + en)

en/docs/doctrine/doctrine-behaviors/sluggable.md new file +74 −0

Sluggable

Generates a slug — a URL part like /post/introduction-to-php — from one or more entity fields.

Entity

use Doctrine\ORM\Mapping as ORM;
use Ufo\DoctrineBehaviors\Contract\Entity\SluggableInterface;
use Ufo\DoctrineBehaviors\Model\Sluggable\SluggableTrait;

#[ORM\Entity]
class BlogPost implements SluggableInterface
{
    use SluggableTrait;

    #[ORM\Id, ORM\GeneratedValue, ORM\Column]
    private ?int $id = null;

    #[ORM\Column]
    private string $title = '';

    /**
     * @return string[]
     */
    public function getSluggableFields(): array
    {
        return ['title'];
    }
}

The bundle maps slug (string, nullable) if the field is not mapped yet.

How it works

On prePersist and preUpdate generateSlug() is called:

  • the value of each field from getSluggableFields() is taken from the property, or, if there is none, from the get<Field>() method;
  • empty values are skipped, the rest are joined with a space and converted by Symfony\Component\String\Slugger\AsciiSlugger with the - delimiter and in lower case: Žluťoučký kůň → zlutoucky-kun;
  • if all values are empty — Ufo\DoctrineBehaviors\Exception\SluggableException.

By default the slug is regenerated on every entity update.

Uniqueness

By default uniqueness is not guaranteed. To enable it, override shouldGenerateUniqueSlugs():

public function shouldGenerateUniqueSlugs(): bool
{
    return true;
}

Then a -1, -2, … suffix is added to a taken slug: the check covers rows of the same class in the database (except the entity itself) and entities scheduled in the current flush().

What can be overridden

Trait method Default Purpose
shouldGenerateUniqueSlugs(): bool false Add a numeric suffix to a taken slug.
shouldRegenerateSlugOnUpdate(): bool true false — the slug is generated once and never changes.
getSlugDelimiter(): string - Word delimiter.
generateSlugValue(array $values): string AsciiSlugger Your own algorithm.

All these methods except shouldGenerateUniqueSlugs() are private in the trait — the entity simply declares its own method with the same name:

private function shouldRegenerateSlugOnUpdate(): bool
{
    return false;
}