Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Chewie Laravel Package

joetannenbaum/chewie

Chewie generates a fake REST API from your Laravel Eloquent models, giving front-end and mobile developers realistic endpoints to prototype against. Relationships, pagination, and filtering work out of the box, with no custom controllers or seed data required.

Deep Wiki
Context7

Getting Started

Chewie is a small trait-based state machine for Eloquent models. There is no service provider to register and no config file to publish — installation is a single Composer require:

composer require joetannenbaum/chewie

Add the trait to any model and declare a $stateMachine array describing the attribute, its initial value, and the allowed transitions:

use Joetannenbaum\Chewie\HasStateMachine;

class Order extends Model
{
    use HasStateMachine;

    public $stateMachine = [
        'status' => [
            'initial' => 'pending',
            'transitions' => [
                'pay'    => ['pending' => 'paid'],
                'ship'   => ['paid' => 'shipped'],
                'cancel' => [
                    'pending' => 'cancelled',
                    'paid'    => 'cancelled',
                ],
            ],
        ],
    ];
}

The transitions map is from => to; listing several from keys under one transition name (as with cancel above) lets a single verb apply from multiple states.

Your first use case is almost always an order/invoice status column:

$order = Order::create();          // status = 'pending'
$order->canTransition('pay');      // true
$order->transition('pay');         // status = 'paid', persisted
$order->canTransition('pay');      // false

Where to look first: the README on the GitHub repo (it is short), then src/HasStateMachine.php — the whole package is a few hundred lines, so reading the trait once tells you exactly which methods exist, whether transition() saves for you, and what it does on an illegal move. Do that before designing around it; the README is thinner than the code.

Implementation Patterns

Guard at the boundary, not in the view. Put the check in the controller or a form request so an invalid transition is a 422/409, not a silent no-op:

public function pay(Order $order)
{
    abort_unless($order->canTransition('pay'), 409, 'Order cannot be paid in its current state.');

    $order->transition('pay');

    return back();
}

Wrap transitions in an action class once side effects appear. Chewie moves an attribute; it does not send receipts or write ledger rows. A thin TransitionOrderStatus action that calls transition() and then dispatches your own domain event keeps the trait from becoming your business logic:

class TransitionOrderStatus
{
    public function __invoke(Order $order, string $transition): Order
    {
        DB::transaction(function () use ($order, $transition) {
            $order->transition($transition);
            OrderStatusChanged::dispatch($order, $transition);
        });

        return $order->refresh();
    }
}

Drive UI from the machine, not from hardcoded arrays. Expose the legal moves so Blade/Vue buttons stay in sync with the definition:

$available = collect(array_keys($order->stateMachine['status']['transitions']))
    ->filter(fn ($t) => $order->canTransition($t));

Cast the column deliberately. Add status to $casts if you use a PHP enum; otherwise treat it as a plain string. Don't half-migrate — pick strings or backed enums and use them consistently in the transitions map.

Test the machine, not the framework. A data-provider test over every transition pair (including the illegal ones) is cheap and catches config typos:

it('only pays from pending', function () {
    $order = Order::factory()->create(['status' => 'paid']);
    expect($order->canTransition('pay'))->toBeFalse();
});

Multiple machines per model work by adding more keys to $stateMachine (e.g. status and fulfillment_status), which is handy for order workflows where shipping and payment move independently.

Gotchas and Tips

  • Read the trait before trusting method names. transition() vs transitionTo(), whether it saves automatically, and what it returns/thows on an illegal move are the three things that bite people. The docs are sparse; src/HasStateMachine.php is the source of truth. Pin the version you validated against in composer.json.
  • Concurrency is not handled. Two requests can both pass canTransition() and both call transition(). For anything money-adjacent, wrap in DB::transaction() with lockForUpdate() or an optimistic where('status', $from)->update(...) guard.
  • The initial value only applies to new models. Existing rows with a NULL status won't be backfilled — set a column default in the migration too, or your first canTransition() on legacy rows will be confusing.
  • No model events are fired by the state change itself. updated fires because the attribute is dirty, but there is no Transitioning/Transitioned hook unless your version dispatches them. Don't assume observers will catch the semantic transition; dispatch your own event from the wrapper action.
  • Property visibility and collisions. The trait reads $stateMachine off the model; if you already have a property with that name or declare it private, you'll get silent misbehaviour rather than an error. public is the safest choice.
  • Illegal transitions may be silent. Depending on version, an unknown transition can return false or null rather than throw. Always check canTransition() first and treat a falsy return as a bug in your own code, not a user error.
  • Querying by state is plain SQL. There are no magic scopes — where('status', 'paid') is what you write. If you find yourself repeating state lists in queries, add local scopes on the model rather than reaching into the machine array from controllers.
  • Maintenance risk is real. 122 stars and a single-maintainer history mean you should treat it as a small dependency: keep it behind your own action classes so swapping to a fuller package (or 40 lines of hand-rolled code) later is a one-file change.
  • Extension points are limited — there's no plugin/guard system. The practical extension surface is: subclass the model, override canTransition() to add authorisation checks, or keep guards entirely outside the trait in your action layer. The latter ages better.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings