lemaur/eloquent-publishing
Add publishing support to Laravel Eloquent models with a simple trait. Manage publish dates, query scopes and helpers, plus custom migration blueprint methods to quickly add publishing columns and build publishable content workflows.
composer require lemaur/eloquent-publishing
Publishes trait to your Eloquent model:
use Lemaur\Publishing\Database\Eloquent\Publishes;
class Post extends Model
{
use Publishes;
}
published_at column to your migration using the provided blueprint method:
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->publishes(); // Adds nullable timestamp `published_at`
});
Publish a model instance in a controller:
$post = Post::find(1);
$post->publish(); // Sets `published_at` to current timestamp
Model Lifecycle Management:
// Publish with custom date (e.g., scheduled content)
$post->publish(Carbon::parse('2025-12-31'));
// Unpublish
$post->unpublish();
// Check status
if ($post->isPublished()) { ... }
if ($post->isPlanned()) { ... }
Query Scoping:
// Filter published content
$published = Post::onlyPublished()->get();
// Filter scheduled content
$scheduled = Post::onlyPlanned()->get();
// Combine filters
$active = Post::onlyPlannedAndPublished()->get();
Ordering:
// Latest published first
$recent = Post::onlyPublished()->latestPublished()->get();
// Oldest scheduled first
$oldest = Post::onlyPlanned()->oldestPlanned()->get();
Events: Listen for publishing, published, unpublishing, or unpublished events to trigger side effects (e.g., notifications, cache updates).
Post::published(function ($post) {
// Send email when published
});
Custom Column Names: Override the default published_at column:
class Post extends Model
{
use Publishes;
const PUBLISHED_AT = 'release_date';
}
Update migrations to match:
$table->publishes('release_date');
Timezone Support: Use publishesTz() for timezone-aware timestamps (requires Laravel 11+ and PHP 8.2+).
$post->publish(Carbon::tomorrow()); // Future publishing
if ($post->isNotPublished()) { ... }
$featured = Post::onlyPublished()->latestPublished()->take(3)->get();
Laravel/PHP Version Requirement:
Column Mismatch:
PUBLISHED_AT is defined in the model but the migration uses a different column name, queries will fail.Null Handling:
isPublished() returns false for null values (unpublished). Use isPlanned() for future dates.Event Timing:
publishing/unpublishing events fire before the timestamp is set. Use published/unpublished for post-update logic.php artisan migrate:fresh if the published_at column is missing.onlyPublished()) by inspecting the generated SQL with ->toSql().3.x:
composer require lemaur/eloquent-publishing:^3.3
Custom Logic:
Override trait methods (e.g., publish()) to add validation or business rules:
public function publish($date = null)
{
if (!$this->isApproved()) {
throw new \Exception('Content must be approved first.');
}
$this->published_at = $date ?? now();
$this->save();
}
Global Scopes: Extend the trait to add default query scopes:
use Lemaur\Publishing\Database\Eloquent\Publishes;
class Post extends Model
{
use Publishes;
protected static function booted()
{
static::addGlobalScope(new PublishedScope());
}
}
Testing:
Mock the published_at attribute for tests:
$post = Post::factory()->create(['published_at' => now()->addDay()]);
$this->assertTrue($post->isPlanned());
published_at for large datasets:
$table->publishes();
$table->index('published_at');
$posts = Post::with('author')->onlyPublished()->get();
publishesTz() for timezone-aware timestamps, but ensure your database supports it (e.g., PostgreSQL).publishes() method accepts a precision parameter (default: 0), which may affect storage for microsecond-level timestamps.How can I help you explore Laravel packages today?