beyondcode/laravel-comments
Add nested, approvable comments to any Laravel Eloquent model. Use a simple HasComments trait, create comments as the current user or on behalf of another user, and manage approval via an is_approved flag with migrations and config publishing included.
Installation:
composer require beyondcode/laravel-comments
The package auto-registers.
Publish Assets:
php artisan vendor:publish --provider="BeyondCode\Comments\CommentsServiceProvider" --tag="migrations"
php artisan vendor:publish --provider="BeyondCode\Comments\CommentsServiceProvider" --tag="config"
Run migrations:
php artisan migrate
Enable Comments on a Model:
Add the HasComments trait to your Eloquent model (e.g., Post):
use BeyondCode\Comments\Traits\HasComments;
class Post extends Model
{
use HasComments;
}
First Comment:
$post = Post::find(1);
$post->comment('This is my first comment!');
config/comments.php (e.g., delete_replies_along_comments, commentable_models).@comments for rendering in views (if using the included Blade helpers).CommentAdded/CommentDeleted for custom logic (e.g., notifications).Add Comments to a Blog Post:
// In a controller or Blade file:
$post = Post::find(1);
// Create a comment as the current user (if authenticated):
$post->comment('Great post!');
// Or as a specific user:
$post->commentAsUser($user, 'Thanks for sharing!');
// Display comments in Blade:
@comments($post)
@foreach($comments as $comment)
<div>{{ $comment->body }}</div>
@comments($comment) <!-- Nested replies -->
@endforeach
@endcomments
Comment Creation:
comment() (auto-associates with current user).
$post->comment('Default comment');
commentAsUser().
$post->commentAsUser($user, 'Comment as user');
$comments = ['Comment 1', 'Comment 2'];
foreach ($comments as $body) {
$post->comment($body);
}
Moderation:
is_approved via methods or query builder.
$comment->approve(); // Manually approve
$post->comments()->approved()->get(); // Fetch approved only
Commentator interface in your User model:
public function needsCommentApproval($model): bool {
return $model instanceof Post; // Only auto-approve for Posts
}
Nested Replies:
$comment = Comment::find(1);
$comment->commentAsUser($user, 'Reply to comment');
config/comments.php:
'delete_replies_along_comments' => true,
Querying:
withComments() on parent models.
$posts = Post::withComments()->get();
$post->comments()->approved()->latest()->take(10)->get();
@comments directive for nested rendering:
@comments($post)
@foreach($comments as $comment)
<div>{{ $comment->body }}</div>
@comments($comment) <!-- Recursive replies -->
@endforeach
@endcomments
CommentAdded/CommentDeleted:
use BeyondCode\Comments\Events\CommentAdded;
CommentAdded::subscribe(function ($event) {
// Send notification, log, etc.
});
return new CommentResource($post->comments()->approved()->get());
CommentFactory (if available) or seed test data:
$post = Post::factory()->create();
$post->comment('Test comment');
Custom Validation:
Override the validateComment method in your model:
use BeyondCode\Comments\Contracts\CommentValidator;
class Post extends Model implements CommentValidator
{
public function validateComment($body, $user = null): array
{
return ['body' => ['required', 'max:500']];
}
}
Storage Engines:
Extend the Comment model to support alternative storage (e.g., Redis):
class RedisComment extends Comment
{
public function save(array $options = [])
{
// Custom logic
}
}
Multi-Tenancy: Use Laravel’s global scopes or middleware to filter comments by tenant:
class TenantScope implements \Illuminate\Database\Eloquent\Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->where('tenant_id', auth()->tenant()->id);
}
}
Migration Conflicts:
comments table, merge the published migration carefully. The package assumes default column names (e.g., commentable_id, commentable_type).Polymorphic Relationships:
commentable_type column uses Laravel’s polymorphic naming (App\Models\Post). Ensure your models are fully qualified (e.g., avoid Post vs. App\Models\Post mismatches).Auto-Approval Logic:
needsCommentApproval method is called after comment creation. Return true to mark as unapproved, false to auto-approve. Test edge cases (e.g., null models).Reply Deletion:
delete_replies_along_comments to true triggers cascading deletes. Test performance with deep reply trees (e.g., 10+ levels).Blade Directives:
@comments directive assumes the comments relationship exists. If using custom relationship names, override the directive or use the raw relationship:
@foreach($post->getComments as $comment)
Missing Comments:
commentable_type and commentable_id match your model’s namespace and ID.SoftDeletes on the commentable model.Approval Not Working:
is_approved column exists (published migration includes it).needsCommentApproval method with dd($model, $user) to confirm logic.Nested Comments Not Rendering:
Comment model uses HasComments (it does by default).@comments($comment, 3) <!-- Max 3 levels deep -->
Performance Issues:
withComments sparingly on large datasets. Consider lazy-loading or pagination:
$post->load(['comments' => function ($query) {
$query->approved()->latest()->take(20);
}]);
Custom Comment Models:
Extend the Comment model to add fields (e.g., rating):
class ExtendedComment extends Comment
{
protected $casts = ['rating' => 'integer'];
}
Update the migration and config accordingly.
Custom Validation:
Override the validateComment method in your commentable model (as shown above).
Custom Storage:
Replace the Comment model’s table or use a custom repository:
class CommentRepository
{
public function create(array $data)
{
// Custom logic (e.g., Redis, database)
}
}
Events:
Subscribe to CommentAdded/CommentDeleted for side effects:
CommentAdded::subscribe(function ($event) {
// Send email, update analytics, etc.
});
commentable_models:
The config allows whitelisting models that can receive comments. If empty, all models with HasComments are allowed. Useful for security:
'commentable_models' => [
'App\Models\Post',
'App\Models\Article',
],
delete_replies_along_comments:
Defaults to false. Set to true to enable casc
How can I help you explore Laravel packages today?