xetaio/xetaravel-mentions
A Laravel package for parsing and rendering @mentions in text, converting them into links and optionally notifying mentioned users. Includes a user trait, configurable mention syntax, events, and Blade helpers for easy integration.
composer require xetaio/xetaravel-mentions. As of 12.0.0 the package targets Laravel 12; the major version bump tracks the framework floor rather than an API break, so ^12.0 is the constraint to use on a Laravel 12 app (stay on 11.x for Laravel 11).config/mentions.php) and the migrations, then php artisan migrate. The migration creates the polymorphic mentions table that stores the parsed @handle → model mapping.HasMentions on the model that owns the text (e.g. Post, Comment).Mentionable on the model that can be mentioned (e.g. User).pool at your mentionable model (App\Models\User::class). This is the class the parser resolves handles against — if it's wrong, parsing silently yields zero mentions.@johndoe is saved, the package parses the field, resolves johndoe against the pool, inserts rows into mentions, and the configured notifier class fires per mentioned model. Start here — save a comment, assert a row in mentions, then assert the notification — before touching rendering or feeds.Where to look first: config/mentions.php (regex, pool, notifier, parser template), then the mentions table schema, then the trait methods on HasMentions.
mentions as a derived index of the raw text, not the source of truth. Call the trait's sync/parse method in a saved model event or explicitly in your form request/action so every write path (API, Nova, console seeder) stays consistent.@handles drop their rows. Appending on each save is the classic bug that leaves stale mentions and produces phantom notifications.parser template (a link pattern with the handle/URL) to turn raw text into HTML at render time. Keep the raw text in the column and linkify on output — do not store pre-linked HTML, or handle renames break old content.$user->mentions (morphMany, from Mentionable) is your "mentions of me" source. Eager load the mentioner (with('mentioner')) so an activity feed doesn't N+1, and paginate on the mentions query rather than on the parent models.User (e.g. Team) means handles must be unambiguous, or you need a prefixed syntax. Model the morph type in the config/parser and resolve by type.notifier at a queued notification class, exclude the author from recipients ($post->user_id), and let a Notification::fake() test cover the "author mentions self" case.id, name, url) from your resource, so clients can render mentions without re-implementing the regex.^11 and ^12 across a monorepo.mentions.php defines what counts as a mention. Changing it does not retroactively re-parse existing rows — write a one-off backfill job that re-syncs every owner model after any regex change.@handle regexes match email addresses ([email protected]) and mentions inside code fences. Tighten the pattern and/or strip code blocks before parsing if your text allows them.u flag and a compatible pattern.Mentionable, just doesn't create a row — no exception. Log or assert in tests when you expect a resolution.e() the raw body before injecting the parser's anchor HTML, or build the link around an escaped fragment. Never {!! $post->body !!} with a linkified string containing unescaped user input.deleted hook so deleting a post/comment removes its mentions rows; the polymorphic relation won't cascade for you on all drivers.pool, regex match against the literal text, the notifier binding in the container, and finally whether the sync method is actually called on that write path.Notification::fake() and a factory that creates mentions via the trait, so tests exercise the same parse path as production instead of inserting rows manually.How can I help you explore Laravel packages today?