Installation:
composer require alibayat/laravel-commentable
php artisan vendor:publish --provider="AliBayat\LaravelCommentable\CommentableServiceProvider"
php artisan migrate
Verify the comments and commentable tables exist in your database.
Enable on a Model:
Add the Commentable trait to your Eloquent model (e.g., Post.php):
use AliBayat\LaravelCommentable\Commentable;
class Post extends Model
{
use Commentable;
}
First Use Case: Create a comment on a model instance:
$post = Post::find(1);
$user = User::find(1);
$post->comment(['body' => 'First comment'], $user);
Comment Creation:
$model->comment($data, $user); // $data = ['body' => '...', 'title' => '...']
$parentComment = Comment::find(1);
$model->reply($data, $user, $parentComment);
collect() to attach multiple comments at once.Querying Comments:
$comments = $model->comments()->with('user')->get();
$replies = $comment->replies()->with('user')->get();
$model->comments()->where('user_id', $user->id)->approved()->get();
Moderation:
$comment->approve(); // or $comment->reject()
$comment->delete(); // Uses Laravel's soft-deletes
Event Listeners:
CommentCreated, CommentApproved):
use AliBayat\LaravelCommentable\Events\CommentCreated;
CommentCreated::listen(function ($comment) {
// Send notification, log, etc.
});
apiResources to structure comment responses:
$comment = $model->comments()->with('user', 'replies.user')->first();
validateComment method in your model:
public function validateComment($data)
{
return validator($data, [
'body' => 'required|min:10',
'title' => 'nullable|max:255',
]);
}
CommentCreated):
event(new CommentCreated($comment));
Migration Conflicts:
comments table, drop it before publishing migrations to avoid conflicts.commentable_id and commentable_type columns in the comments table (polymorphic relations).Soft Deletes:
Comment model uses SoftDeletes if you want to soft-delete comments:
use Illuminate\Database\Eloquent\SoftDeletes;
class Comment extends Model
{
use SoftDeletes;
}
delete() method on comments will respect this.Nested Comment Depth:
public function reply($data, $user, $parentComment = null, $depth = 0)
{
if ($depth >= 3) {
throw new \Exception('Maximum reply depth reached.');
}
// ...
}
User Association:
user_id field in the comments table is required. Ensure your $user variable is always valid before calling comment() or reply().Polymorphic Relations:
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
Missing Comments:
commentable_id and commentable_type are correctly populated in the comments table.php artisan tinker and test:
$post = Post::find(1);
$post->comments()->count(); // Should return > 0 if comments exist.
Validation Errors:
validateComment method in your model to customize validation rules (see Integration Tips).Event Firing:
CommentCreated::listen(function ($comment) {
Log::info('Comment created:', ['comment_id' => $comment->id]);
});
Custom Comment Model:
Comment model by publishing the config:
php artisan vendor:publish --provider="AliBayat\LaravelCommentable\CommentableServiceProvider" --tag=config
config/commentable.php to specify a custom model:
'model' => \App\Models\CustomComment::class,
Additional Fields:
comments table by modifying the migration:
Schema::table('comments', function (Blueprint $table) {
$table->string('ip_address')->nullable();
$table->boolean('is_edited')->default(false);
});
Comment model to fill these fields:
protected $fillable = ['body', 'title', 'ip_address'];
Custom Scopes:
Comment model for reusable queries:
public function scopeRecent($query)
{
return $query->orderBy('created_at', 'desc')->limit(10);
}
$model->comments()->recent()->get();
Rate Limiting:
throttle middleware):
Route::middleware(['throttle:5,1'])->group(function () {
Route::post('/posts/{post}/comments', [PostController::class, 'store']);
});
Localization:
php artisan vendor:publish --provider="AliBayat\LaravelCommentable\CommentableServiceProvider" --tag=lang
resources/lang/{locale}/commentable.php.How can I help you explore Laravel packages today?