laravel-comments maintained by newway-solutions
Laravel Comments
A Laravel package that provides a simple and flexible commenting system for your Laravel application. Add comments to any Eloquent model with support for nested replies, comment approval, and event-driven notifications.
Features
- 💬 Add comments to any Eloquent model
- 🔄 Nested comments and replies support
- ✅ Comment approval system
- 🎯 Polymorphic relationships
- 📡 Event-driven architecture
- 🔒 Flexible user authentication
- 🧪 Comprehensive test coverage
- 📦 Easy installation and configuration
Requirements
- PHP 8.1 or higher
- Laravel 10.0, 11.0, or 12.0
Installation
You can install the package via composer:
composer require newway-solutions/laravel-comments
Publish and Run Migrations
Publish the migration and run it:
php artisan vendor:publish --provider="NewWaySo\Comments\CommentsServiceProvider" --tag="migrations"
php artisan migrate
Publish Configuration (Optional)
You can publish the config file with:
php artisan vendor:publish --provider="NewWaySo\Comments\CommentsServiceProvider" --tag="config"
This will publish a config file to config/comments.php where you can customize the package behavior.
Configuration
Basic Configuration
The configuration file allows you to customize several aspects:
<?php
return [
/*
* The comment class that should be used to store and retrieve
* the comments.
*/
'comment_class' => \NewWaySo\Comments\Comment::class,
/*
* The user model that should be used when associating comments with
* commentators. If null, the default user provider from your
* Laravel authentication configuration will be used.
*/
'user_model' => null,
/**
* Determines if replies will be deleted when comments are deleted
*/
'delete_replies_along_comments' => false,
];
Usage
Preparing Your Models
Making Models Commentable
To allow a model to have comments, use the HasComments trait:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use NewWaySo\Comments\Traits\HasComments;
class Post extends Model
{
use HasComments;
// Your model code here...
}
Making Users Able to Comment
For users to be able to comment, implement the Commentator contract and use the CanComment trait:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use NewWaySo\Comments\Contracts\Commentator;
use NewWaySo\Comments\Traits\CanComment;
class User extends Authenticatable implements Commentator
{
use CanComment;
/**
* Check if a comment for a specific model needs to be approved.
*/
public function needsCommentApproval($model): bool
{
// Return false if the user's comments should be auto-approved
// Return true if the user's comments need manual approval
return false; // Auto-approve comments from this user
}
}
Basic Usage
Adding Comments
// Add a comment as the currently authenticated user
$post = Post::find(1);
$comment = $post->comment('This is a great post!');
// Add a comment as a specific user
$user = User::find(1);
$comment = $post->commentAsUser($user, 'This is a great post!');
// Add a comment without a user (anonymous comment)
$comment = $post->commentAsUser(null, 'Anonymous comment');
Retrieving Comments
$post = Post::find(1);
// Get all comments
$comments = $post->comments;
// Get only approved comments
$approvedComments = $post->comments()->approved()->get();
// Get comments with their commentators (users)
$commentsWithUsers = $post->comments()->with('commentator')->get();
Working with Nested Replies
$post = Post::find(1);
$comment = $post->comment('This is the main comment');
// Reply to a comment
$reply = $post->replyToComment($comment, 'This is a reply to the comment');
// Reply as a specific user
$user = User::find(1);
$reply = $post->replyToComment($comment, 'This is a reply', $user);
// Get replies for a comment
$replies = $comment->replies;
// Get parent comment of a reply
$parentComment = $reply->parent;
Comment Management
Approving and Disapproving Comments
$comment = Comment::find(1);
// Approve a comment
$comment->approve();
// Disapprove a comment
$comment->disapprove();
// Check if comment is approved
if ($comment->is_approved) {
// Comment is approved
}
Deleting Comments
$comment = Comment::find(1);
// Delete a comment
$comment->delete();
// If you want replies to be deleted along with the comment,
// set this in your config file:
// 'delete_replies_along_comments' => true,
Advanced Usage
Using Query Scopes
// Get only approved comments
$approvedComments = Comment::approved()->get();
// Get comments for a specific model
$post = Post::find(1);
$postComments = $post->comments()->approved()->latest()->get();
Accessing Comment Relationships
$comment = Comment::find(1);
// Get the model that was commented on
$commentedModel = $comment->commentable;
// Get the user who made the comment
$user = $comment->commentator;
// Get replies to this comment
$replies = $comment->replies;
// Get parent comment (if this is a reply)
$parent = $comment->parent;
Events
The package dispatches events when comments are created or deleted:
CommentAdded Event
<?php
namespace App\Listeners;
use NewWaySo\Comments\Events\CommentAdded;
class SendCommentNotification
{
public function handle(CommentAdded $event)
{
$comment = $event->comment;
// Send notification email
// Log the comment
// Update statistics
// etc.
}
}
CommentDeleted Event
<?php
namespace App\Listeners;
use NewWaySo\Comments\Events\CommentDeleted;
class HandleCommentDeletion
{
public function handle(CommentDeleted $event)
{
$comment = $event->comment;
// Clean up related data
// Update statistics
// Send notifications
// etc.
}
}
Register your listeners in EventServiceProvider:
protected $listen = [
\NewWaySo\Comments\Events\CommentAdded::class => [
\App\Listeners\SendCommentNotification::class,
],
\NewWaySo\Comments\Events\CommentDeleted::class => [
\App\Listeners\HandleCommentDeletion::class,
],
];
API Reference
HasComments Trait Methods
| Method | Description |
|---|---|
comments() |
Returns all comments for the model |
comment($comment) |
Add a comment as the authenticated user |
commentAsUser($user, $comment) |
Add a comment as a specific user |
replyToComment($parentComment, $comment, $user = null) |
Reply to an existing comment |
Comment Model Methods
| Method | Description |
|---|---|
approve() |
Approve the comment |
disapprove() |
Disapprove the comment |
commentable() |
Get the model that was commented on |
commentator() |
Get the user who made the comment |
parent() |
Get the parent comment (for replies) |
replies() |
Get all replies to this comment |
Query Scopes
| Scope | Description |
|---|---|
approved() |
Get only approved comments |
Database Structure
The package creates a comments table with the following structure:
- id (increments)
- commentable_id (unsignedBigInteger)
- commentable_type (string)
- comment (text)
- is_approved (boolean, default: false)
- user_id (unsignedBigInteger, nullable)
- parent_id (unsignedInteger, nullable)
- created_at (timestamp)
- updated_at (timestamp)
Examples
Complete Example: Blog with Comments
<?php
// Models
class Post extends Model
{
use HasComments;
protected $fillable = ['title', 'content'];
}
class User extends Authenticatable implements Commentator
{
use CanComment;
public function needsCommentApproval($model): bool
{
// Auto-approve comments from verified users
return !$this->email_verified_at;
}
}
// Controller
class PostController extends Controller
{
public function show(Post $post)
{
$post->load([
'comments' => function ($query) {
$query->approved()
->whereNull('parent_id') // Only top-level comments
->with(['commentator', 'replies.commentator'])
->latest();
}
]);
return view('posts.show', compact('post'));
}
public function storeComment(Request $request, Post $post)
{
$request->validate([
'comment' => 'required|string|max:1000',
'parent_id' => 'nullable|exists:comments,id'
]);
if ($request->parent_id) {
$parentComment = Comment::find($request->parent_id);
$comment = $post->replyToComment($parentComment, $request->comment);
} else {
$comment = $post->comment($request->comment);
}
return back()->with('success', 'Comment added successfully!');
}
}
Blade Template Example
{{-- Display comments --}}
@foreach($post->comments as $comment)
<div class="comment">
<div class="comment-header">
<strong>{{ $comment->commentator->name ?? 'Anonymous' }}</strong>
<small>{{ $comment->created_at->diffForHumans() }}</small>
</div>
<div class="comment-body">
{{ $comment->comment }}
</div>
{{-- Display replies --}}
@foreach($comment->replies as $reply)
<div class="reply">
<div class="reply-header">
<strong>{{ $reply->commentator->name ?? 'Anonymous' }}</strong>
<small>{{ $reply->created_at->diffForHumans() }}</small>
</div>
<div class="reply-body">
{{ $reply->comment }}
</div>
</div>
@endforeach
</div>
@endforeach
{{-- Comment form --}}
<form method="POST" action="{{ route('posts.comments.store', $post) }}">
@csrf
<div class="form-group">
<textarea name="comment" class="form-control" placeholder="Add a comment..."></textarea>
</div>
<button type="submit" class="btn btn-primary">Post Comment</button>
</form>
Testing
Run the tests with:
composer test
Contributing
Please see CONTRIBUTING for details.
Security
If you discover any security related issues, please email security@newway-solution.com instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see License File for more information.