Installation:
composer require jamesmills/watchable
For Laravel <5.5, register the service provider in config/app.php:
'providers' => [
// ...
Jamesmills\Watchable\WatchableServiceProvider::class,
],
Enable Watchable on a Model:
Use the Watchable trait in your Eloquent model (e.g., Post.php):
use Jamesmills\Watchable\Watchable;
class Post extends Model
{
use Watchable;
}
Migrate the Database: Publish and run the migrations:
php artisan vendor:publish --provider="Jamesmills\Watchable\WatchableServiceProvider" --tag="migrations"
php artisan migrate
First Use Case: Watch a model instance:
$post = Post::find(1);
$post->watch(); // User is authenticated via Laravel's auth
Watching/Unwatching Models:
// Watch a model
$post->watch(); // Creates a watch record for the authenticated user
// Unwatch a model
$post->unwatch(); // Removes the watch record
// Check if a model is watched
$isWatched = $post->isWatched(); // Returns boolean
Querying Watched Models: Fetch all watched models for the current user:
$watchedPosts = Post::watched()->get();
Events and Notifications:
Listen for watched and unwatched events:
// In EventServiceProvider
protected $listen = [
'Jamesmills\Watchable\Events\Watched' => [
'App\Listeners\SendWatchNotification',
],
'Jamesmills\Watchable\Events\Unwatched' => [
'App\Listeners\HandleUnwatchLogic',
],
];
Trigger notifications via the notify method:
$post->watch()->notify(new PostWatchedNotification());
Customizing Watch Behavior:
Override the getWatchableKey() method in your model to use a custom key (e.g., slug):
public function getWatchableKey()
{
return $this->slug;
}
Middleware for Protected Routes: Use middleware to restrict access to watched models:
Route::middleware(['auth', 'can:view-watched'])->group(function () {
Route::get('/watched', [PostController::class, 'index']);
});
Eager Loading Watched Models: Optimize queries by eager-loading watches:
$posts = Post::with(['watches' => function($query) {
$query->where('user_id', auth()->id());
}])->get();
Batch Processing:
Use syncWatched() to update watches in bulk (e.g., for syncing with an external service):
$post->syncWatched([1, 2, 3]); // Watch IDs 1, 2, 3
Scopes for Complex Queries: Combine scopes for advanced filtering:
$watchedAndPublished = Post::watched()->published()->get();
Authentication Dependency:
auth()->check() before calling watch()/unwatch().if (auth()->check()) {
$post->watch();
}
Key Collisions:
getWatchableKey() returns the same value for multiple models, watches may behave unexpectedly.id by default or override getWatchableKey() carefully).Migration Conflicts:
watches table may conflict with existing tables if not published correctly.php artisan vendor:publish --tag=migrations and review the generated migration.Event Listener Order:
Watched, Unwatched) fire after the watch is created/removed. If you need pre-actions, use model observers or middleware.Check Watch Records:
Inspect the watches table directly or log queries:
\DB::enableQueryLog();
$post->watch();
\Log::info(\DB::getQueryLog());
Verify User ID:
Ensure the user_id in the watches table matches the authenticated user:
$this->assertEquals(auth()->id(), $post->watches()->first()->user_id);
Clear Cached Config: If changes to config aren’t reflected, clear the config cache:
php artisan config:clear
Custom Watch Model:
Extend the Watch model (published via php artisan vendor:publish --tag=watchable-models):
class CustomWatch extends \Jamesmills\Watchable\Models\Watch
{
protected $table = 'custom_watches';
}
Update the trait to use your model:
use Jamesmills\Watchable\Concerns\UsesCustomWatchModel;
Add Metadata to Watches: Attach extra data to watches via accessors/mutators:
// In Watch model
protected $casts = ['metadata' => 'array'];
// When watching
$post->watch(['notified_at' => now()]);
Custom Watch Logic:
Override the watch() method in your model:
public function watch(array $options = [])
{
$this->fireModelEvent('watching', false);
$result = parent::watch($options);
$this->fireModelEvent('watched', false);
return $result;
}
Soft Deletes:
If your models use soft deletes, ensure the watches table also supports soft deletes:
use Illuminate\Database\Eloquent\SoftDeletes;
class Watch extends Model
{
use SoftDeletes;
protected $dates = ['deleted_at'];
}
How can I help you explore Laravel packages today?