NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

Comment bodies are scanned for @handle tokens on write and edit. Handles are always stored as CommentMention rows; set a resolver to link them to models and fire CommentMentioned. The resolver is a class, not a closure — php artisan config:cache cannot store a closure:

// app/Mentions/ResolveMention.php
namespace App\Mentions;

use App\Models\User;

final class ResolveMention
{
    public function __invoke(string $handle): ?User
    {
        return User::where('username', $handle)->first();
    }
}

// config/comments.php — an invokable class, or [ResolveMention::class, 'someMethod']
'mention_resolver' => \App\Mentions\ResolveMention::class,

$comment = Comments::on($post)->body('thanks @alice!')->post();
$comment->mentions; // CommentMention rows (handle + optional mentionable)
  • A handle is @ followed by letters, digits, _, . or - (@john.doe), but it ends on a letter, digit or underscore — so “thanks @alice.” mentions alice. It must not follow a word character, so an email address like [email protected] is not a mention.
  • The resolver receives the handle without the @ and returns an Eloquent model or null. It is built through the container, so it may take constructor dependencies.

When CommentMentioned fires

  • Only for an approved comment, once per person. A comment the moderation layer holds back — require_approval, a blocklist pending or hidden match, a moderator’s hide — notifies nobody until Comments::approve(); then each resolved mention fires once.
  • Each handle is stored once per comment. An edit fires it only for people the comment has not notified yet — handles still in the body keep their rows, removed ones are deleted, and re-mentioning the same person, even under another handle, is not a new mention.
  • A hide-and-approve round trip never notifies anyone twice.

A [class, method] resolver

Instead of an invokable class, point the resolver at a [Class::class, 'method'] pair. The class is built through the container too, so the pair stays config:cache-safe:

// config/comments.php
'mention_resolver' => [App\Support\MentionResolver::class, 'resolve'],

// app/Support/MentionResolver.php — built through the container
namespace App\Support;

use App\Models\User;

final class MentionResolver
{
    public function resolve(string $handle): ?User
    {
        return User::where('username', $handle)->first();
    }
}

Notifying mentioned users

use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Comments\Events\CommentMentioned;

Event::listen(function (CommentMentioned $event): void {
    $event->mention->handle;       // 'alice'
    $event->mention->mentionable;  // the resolved model (e.g. your User)
    $event->mention->comment;      // the Comment that mentions them
});

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.