Skip to content

Latest commit

 

History

History
178 lines (164 loc) · 6.83 KB

Slugged.md

File metadata and controls

178 lines (164 loc) · 6.83 KB

Slugged Behavior

A CakePHP behavior to automatically create and store slugs.

  • Input data can consist of one or many fields
  • Slugs can be unique and persistent, ideal for lookups by slug
  • Multibyte aware, umlauts etc will be properly replaced

Configs

Key Default Description
label `null`
  • set to the name of a field to use for the slug
  • an array of fields to use as slugs
  • or leave as null to rely on the format returned by find('list') to determine the string to use for slugs
field `'slug'` The slug field name
overwriteField 'overwrite_slug' The boolean field/property to trigger overwriting if "overwrite" is false
mode `'url'`
  • ascii: returns an ASCII slug generated using the core Inflector::slug() function
  • display: a dummy mode which returns a slug legal for display - removes illegal (not unprintable) characters
  • url: returns a slug appropriate to put in a URL
  • class: a dummy mode which returns a slug appropriate to put in a HTML class (there are no restrictions)
  • id: returns a slug appropriate to use in a HTML id
  • {callable}: Use your custom callable to pass in your slugger method
separator `-` The separator to use
length `null` Set to 0 for no length. Will be auto-detected if possible via schema.
overwrite `false` has the following values
  • false: Once the slug has been saved, do not change it (use if you are doing lookups based on slugs)
  • true: If the label field values change, regenerate the slug (use if the slug is just window-dressing). Note: For multi-field labels, all fields are required to be present once one label field has been touched (dirty set to true).
unique `false` has the following values
  • false: will not enforce a unique slug, whatever the label is is directly slugged without checking for duplicates
  • true: use if you are doing lookups based on slugs (see overwrite)
case `null` has the following values
  • null don't change the case of the slug
  • low force lower case. E.g. "this-is-the-slug"
  • up force upper case E.g. "THIS-IS-THE-SLUG"
  • title force title case. E.g. "This-Is-The-Slug"
  • camel force CamelCase. E.g. "ThisIsTheSlug"
replace see code Custom replacements as array. `Set to null` to disable.
on `'beforeRules'` `beforeSave` or `beforeMarshal` or `beforeRules`.
scope `[]` Certain conditions to use as scope.
tidy `true` If cleanup should be run on slugging.

Usage

Attach it to your models in initialize() like so:

$this->addBehavior('Tools.Slugged');

Examples

Persistent slugs

We want to store categories and we need a slug for nice SEO URLs like /category/[slugname]/.

$this->addBehavior('Tools.Slugged',
    ['label' => 'name', 'unique' => true, 'mode' => 'ascii']
);

Upon creating and storing a new record it will look for content in "name" and create a slug in "slug" field.

With the above config on "edit" the slug will not be modified if you alter the name. That is important to know. You cannot just change the slug, as the URL is most likely indexed by search engines now.

If you want to do that, you would also need a .htaccess rewrite rule to 301 redirect from the old to the new slug. So if that is the case, you could add an "overwrite field" to your form.

echo $this->Form->field('overwrite_slug', ['type' => 'checkbox']);

Once that boolean checkbox is clicked it will then perform the slug update on save.

Non persistent slugs

If we just append the slug to the URL, such as /category/123-[slugname], then we don't need to persist the slug.

$this->addBehavior('Tools.Slugged',
    ['label' => 'name', 'overwrite' => true, 'mode' => 'ascii', 'unique' => true]
);

Note that we don't need "unique" either then.

Each save now re-triggers the slug generation.

Using a custom slugger

You can pass your own callable for slugging into the mode config. And you can even use a static method on any class this way (given it has a static slug() method):

$this->addBehavior('Tools.Slugged', ['mode' => [MySlugger::class, 'slug']]);

Tip: Use 'mode' => [Text::class, 'slug'] if you want to avoid using the deprecated Inflector::slug() method. Don't forget the use statement at the top of the file, though (use Tools\Utility\Text;).

Using custom finder

If you quickly want to find a record by its slug, use:

->find()->find('slugged', slug: $slug)->firstOrFail();

etc