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

Kinetic Laravel Package

ambengers/kinetic

Kinetic is a Laravel package for adding state machines to Eloquent models. Define states and allowed transitions, guard changes with conditions, and hook into lifecycle events so status workflows stay explicit, consistent, and easy to maintain.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require ambengers/kinetic

Laravel auto-discovers the service provider. If your app disables discovery, register Ambengers\Kinetic\KineticServiceProvider::class in bootstrap/providers.php. Publish the config file if the package ships one (php artisan vendor:publish --tag=kinetic-config).

Where to look first, in order:

  1. The README's "Usage" section — it is the fastest way to confirm the registration signature for your version.
  2. The package's service provider — it shows exactly where Kinetic hooks into Inertia's response (a response macro, a middleware, or an event listener). That tells you when composers run, which dictates everything else.
  3. The package's tests/ directory — the composer class contract (method name, arguments, return type) is usually clearer in tests than in docs.

First use case — page-scoped ambient props. The canonical win is data that many pages need but no single controller owns: notification counts, feature flags, sidebar badges. Register it once in AppServiceProvider::boot() (or a dedicated provider):

use Ambengers\Kinetic\Facades\Kinetic;

public function boot(): void
{
    Kinetic::compose('Dashboard', function (array $page) {
        return [
            'notifications' => auth()->user()?->unreadNotifications()->count() ?? 0,
        ];
    });
}

The closure receives the Inertia page payload (component name plus existing props) and returns an array merged into the page's props before the response is serialized. The controller stays focused on the page's own data; the composer owns the cross-cutting data. If your version uses a config array rather than a facade, the mental model is identical — component name in, props out.

Implementation Patterns

Class-based composers for anything non-trivial. A composer is a class with a compose method returning an array, resolved from the container — so you get constructor injection for free.

namespace App\Http\Kinetic;

class UserIndexComposer
{
    public function __construct(private NotificationRepository $notifications) {}

    public function compose(array $page): array
    {
        return [
            'unread_count' => $this->notifications->unreadFor(request()->user()),
            'filters'      => request()->only('search', 'sort'),
        ];
    }
}
Kinetic::compose('Users/Index', UserIndexComposer::class);

Give composers their own service provider. AppServiceProvider becomes a dumping ground fast. Create app/Providers/KineticServiceProvider.php, register it, and group compositions by domain (Users, Billing, Admin). This also makes it obvious where to add the next one.

Wildcard matching for module-wide props. If the package supports patterns like Users/* or Admin/*, use a single composer for layout-level data (breadcrumbs, sidebar nav, tenant banner) instead of duplicating it across every page in the module. Check the README for the exact glob syntax — this is the feature most likely to differ between versions.

Layer Kinetic with Inertia::share(), don't replace it. share() is truly global and runs on every response. Kinetic is page-scoped. Use share() for app name, flash messages, and auth identity; use Kinetic for data that only a subset of pages needs. Mixing the two responsibilities is the most common architectural mistake.

Defer or lazy-load expensive values. Wrap composed props in Inertia::lazy() or Inertia::defer() so the initial page load and non-partial requests don't pay for queries the page doesn't render immediately.

Conditional composition, static registration. Check roles or feature flags inside the composer and return [] when not applicable. Registering conditionally at boot makes the composer map harder to reason about and harder to test.

Testing. Use Inertia's AssertableInertia to assert composed props land where you expect:

$this->get('/users')
    ->assertInertia(fn (Assert $page) => $page
        ->component('Users/Index')
        ->where('unread_count', 3)
    );

If composers leak between tests, reset them in setUp() (many packages expose a flush-style method, or you can re-boot the provider).

Gotchas and Tips

  • Component names are matched exactly, and mismatches fail silently. Users/Index is not users/index, and neither is Users/Index.vue. A typo produces no exception — just missing props. Keep the registration list in one file so it's scannable, and assert in a test that a known composer actually applies.
  • Pin down prop precedence before you ship. When a controller prop and a composer prop share a key, which wins depends on merge order inside the package. Write one test that sets both and asserts the winner. Discover this in a test, not in production.
  • Partial reloads still invoke composers. Inertia's partial reload only sends the requested props, but the composer runs server-side regardless. Guard heavy queries by checking $request->header('X-Inertia-Partial-Data') or by using lazy()/defer().
  • An exception in a composer breaks the whole page, not just that one prop. Wrap third-party or API calls in try/catch and return a safe default. A failed notification-count query should not 500 the dashboard.
  • Config caching kills closures. If you register composers from a config file, php artisan config:cache cannot serialize closures — you'll get a LogicException or silent breakage in production. Register in a service provider and keep config to scalar values only.
  • Timing and auth(). Call auth()->user() inside the composer closure, never at registration time. Registration runs at boot, before the request is fully resolved.
  • Facade alias resolution. If Kinetic:: is unresolved, use the FQCN \Ambengers\Kinetic\Facades\Kinetic or check the package's aliases config. Auto-discovery registers the provider; it does not always register the alias.
  • Version coupling with inertiajs/inertia-laravel. Kinetic sits on Inertia's response pipeline. A major Inertia upgrade can change how props are typed or merged. Pin both packages and re-run your prop assertions on every upgrade.
  • Class composers are the extension point. Because they're container-resolved, you can bind decorators, swap implementations in tests, and mock dependencies. Reach for a class before you reach for a closure when the logic grows past three lines.
  • Debugging recipe. dd() inside a composer returns HTML into a JSON response and confuses the client — use Log::info($page) instead. When props vanish, check in this order: (1) the registered component string, (2) merge precedence, (3) whether the value is lazy() and no partial reload requested it.
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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums