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

Laravel Manageable Laravel Package

signifly/laravel-manageable

Laravel Manageable lets you make any Eloquent model manageable with a single trait, adding admin-friendly CRUD, publish states, and sorting. It keeps management logic reusable and controllers lean, so building back-office screens takes minutes instead of hours.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and publish

composer require signifly/laravel-manageable
php artisan vendor:publish --tag="manageable"
php artisan migrate

Publishing typically gives you a config file (config/manageable.php) and a migration for the management table (polymorphic manageable_type / manageable_id + user_id). Check the published migration before running it — since the package is archived you'll want to confirm the column types/indexes match how you actually query (see Gotchas).

2. Attach the trait to the models you care about

use Signifly\Manageable\Manageable;

class Post extends Model
{
    use Manageable;
}

That's the whole "setup". From here the package hooks the model's created / updated / deleted events and writes a row pointing at the currently authenticated user.

3. Where to look first

  • config/manageable.php — which user model is resolved and which table is used.
  • The published migration — the source of truth for the schema, since the README is terse.
  • vendor/signifly/laravel-manageable/src/ — the trait and any observer; it's a small package, so reading it takes ten minutes and removes all guesswork.

4. First use case

Add an "Added by / Last touched by" line to an admin resource:

$post->managers;            // collection of users who created/updated/deleted it
$post->isManagedBy($user);  // boolean check

Show the first manager as the creator and the latest as the last editor, then build outward from there.

Implementation Patterns

Trait placement is the design decision. Put Manageable only on models where "who touched this" is a real question — orders, invoices, content, user records. Blanket-applying it across every model turns the management table into a write-amplified logging table you never query.

Guard clauses before display. Because management rows are appended on every save (not deduplicated into "creator" and "updater"), eager load and take what you need rather than dumping the whole relation:

$post->load('managers');

$creator = $post->managers->first();
$lastEditor = $post->managers->last();

For index screens, prefer a scoped query with withCount('managers') or a latestOfMany relation over hydrating full manager collections per row.

Authorization and policy hooks. isManagedBy() is a natural fit for "can this user still edit the record" style rules:

public function update(User $user, Post $post): bool
{
    return $user->isAdmin() || $post->isManagedBy($user);
}

This is the pattern the package is genuinely good at: lightweight ownership, not compliance-grade auditing.

Import and seed safety. Console commands, queue workers, and seeders usually have no authenticated user. Make sure the config's user resolution returns null gracefully, and wrap bulk imports so you don't spray null-user rows through the table:

// during seeding / importing
Auth::loginUsingId($adminId);   // if you need attribution
// or simply run imports inside a context where no user is logged in

Testing. Use $this->actingAs($user) before factory()->create() so the trait has someone to record. Assertions then read naturally:

$this->actingAs($user);
$post = Post::factory()->create();

$this->assertTrue($post->isManagedBy($user));
$this->assertCount(1, $post->fresh()->managers);

Integrate, don't duplicate. If you already use spatie/laravel-activitylog or a custom audit table for field-level diffs, keep this package to the single question it answers ("who managed this?") rather than maintaining two parallel trails of the same events.

Gotchas and Tips

The package is archived (last release Nov 2023). Treat it as vendored code you own. Before adopting, check composer.json constraints against your Laravel version; on Laravel 11+/12 you may need to fork and bump constraints, or reimplement the ~100 lines yourself. The trait-and-event approach is trivial to reproduce if the archived state becomes a blocker.

Model events only — query-builder writes are invisible. Post::where(...)->update([...]) and ->delete() fire no model events, so no management rows. Same for insert() and truncation. If your app does bulk operations, you'll get silent gaps in the trail; use ->eachById(fn ($p) => $p->update(...)) where attribution matters.

Appended, not deduplicated. Expect many rows per model per user. Budget for table growth: add an index on (manageable_type, manageable_id) and on user_id if you ever query "what has this user touched?". Plan a pruning job or an archival strategy before the table reaches millions of rows.

It records who, not what changed. There's no old/new value diff. Don't sell it internally as an audit log — for compliance you need field-level history (activitylog, audit tables, or DB triggers).

Config caching and the publish step. php artisan config:cache will freeze the user model/table config; re-run it in deploys after changing config/manageable.php. Also publish and migrate in the right order on fresh installs — migrate first, then any code that uses the trait, or tests will fail on a missing table.

Soft deletes and restore. A restored event isn't the same as created; confirm how your version handles it. If restores should count as a management action, listen for restored yourself and write the row explicitly.

Debugging silently missing rows. Checklist in order: (1) trait actually used on the model, (2) a user is authenticated in the current guard, (3) the config's user model matches your app's, (4) the write went through a model method, not a query builder, (5) the migration actually ran in that environment (check migrate:status).

Extension points. The trait is the seam: override the manager resolution or the relationship in a subclass if you need tenant scoping or a manager_type filter. For anything more, fork — the package's value is the convention, not the code mass.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core