Skip to main content

Overview

The Watchlist model manages user watchlists for both VOD streams and series. It uses a polymorphic relationship to support multiple media types in a single table. Model Path: app/Models/Watchlist.php
Table: watchlists

Database Schema

id
integer
required
Auto-incrementing primary key
user_id
integer
required
Foreign key to users table (cascade on delete)
watchable_id
integer
required
Polymorphic foreign key to the watchable item (Series or VodStream)
watchable_type
string
required
Polymorphic type (e.g., ‘App\Models\Series’ or ‘App\Models\VodStream’)
created_at
timestamp
Record creation timestamp
updated_at
timestamp
Record last update timestamp

Fillable Attributes

[
    'user_id',
    'watchable_id',
    'watchable_type',
]

Constraints

Unique Constraint

The table has a unique composite index to prevent duplicate watchlist entries:
['user_id', 'watchable_id', 'watchable_type']
This ensures a user cannot add the same item to their watchlist multiple times.

Foreign Key Constraint

  • user_idusers.id (cascade on delete)
  • When a user is deleted, all their watchlist entries are automatically removed

Relationships

User

Belongs to a User:
public function user(): BelongsTo
Usage:
$watchlistItem = Watchlist::find(1);
$user = $watchlistItem->user;

Watchable (Polymorphic)

Polymorphic relationship to either VodStream or Series:
public function watchable(): MorphTo
Usage:
$watchlistItem = Watchlist::find(1);
$media = $watchlistItem->watchable; // Returns VodStream or Series

if ($media instanceof VodStream) {
    // Handle VOD
} elseif ($media instanceof Series) {
    // Handle series
}

Common Operations

Add to Watchlist

// Add a VOD stream to watchlist
Watchlist::create([
    'user_id' => auth()->id(),
    'watchable_id' => $vodStream->stream_id,
    'watchable_type' => VodStream::class,
]);

// Add a series to watchlist
Watchlist::create([
    'user_id' => auth()->id(),
    'watchable_id' => $series->series_id,
    'watchable_type' => Series::class,
]);

Using Polymorphic Helper

// Add using relationship
$user->watchlists()->create([
    'watchable_id' => $vodStream->stream_id,
    'watchable_type' => VodStream::class,
]);

// Or from the media model
$vodStream->watchlists()->create([
    'user_id' => auth()->id(),
]);

Get User’s Watchlist

// Get all watchlist items for a user
$watchlist = Watchlist::where('user_id', auth()->id())->get();

// With eager loading
$watchlist = Watchlist::with('watchable')
    ->where('user_id', auth()->id())
    ->get();

// Group by type
$vodWatchlist = Watchlist::where('user_id', auth()->id())
    ->where('watchable_type', VodStream::class)
    ->get();

$seriesWatchlist = Watchlist::where('user_id', auth()->id())
    ->where('watchable_type', Series::class)
    ->get();

Check if Item is in Watchlist

$isInWatchlist = Watchlist::where('user_id', auth()->id())
    ->where('watchable_id', $vodStream->stream_id)
    ->where('watchable_type', VodStream::class)
    ->exists();

Remove from Watchlist

Watchlist::where('user_id', auth()->id())
    ->where('watchable_id', $vodStream->stream_id)
    ->where('watchable_type', VodStream::class)
    ->delete();

Error Handling

Duplicate Entry

Attempting to add a duplicate item will throw a unique constraint violation:
try {
    Watchlist::create([
        'user_id' => auth()->id(),
        'watchable_id' => $vodStream->stream_id,
        'watchable_type' => VodStream::class,
    ]);
} catch (\Illuminate\Database\QueryException $e) {
    if ($e->getCode() === '23000') {
        // Duplicate entry - item already in watchlist
    }
}
Alternatively, use firstOrCreate:
$watchlistItem = Watchlist::firstOrCreate([
    'user_id' => auth()->id(),
    'watchable_id' => $vodStream->stream_id,
    'watchable_type' => VodStream::class,
]);

Build docs developers (and LLMs) love