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() column to your migration:
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->publishes(); // Adds `published_at` timestamp
});
Publish a model immediately:
$post = Post::create(['title' => 'Hello World']);
$post->publish(); // Sets `published_at` to current timestamp
// Publish with current timestamp
$post->publish();
// Publish at a future date
$post->publish(Carbon::parse('2025-12-31'));
// Unpublish
$post->unpublish();
// Get only published posts
$published = Post::onlyPublished()->get();
// Get upcoming (planned) posts
$planned = Post::onlyPlanned()->get();
// Combine scopes
$activePosts = Post::onlyPlannedAndPublished()->latestPublished()->get();
// Default (nullable timestamp)
$table->publishes();
// Custom column name
$table->publishes('release_date');
// Timezone-aware
$table->publishesTz('scheduled_at');
// Drop column
$table->dropPublishes('release_date');
// In EventServiceProvider
protected $listen = [
'publishing' => [
\App\Listeners\LogPublish::class,
],
'published' => [
\App\Listeners\NotifyEditor::class,
],
];
// Model
class Post extends Model
{
use Publishes;
const PUBLISHED_AT = 'publication_date';
}
// Migration
$table->publishes('publication_date');
Column Name Mismatch:
PUBLISHED_AT is defined in the model but the migration uses a different name, queries will fail.Timezone Issues:
publishes() uses timestamp, while publishesTz() uses timestampTz. Mixing these may cause serialization errors.Null Checks:
isPublished() returns true if published_at is not null (past or future). Use isPlanned() to check for future dates.whereDate() for precise filtering:
Post::whereNotNull('published_at')->where('published_at', '>=', now());
Ordering Quirks:
latestPublished() sorts by published_at ascending by default. Use oldestPublished() for descending.dd($query->toSql()).publishing/published events to debug timing:
event(new Publishing($post));
DB::enableQueryLog();
Post::onlyPublished()->get();
dd(DB::getQueryLog());
Custom Logic in Events:
Extend the publishing event to validate content before publishing:
public function handle(Publishing $event)
{
if (!$event->model->isValid()) {
throw new \Exception('Invalid content!');
}
}
Add Soft Publishing:
Combine with SoftDeletes for "unpublish" as soft delete:
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use Publishes, SoftDeletes;
public function unpublish()
{
$this->delete(); // Soft delete
}
}
Override Default Column: Dynamically set the column name in the trait:
// In Publishes trait
protected static function getPublishedAtColumn()
{
return config('eloquent-publishing.column', 'published_at');
}
PUBLISHED_AT) or migration arguments. No config/eloquent-publishing.php exists.publishes() method accepts a precision parameter (e.g., $table->publishes('published_at', 3)), but this is rarely needed for timestamps.$table->publishes('published_at');
$table->index('published_at');
with() or load() when eager-loading related models in scopes:
Post::onlyPublished()->with('author')->get();
How can I help you explore Laravel packages today?