TL;DR: I released a CakePHP 5.x plugin that validates user-input URL slugs against reserved words and application routes to prevent collisions.

elstc/cakephp-slug-guard: CakePHP plugin for URL-safe slug validation and reserved-word collision prevention


SlugGuard - CakePHP Plugin

What Does This Plugin Do?

In web applications, you often want to let users choose their own profile URL slug – something like example.com/nojimage.

But if a user registers a slug like admin, login, or api, it collides with your application’s routes and causes serious problems.

SlugGuard is a CakePHP 5.x plugin that prevents these slug collisions. It provides three validation rules:

  • SlugValidator – Format check (lowercase alphanumerics and hyphens only, must start and end with alphanumeric)
  • IsNotReservedSlug – Checks against approximately 710 reserved words (admin, login, api, settings, etc.)
  • IsNotRouteConflict – Dynamically checks against routes registered in CakePHP

Installation

composer require elstc/cakephp-slug-guard

bin/cake plugin load Elastic/SlugGuard

After loading the plugin, run the migration to create the reserved words table.

bin/cake migrations migrate --plugin Elastic/SlugGuard
bin/cake slug_guard sync

The sync command imports approximately 710 built-in reserved words into the database. These cover common terms from admin, dashboard, login, api to social media names like facebook, twitter, and github.

Usage

Reserved Word & Route Conflict Check (Application Rule)

Add IsNotReservedSlug and IsNotRouteConflict in your table class’s buildRules method.

use Elastic\SlugGuard\Model\Rule\IsNotReservedSlug;
use Elastic\SlugGuard\Model\Rule\IsNotRouteConflict;

// In your Table class
public function buildRules(RulesChecker $rules): RulesChecker
{
    $rules->add(new IsNotReservedSlug('slug'), 'reservedSlug', [
        'errorField' => 'slug',
        'message' => 'This slug is reserved',
    ]);
    $rules->add(new IsNotRouteConflict('slug'), 'routeConflict', [
        'errorField' => 'slug',
        'message' => 'This slug conflicts with an existing route',
    ]);

    return $rules;
}

Pass the field name to check as the constructor argument. You can use it for fields other than slug – for example, username.

Slug Format Check (Validator)

SlugValidator is used as a Validator provider. It allows only lowercase alphanumerics and hyphens, and also enforces length constraints.

use Elastic\SlugGuard\Validation\SlugValidator;

// In your Table class
public function validationDefault(Validator $validator): Validator
{
    $validator->setProvider('slugValidator', SlugValidator::class);
    $validator->add('slug', 'validSlug', [
        'rule' => ['isValid'],
        'provider' => 'slugValidator',
        'message' => 'Only lowercase alphanumerics and hyphens are allowed',
    ]);

    return $validator;
}

The default length is 4 to 24 characters, but you can customize it.

// Change to minimum 3, maximum 32 characters
$validator->add('slug', 'validSlug', [
    'rule' => ['isValid', 3, 32],
    'provider' => 'slugValidator',
]);

Managing Reserved Words

You can add, remove, and list reserved words using CLI commands.

# List reserved words
bin/cake slug_guard list

# Search
bin/cake slug_guard list --search admin

# Add a reserved word
bin/cake slug_guard add my-reserved-word

# Remove a reserved word
bin/cake slug_guard remove my-reserved-word

# Import from file (one slug per line, # for comments)
bin/cake slug_guard import path/to/custom-slugs.txt

# Extract routes from your application and add as reserved words
bin/cake slug_guard routes | bin/cake slug_guard add

# Sync config file with database (supports dry-run)
bin/cake slug_guard sync --dry-run

Design Highlights

Dynamic Route Conflict Detection

IsNotRouteConflict dynamically retrieves the first segment of routes registered in CakePHP and checks for collisions. When you change your routing configuration, it automatically picks up the changes – no manual maintenance needed.

Database-Managed Reserved Words

Reserved words are stored in the reserved_slugs table. The slug string itself is used as the primary key, making existence checks fast. In addition to CLI add/remove commands, it supports file imports and stdin input for operational flexibility.


Aside

I’ve had this mechanism for quite a while and needed it in many real projects, but never got around to extracting it as a plugin. Thanks to AI agents, I was able to quickly handle the code cleanup and documentation needed to release it as a plugin, and finally got it published.

For this plugin specifically, the CLI command implementation was almost entirely handled by the AI agent.

So, when building an application that lets users freely input slugs, reserved word checking is a subtle but important concern – forgetting it leads to headaches later. With SlugGuard, format checking, reserved word checking, and route conflict checking are all handled in one place, so you can implement slug input features with confidence.

It works with CakePHP 5.x + PHP 8.2 or later. Give it a try!

References