Skip to main content

Installation

Requirements​

  • PHP 8.4+
  • Laravel 13+ — 13.17+ only for the optional laravel/head integration, which is that package's own floor
  • PostgreSQL 18+, MySQL 8.4+, or SQLite
  • The bcmath or gmp extension, only if you use SqidsEncoder

The uniqueness guarantees are enforced natively on each engine — a functional partial unique index on PostgreSQL and SQLite, equivalent generated key columns on MySQL — and the full suite runs against all three. So Polyslug works on Laravel Cloud (serverless Postgres plus MySQL 8.4 LTS) with no extra configuration.

Install​

composer require pushery/polyslug-for-laravel

The service provider registers itself through package discovery.

Migrate​

Polyslug stores its slugs in a polyslug_slugs table whose migration is registered automatically, so a plain migrate is enough:

php artisan migrate

The package ships its migrations in order, and migrate applies all of them:

MigrationWhat it does
create_polyslug_slugs_tableThe three tables — polyslug_slugs (every slug a model has ever had), polyslug_tokens (the key-to-token store the RandomTokenEncoder uses) and polyslug_short_links (the /go tokens) — plus the unique indexes that carry the uniqueness guarantees.
add_enforce_unique_to_polyslug_slugsThe enforce_unique column behind shared slugs.
scope_polyslug_tokens_to_a_model_typeGives each morph type its own token space, so Page#1 and Wishlist#1 no longer share a token.
add_polyslug_slugs_resolution_indexThe index every incoming URL uses. Without it, resolution was a full table scan.
pin_polyslug_token_collationMySQL only: pins the token columns to a byte-exact collation, so a mixed-case alphabet keeps its entropy. A no-op on PostgreSQL and SQLite, which already compare byte-exactly.
reorder_polyslug_slugs_resolution_indexRebuilds the resolution index with the slug ahead of the scope, so a slug-only URL seeks on the slug whether or not the model names a scope. The new index is built before the old one is dropped.
add_retired_at_to_polyslug_slugsThe retired_at column behind retired slugs. Null on every existing row, so nothing changes until a slug is retired.
pin_polyslug_key_collationMySQL only: pins the columns that say which record a row belongs to and under which name, in all three tables, to a byte-exact collation. Every lookup then tells two names or two record keys apart the way the unique keys do: café from cafe, the scope Team from team, a record keyed Abc from one keyed abc. A no-op on PostgreSQL and SQLite, which already compare byte-exactly.
pin_polyslug_slug_collationPostgreSQL only, and only on a database whose locale folds an ASCII capital differently from PHP, as a Turkish or Azerbaijani one folds I to ı: pins the slug column to the C collation, so a slug that keeps its capitals with preserveCase collides and resolves the same way on every database. Rebuilds the indexes on the column once. A no-op everywhere else.
store_polyslug_times_as_datetime_on_mysqlMySQL only: turns the time columns of the three tables from TIMESTAMP into DATETIME, which holds a date after 2038-01-19 and is not converted through the session's time zone. Every value reads as it did before. Rewrites each table once. A no-op on PostgreSQL and SQLite.

The two optional tables stay empty and inert until you use the feature behind them. See the database reference.

To manage the migrations in your application instead of loading them from the package, publish them and disable the automatic registration in a service provider's register():

use Polyslug\PolyslugServiceProvider;

PolyslugServiceProvider::ignoreMigrations();

Publishing the package files​

Nothing has to be published to use Polyslug. Publish what you want to customize:

php artisan vendor:publish --tag=polyslug-config # config/polyslug.php
php artisan vendor:publish --tag=polyslug-migrations # the slug + token tables
php artisan vendor:publish --tag=polyslug # both at once

Polyslug ships no views and no translations — it routes and resolves, it renders nothing and emits no user-facing text — so there is nothing else to publish.

Every option in the published config/polyslug.php is documented inline; the same options are listed in the configuration reference.

Verify the setup​

php artisan polyslug:doctor

The command checks that the configured encoder is valid and that the uniqueness-guaranteeing indexes exist, and exits non-zero when they do not. See Diagnostics.

Next​