Browse documentationOpen

byrcsc/laravel-comments · 1.x

Authorization.

The engine never authorizes its own methods. Register the shipped policy, override the abilities that matter, and gate the call sites.

The package registers no policy, defines no gate, and never checks whether the current user may do what you just asked it to do.

That is deliberate. approve(), react(), pin(), and the rest work in a queued job, a seeder, and a console command, where there is no authenticated actor to ask about. Enforcement belongs where your application calls the engine.

Register the policy

CommentPolicy is the boilerplate every installing application would otherwise write. It is never registered for you:

use ByRcsc\LaravelComments\Models\Comment;
use ByRcsc\LaravelComments\Policies\CommentPolicy;
use Illuminate\Support\Facades\Gate;

// A service provider
Gate::policy(Comment::class, CommentPolicy::class);

Then gate the call site:

// A controller
$this->authorize('approve', $comment);
$comment->approve(by: $request->user());

The default abilities

AbilityDefault
createAny authenticated actor
updateThe comment's author
deleteThe comment's author
reactAny authenticated actor
attachAny authenticated actor
restoreNobody
forceDeleteNobody
approveNobody
rejectNobody
markAsSpamNobody
pinNobody
unpinNobody

In short: authors may edit and delete what they wrote, anyone signed in may react and attach, and nobody moderates until you say who does.

A few of those defaults are choices worth naming:

attach allows any authenticated actor, including on somebody else's comment. A moderator adding evidence to a reported comment is as ordinary as an author adding a screenshot to their own. Narrow it to authors by overriding.

restore denies everybody. Restoring is a moderator's undo, not an author's — a comment removed for a reason should not come back because the person who wrote it wants it to.

forceDelete denies everybody. It destroys a comment, its replies, its reactions, its revisions, and its attachment rows.

Ownership

update and delete both ask the comment whether the actor wrote it:

$comment->isBy($actor);

Both halves of the morph have to match, so a User and an Admin sharing a primary key are not the same author. A guest-authored comment matches nobody, which means ownership always denies for one: a later visitor claiming to be Jane has nothing to prove it with.

Override one ability

Extend the class and register yours instead. Everything you do not override keeps its default:

use ByRcsc\LaravelComments\Models\Comment;
use ByRcsc\LaravelComments\Policies\CommentPolicy;
use Illuminate\Database\Eloquent\Model;

final class AppCommentPolicy extends CommentPolicy
{
    public function approve(?Model $actor, Comment $comment): bool
    {
        return $actor?->getAttribute('is_moderator') === true;
    }

    public function reject(?Model $actor, Comment $comment): bool
    {
        return $this->approve($actor, $comment);
    }

    public function markAsSpam(?Model $actor, Comment $comment): bool
    {
        return $this->approve($actor, $comment);
    }
}
Gate::policy(Comment::class, AppCommentPolicy::class);

The actor is nullable throughout, so an unauthenticated visitor reaches these methods rather than being refused before them. That is what lets create be the one place deciding whether guests may write at all:

public function create(?Model $actor): bool
{
    return true;  // guests may comment
}

An application that does that owns its own rate limiting, which is the check that actually matters for anonymous writes and one this package deliberately does not ship.

No view ability

There is no view or viewAny here. Visibility is a query, not an ability: the approved() scope is what decides what a visitor reads. See moderation.

No roles, teams, or tenancy

The policy knows nothing about permission packages, roles, teams, or tenants, and it never will. Those are your application's, and the way to bring them in is the subclass above.