PHPackages                             xcopy/laravel-model-state-transitions - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. xcopy/laravel-model-state-transitions

ActiveLibrary

xcopy/laravel-model-state-transitions
=====================================

Manage state transitions for your Laravel models with role-based access control and automatic history tracking.

00PHPCI passing

Since Mar 17Pushed 5d agoCompare

[ Source](https://github.com/xcopy/laravel-model-state-transitions)[ Packagist](https://packagist.org/packages/xcopy/laravel-model-state-transitions)[ RSS](/packages/xcopy-laravel-model-state-transitions/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (1)Used By (0)

Laravel Model State Transitions
===============================

[](#laravel-model-state-transitions)

[![GitHub Tests Action Status](https://camo.githubusercontent.com/cc764f47c7ba7c145807392afe49aa547447560d605c8a7dacb6adcf4db47165/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f78636f70792f6c61726176656c2d6d6f64656c2d73746174652d7472616e736974696f6e732f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/xcopy/laravel-model-state-transitions/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/8664f9a4fb5cb4afa3b3a4ebecf9a37cef697d1c6eb30d27af0f9ed8f3b2c6e3/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f78636f70792f6c61726176656c2d6d6f64656c2d73746174652d7472616e736974696f6e732f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/xcopy/laravel-model-state-transitions/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/6b416e9c8852203e32acdc31f5572935e0f33bb2064a23612b62bc65c39be046/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f78636f70792f6c61726176656c2d6d6f64656c2d73746174652d7472616e736974696f6e732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/xcopy/laravel-model-state-transitions)

Manage state transitions for your Laravel models with role-based access control and automatic history tracking.

Features
--------

[](#features)

- **Define valid state transitions** for any Eloquent model
- **Role-based access control (RBAC)** for transitions - users and roles can be authorized
- **Automatic transition history tracking** with complete audit trail and user attribution
- **Type-safe enum support** – automatic casting between database values and BackedEnum instances
- **Polymorphic relationships** for flexible model support
- **Custom properties and descriptions** for each transition with metadata support
- **Query available transitions** based on current state and user permissions
- **Convenience methods** for state transitions with metadata (`stateTransitionTo()`, `setTransitionMetadata()`)
- **Automatic user tracking** via EloquentBlameable integration

Installation
------------

[](#installation)

**Note:** This package is not yet available on Packagist. You must add it to your `composer.json` manually.

```
{
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/xcopy/laravel-model-state-transitions"
        }
    ],
    "require": {
        "xcopy/laravel-model-state-transitions": "dev-main"
    }
}
```

Run the installation commands:

```
php artisan vendor:publish --provider="Jenishev\Laravel\ModelStateTransitions\ModelStateTransitionsServiceProvider" --tag=config
php artisan vendor:publish --provider="Jenishev\Laravel\ModelStateTransitions\ModelStateTransitionsServiceProvider" --tag=migrations
php artisan migrate
```

Quick Start
-----------

[](#quick-start)

### 1. Create a State Enum

[](#1-create-a-state-enum)

```
namespace App\Enums;

enum PaymentStateEnum: string
{
    case Pending = 'pending';
    case Paid = 'paid';
    // ...
}
```

### 2. Add Trait to Your Model

[](#2-add-trait-to-your-model)

```
use Jenishev\Laravel\ModelStateTransitions\Concerns\HasStateTransitions as HasStateTransitionsConcern;
use Jenishev\Laravel\ModelStateTransitions\Contracts\HasStateTransitions as HasStateTransitionsContract;

class Payment extends Model implements HasStateTransitionsContract
{
    use HasStateTransitionsConcern;

    protected $fillable = [
        // ...
        'state'
    ];

    protected function casts(): array
    {
        return [
            'state' => PaymentStateEnum::class,
            // ... other casts
        ];
    }

    // Override only if using custom enum naming convention,
    // By default, expects: App\Enums\{ModelName}StateEnum
    public static function resolveStateEnum(): string
    {
        return PaymentStateEnum::class;
    }
}
```

### 3. Define Transitions

[](#3-define-transitions)

```
use Jenishev\Laravel\ModelStateTransitions\Models\Transition;

// Create a transition definition
$transition = Transition::create([
    'model_type' => Payment::class,
    'from_state' => PaymentStateEnum::Pending,
    'to_state' => PaymentStateEnum::Paid,
]);

// Authorize specific users
$transition->users()->attach($userId);

// Authorize by role
$transition->roles()->attach($roleId);

// Or authorize multiple at once
$transition->users()->attach([$userId1, $userId2]);
$transition->roles()->attach([$adminRoleId, $managerRoleId]);
```

### 4. Use in Your Application

[](#4-use-in-your-application)

```
// Check available transitions for current user
$availableTransitions = $payment->transitions()->get();

// Perform transition with metadata
$payment->stateTransitionTo(
    state: PaymentStateEnum::Paid,
    description: 'Payment confirmed'
);

// View history
foreach ($payment->transitionHistory as $history) {
    echo "{$history->from_state->value} → {$history->to_state->value} by user {$history->creator->name}";
    // OR
    // echo "{$history->from_state->label()} → {$history->to_state->label()}";
}
```

Usage
-----

[](#usage)

### Performing State Transitions

[](#performing-state-transitions)

The package provides multiple ways to transition between states:

#### Simple State Change (Auto-tracked)

[](#simple-state-change-auto-tracked)

```
// Direct state change - automatically tracked in history
$payment->state = PaymentStateEnum::Paid;
$payment->save();
```

#### Transition with Metadata (Recommended)

[](#transition-with-metadata-recommended)

```
// Use stateTransitionTo() for convenience - transitions and saves in one call
$payment->stateTransitionTo(
    state: PaymentStateEnum::Paid,
    description: 'Payment approved by manager',
    custom_properties: [
        'approved_by' => $manager->id,
        'approval_method' => 'manual',
        'notes' => 'All documents verified'
    ]
);
```

#### Set Metadata Before Saving

[](#set-metadata-before-saving)

```
// Set metadata first, then update the state
$payment->setTransitionMetadata(
    description: 'Payment rejected due to insufficient funds',
    custom_properties: ['reason_code' => 'INSUFFICIENT_FUNDS']
);
$payment->state = PaymentStateEnum::Rejected;
$payment->save();
```

### Get Available Transitions

[](#get-available-transitions)

```
// For authenticated user
$payment->transitions()->get();

// For specific user
$payment->transitions($user)->get();

// Check if a specific transition exists
$payment->transitions()
    ->where('to_state', PaymentStateEnum::Paid)
    ->exists();
```

### Query History

[](#query-history)

```
// Get all transitions for a model
$payment->transitionHistory;

// Latest transition
$payment->transitionHistory()->latest()->first();

// Filter by state
$payment->transitionHistory()
    ->where('to_state', PaymentStateEnum::Paid)
    ->get();

// Access enum values
$history = $payment->transitionHistory()->latest()->first();
$history->from_state; // Returns PaymentStateEnum::Pending
$history->to_state;   // Returns PaymentStateEnum::Paid

// Access metadata
$history->description;
$history->custom_properties; // Array of custom data
$history->created_by; // User ID who made the transition
```

Advanced Usage
--------------

[](#advanced-usage)

### Attaching Transitions to Users/Roles

[](#attaching-transitions-to-usersroles)

To query available transitions from the user or role side, add the `HasAttachedTransitions` trait:

```
use Jenishev\Laravel\ModelStateTransitions\Concerns\HasAttachedTransitions;

class User extends Authenticatable
{
    use HasAttachedTransitions;

    // ... existing code
}

class Role extends Model
{
    use HasAttachedTransitions;

    // ... existing code
}
```

This enables:

```
// Get all transitions available to a user
$user->transitions;

// Attach a transition to a user
$user->transitions()->attach($transitionId);

// Check if a user has a specific transition
$user->transitions()->where('to_state', 'approved')->exists();
```

Configuration
-------------

[](#configuration)

The package provides sensible defaults out of the box. You can customize the behavior by editing the published configuration file at `config/model-state-transitions.php`.

Key configuration options include:

- **`transitions_table`** - Table storing state transition definitions (default: `transitions`)
- **`transition_history_table`** - Audit trail table (default: `transition_history`)
- **`pivot_table`** - User/Role authorization pivot table (default: `model_has_transitions`)
- **`transitionable_state_column`** - Column name on your models (default: `state`)
- **`transition_model`** - Transition model class (customizable)
- **`transition_history_model`** - History model class (customizable)
- **`role_model`** - Your application's Role model (customizable)
- **`user_model`** - Your application's User model (customizable)

Refer to the config file for detailed documentation on each option.

Testing
-------

[](#testing)

```
composer test
composer format
composer analyse
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

20

↑

LowBetter than 16% of packages

Maintenance65

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/00b9a5db4855c1ef50ec05482f605d3147cd096a0d4a199fdf711642fca0f7fc?d=identicon)[xcopy](/maintainers/xcopy)

---

Top Contributors

[![xcopy](https://avatars.githubusercontent.com/u/202339?v=4)](https://github.com/xcopy "xcopy (17 commits)")

---

Tags

laravelphpstatestransitions

### Embed Badge

![Health badge](/badges/xcopy-laravel-model-state-transitions/health.svg)

```
[![Health](https://phpackages.com/badges/xcopy-laravel-model-state-transitions/health.svg)](https://phpackages.com/packages/xcopy-laravel-model-state-transitions)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
