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;
}
publishes() method to your migration:
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->publishes(); // Adds `published_at` timestamp column
});
Publish a model in a controller:
$post = Post::find(1);
$post->publish(); // Publishes immediately with current timestamp
Model Lifecycle Management
$model->publish() or $model->publish(Carbon::parse('2025-01-01'))$model->unpublish()$model->isPublished(); // true if published (past or present)
$model->isPlanned(); // true if scheduled for future
Query Scoping
Post::onlyPublished()->get(); // Only past/present published
Post::onlyPlanned()->get(); // Only future-published
Post::withoutPlannedAndPublished()->get(); // Unpublished
Post::latestPublished()->get(); // Newest first
Post::oldestPlanned()->get(); // Oldest future date first
Migration Patterns
publishes() blueprint methods:
$table->publishes('custom_date'); // Custom column name
$table->publishesTz(); // Timezone-aware timestamp
$table->dropPublishes(); // Remove column
Event-Driven Extensions
event(new Publishing($model));
event(new Published($model));
PUBLISHED_AT constant in your model and use it in migrations.SoftDeletes for unpublished records:
use SoftDeletes;
class Post extends Model
{
use Publishes, SoftDeletes;
}
public function toArray()
{
return [
'title' => $this->title,
'is_published' => $this->isPublished(),
'published_at' => $this->published_at,
];
}
Column Name Mismatch
PUBLISHED_AT constant is defined in the model but the migration uses a different name, queries will fail.Timezone Issues
publishes() uses timestamp (UTC), while publishesTz() uses timestampTz (timezone-aware).publishesTz() if your app relies on timezone-specific publishing.Event Ordering
publishing fires before published, but unpublishing fires before unpublished.dd() in event listeners to verify order.Null Handling
isPublished() returns false for null values, but isPlanned() requires a future date.null published_at is neither published nor planned.published_at column exists in the DB:
php artisan schema:dump
event(new Publishing($model));
Log::debug('Publishing model', ['id' => $model->id]);
Post::onlyPublished()->toSql(); // Show raw SQL
Custom Logic
publish()) to add validation:
public function publish(?CarbonInterface $date = null)
{
if (!$this->isApproved()) {
throw new \Exception('Must be approved first');
}
$this->published_at = $date ?? now();
$this->save();
}
Custom Events
event(new CustomPublished($model, ['author_id' => auth()->id()]));
Policy Integration
public function update(PublishPost $request, Post $post)
{
$this->authorize('publish', $post);
$post->publish();
}
Testing
$model = new class extends Model {
use Publishes;
};
$model->publish();
$this->assertNotNull($model->published_at);
How can I help you explore Laravel packages today?