open-southeners/laravel-model-status
Lightweight Laravel model status handling using native PHP enums—no extra tables needed. Define a status enum, attach it via a PHP attribute, and use a trait for helpers like setStatus, setStatusWhen, hasStatus, and optional status events.
Installation:
composer require open-southeners/laravel-model-status
No additional configuration is required beyond publishing the package (though none is needed by default).
First Use Case:
Define a status enum for your model (e.g., PostStatus for a Post model):
use OpenSoutheners\LaravelModelStatus\ModelStatus;
enum PostStatus: int implements ModelStatus
{
case Draft = 1;
case Published = 2;
case Archived = 3;
}
ModelStatus and use an int (or string) backing type.Model Integration:
Attach the enum to your model via the ModelStatuses attribute and implement Statusable:
use OpenSoutheners\LaravelModelStatus\Attributes\ModelStatuses;
use OpenSoutheners\LaravelModelStatus\HasStatuses;
use OpenSoutheners\LaravelModelStatus\Statusable;
#[ModelStatuses(PostStatus::class)]
class Post implements Statusable
{
use HasStatuses;
}
HasStatuses trait provides all CRUD operations for statuses.Usage:
$post = new Post();
$post->setStatus(PostStatus::Published); // Set status
$post->getStatus(); // Get current status (returns PostStatus::Published)
$post->isStatus(PostStatus::Draft); // Check if status matches (returns bool)
Status Transitions:
Enforce valid transitions (e.g., Draft → Published but not Published → Draft) by overriding validateStatusTransition in your model:
protected function validateStatusTransition(?ModelStatus $newStatus): bool
{
if ($this->status === PostStatus::Published && $newStatus === PostStatus::Draft) {
return false;
}
return true;
}
Default Status: Set a default status in the model’s constructor:
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->setStatus(PostStatus::Draft); // Default to Draft
}
Querying by Status:
Use the status() scope provided by the trait:
$publishedPosts = Post::status(PostStatus::Published)->get();
Status-Based Logic: Add methods to your model for status-specific behavior:
public function isPublishable(): bool
{
return $this->isStatus(PostStatus::Draft);
}
Serialization: Cast the status to a string or integer for JSON/API responses:
protected $casts = [
'status' => 'int', // or 'string' if using string-backed enums
];
$request->validate([
'status' => 'required|in:' . implode(',', array_column(PostStatus::cases(), 'value')),
]);
class PostObserver
{
public function saved(Post $post)
{
if ($post->wasStatusChanged) {
// Send email, log activity, etc.
}
}
}
public function update(Statusable $model, User $user)
{
if (!$user->can('publish_posts') && $model->isStatus(PostStatus::Draft)) {
abort(403);
}
}
Enum Backing Type:
int or string backing types for enums. Using other types (e.g., float) will cause errors.int or string:
enum PostStatus: string implements ModelStatus { ... } // Valid
Missing ModelStatus Interface:
ModelStatus on your enum will throw runtime errors.ModelStatus:
enum PostStatus implements ModelStatus { ... } // Correct
Status Not Persisted:
use Illuminate\Database\Eloquent\Model).Case-Sensitive Enum Values:
string-backed enums, ensure case matches exactly when validating or querying.case Published = 'PUBLISHED';
Overriding HasStatuses:
getStatusKey() or setStatus() can break functionality.status() method to your model).Status Not Updating:
validateStatusTransition() is returning false. Add logging:
protected function validateStatusTransition(?ModelStatus $newStatus): bool
{
logger()->debug("Attempting transition to: {$newStatus->value}");
return true; // Temporarily bypass for testing
}
Enum Not Recognized:
ModelStatuses attribute is correctly applied to the model class (not just the file).composer dump-autoload if changes aren’t reflected.Performance with Large Datasets:
status() scope adds a WHERE clause. For large tables, ensure your status column (if using a database) is indexed.Custom Status Storage:
getStatusAttribute() to store statuses in a custom column or cache:
public function getStatusAttribute(): ?ModelStatus
{
return $this->status ?? $this->customStatusColumn;
}
Status Events:
StatusChanged events:
use OpenSoutheners\LaravelModelStatus\Events\StatusChanged;
event(new StatusChanged($model, $oldStatus, $newStatus));
Multi-Status Support:
trait HasMultipleStatuses
{
use HasStatuses;
public function setStatus(string $field, ModelStatus $status): static
{
$this->{$field} = $status;
return $this;
}
}
Localization:
enum PostStatus implements ModelStatus
{
case Draft;
public function label(): string { return __('status.draft'); }
}
PostStatus::Published->label() in views.How can I help you explore Laravel packages today?