Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Slug Field Type Laravel Package

anomaly/slug-field_type

Slug field type for the Anomaly Streams Platform. Adds an editable slug input that auto-generates URL-friendly, unique slugs from a source field, helping create clean, SEO-friendly URLs in Laravel-based Streams applications.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a Streams Platform field type addon — it does not work standalone. You need an existing Streams/PyroCMS install before it means anything.

  1. Install via Composer into a Streams-enabled project:

    composer require anomaly/slug-field_type
    

    The addon registers through its service provider and the composer.json extra.streams hints, so no manual providers array edit is normally required.

  2. Recompile the addon manifest so the platform sees it:

    php artisan addon:recompile
    # or, if you use the compiled cache:
    php artisan streams:compile
    
  3. In the admin, go to Fields → Create and pick Slug from the field type dropdown (its slug is anomaly.field_type.slug). The config form will ask for the source field (the field to slugify, e.g. title) and a separator.

  4. Assign the field to a stream (e.g. posts), then render it in a view:

    <a href="{{ url('blog/' ~ entry.slug) }}">{{ entry.title }}</a>
    

The first real use case is a posts/pages stream where you want a title → slug pair for pretty URLs. That is essentially the whole point of the package.

Implementation Patterns

Standard title/slug pair on a stream. Create a title text field and a slug field whose config points at title. In the admin form, typing in the title auto-fills the slug client-side. Pair this with a route that resolves by slug:

Route::get('blog/{slug}', function ($slug) {
    return app(\Anomaly\PostsModule\Post\Contract\PostRepositoryInterface::class)
        ->findBySlug($slug);
});

Enforcing uniqueness on save. The field type itself will not guarantee unique slugs, so either add a unique index on the column or hook the stream's save:

$post->slug = str_slug($post->title) . '-' . Str::random(4);

A more durable pattern is a small custom field type extending the slug one and overriding preSave()/fill() to de-duplicate before persistence.

Custom field type extension. Because it is a normal FieldType, you can extend it:

class ProjectSlugFieldType extends \Anomaly\SlugFieldType\SlugFieldType
{
    protected $slug = 'project_slug';
}

Register the new type in your own addon and you inherit the JS watcher and config UI.

Views and overrides. Publish the addon's views into your project to customise the input markup:

php artisan addon:publish anomaly/slug_field_type

Then edit resources/views/input.twig (or form.twig) in resources/{namespace}/slug-field_type/. Overriding the input view is the usual way to change the JS binding attribute if you use a nonstandard source field name.

API/import workflows. For seeded or API-created entries, set the slug explicitly in your payload — the client-side generator is not involved. A common pattern is to compute the slug in a form request or observer before passing data to the repository.

Gotchas and Tips

  • Slug generation is client-side. The package's headline behaviour is a JS watcher that mirrors the source field into the slug input. Anything created without a browser — Artisan seeders, API endpoints, imports — will store an empty slug unless you set it yourself. This is the single most common source of "why are my URLs blank" reports.
  • No uniqueness enforcement by default. Two posts with the same title produce the same slug. Add a database unique constraint and handle the QueryException, or de-duplicate in a preSave hook.
  • The source field must exist on the same stream. If you change the source field name in config after the fact, the JS watcher silently stops firing because it binds by field name. Re-check the field slug, not the label — labels are translated and change per locale.
  • Config key names matter. In stream definitions/migrations the config is nested under field_type options and uses keys like source and separator. A typo silently falls back to defaults rather than throwing, so verify with $field->getConfig('source') in a tinker session.
  • Caching. Streams aggressively caches compiled addon/field data. After installing, upgrading, or editing the addon's views, run php artisan cache:clear and php artisan streams:compile. A freshly installed field type that "doesn't show in the dropdown" is almost always a stale compile.
  • Locale and non-ASCII. Slugification of accented or non-Latin characters is basic; if you need proper transliteration, override the field type and use your own slugify implementation rather than relying on the bundled JS.
  • Empty slug in templates. Guard with {% if entry.slug %} in Twig; a missing slug renders as an empty string, producing broken links like /blog/ that are easy to miss in development.
  • Extension points. The useful ones are the input view, getConfig() for defaults, and preSave()/fill() for programmatic slug generation — these cover almost every real customisation need without forking.
  • Tiny package, small surface. With two stars and no dependents, treat it as a thin helper rather than a maintained slug engine. Pin the version in composer.json and be prepared to vendor/patch it if the platform's field type API moves.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager