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

Admin Laravel Package

webman/admin

webman/admin is a ready-to-use admin panel for Webman applications, offering user, role, and permission management, menu configuration, and CRUD scaffolding to help you build secure backends quickly with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Think of webman/admin as Filament + a CRUD generator + spatie/laravel-permission, but for webman and with a layui-based (not Livewire/Inertia) frontend. It is not a library you use in a controller — it is a runnable admin application that installs into plugin/admin/ and bootstraps its own routes, session auth, RBAC tables, upload endpoint, and code generator.

Minimal path to a running panel

  1. composer require webman/admin
  2. Run the plugin installer (php webman plugin:install webman/admin) so the package is copied into plugin/admin/ and its config is merged into config/plugin/webman/admin/.
  3. Point it at a database (the admin uses the app's DB connection; default table prefix is wa_). Import the shipped install.sql to create the menu/role/admin/attachment/log tables and the seed admin user.
  4. Make sure webman/session is installed and a session handler is configured — a missing/broken session store is the #1 cause of "login succeeds, redirects straight back to login".
  5. php start.php start and visit http://127.0.0.1:8787/app/admin. Seed credentials are admin / 123456 — change them before anything else.

Where to look first, in this order

Path Why
plugin/admin/config/route.php Shows the /app/admin route group and middleware stack — your mental model of "what wraps every request"
plugin/admin/app/controller/ Login, index, upload, menu, role, and the CRUD base class the generator extends
plugin/admin/app/middleware/ Session/auth check and the access-control middleware that enforces permissions
Menu + role tables (wa_admin_menu, wa_admin_role*) Permissions are rows, not code — this is where access actually lives
The "代码生成" (code generator) page in the UI The highest-leverage feature; it turns a table into controller + model + views

First use case worth doing end-to-end: take one small existing table (say articles), run it through the generator, insert the resulting menu row, log in as a non-superadmin role that only has that one menu granted, and confirm you can list/add/edit/delete — and that a second controller is 403. That single loop teaches you 80% of the package.

Implementation Patterns

Schema-first, then generate, then customize in a separate layer

The intended workflow is: finalize the table → run the generator → get a working module → then add business logic. The generator emits a controller extending a base CRUD class, a model, and layui views. Treat generated files as scaffolding you own but do not hand-edit structurally — put custom behaviour in a service class under app/ (shared with your API) and keep the controller thin.

// Illustrative shape — the generator emits the real thing for your version
namespace app\admin\controller;

use plugin\admin\app\controller\Crud;   // generic list/add/edit/delete base

class Article extends Crud
{
    protected $model  = \app\model\Article::class;
    protected $fields = ['title', 'category_id', 'status', 'created_at'];

    // override only what differs
    protected function beforeSave(array $data): array
    {
        $data['updated_by'] = session('admin.id');
        return $data;
    }
}

Generate into your own app or plugin (app/admin/... or plugin/myapp/...), not into plugin/admin/. Anything you put inside plugin/admin/ is inside the package's install footprint and is the first thing lost in an upgrade.

The layui JSON contract is the real integration surface

Every list endpoint the admin frontend talks to must return layui-table shape, and every action endpoint must use the admin's envelope:

// list
return json(['code' => 0, 'msg' => '', 'count' => $total, 'data' => $rows]);

// action success / failure
return json(['code' => 0, 'msg' => 'ok']);
return json(['code' => 1, 'msg' => '标题不能为空']);

Note the inversion from what you're used to: code: 0 means success (layui convention), and HTTP status stays 200 for validation failures. If you return {"code":1} from a Laravel habit, tables render empty and dialogs silently close; if you return a 422, the frontend shows a generic error. This contract is what makes it possible to point a custom Vue/React page at the same endpoints later.

RBAC as data, with a superadmin bypass

Access is DB-driven: roles are granted menu/permission rows whose key maps to a controller@method pairing, and the access middleware compares the current route against the logged-in user's grants. Admin id 1 typically bypasses everything.

Day-to-day practice:

  • Add the menu row and the per-method permission rows whenever you add a controller — a controller without rows is invisible in the sidebar and 403 for everyone but the superadmin.
  • Never rename a controller method without updating the matching permission key; the rename fails open in menus (stale entry) and closed for access, which is confusing to debug.
  • Test as a real role account, not as admin. The bypass hides every permission bug you have.

Enums via the dictionary, not hardcoded arrays

Use the built-in dictionary (字典) for statuses, categories, and any option list that also needs to appear in a filter dropdown. One record feeds the form select, the list-column label, and the search filter — and non-developers can change labels without a deploy. Hardcoding [0 => '禁用', 1 => '启用'] in three places is the fastest way to build an admin that drifts.

Uploads and attachments

There is a single upload endpoint plus an attachment table. Store relative paths in your own tables and resolve to a URL at render time; the public base URL differs between dev, prod, and CDN. Reuse the admin's upload endpoint rather than writing a second one, or you'll end up with two storage conventions and orphaned files.

Cross-cutting concerns go in middleware, not the generator

Audit logging, tenant scoping, and "only the creator can edit" belong in middleware or a service layer applied to the route group — not in every generated controller. Webman middleware composes the same way as Laravel's; register it on the /app/admin group in plugin/admin/config/route.php (or your own route file) so regenerating a module doesn't drop the rule.

Reuse the admin's session for internal tooling

Custom admin pages and small internal tools can live inside the same session/auth as the panel: same cookie, same user, same permission check. You get login, roles, and logging for free — which is exactly the reason to pick this package over hand-rolling an admin.

Gotchas and Tips

  • Login loop / "验证码错误" forever. Almost always session config, not credentials: no webman/session dependency, an unwritable runtime/ dir, or a cookie domain mismatch behind a proxy. Fix session before debugging the auth code.
  • code: 0 for success, HTTP 200 always. Cover this in your first PR to the team. It is the single most common source of "the button does nothing".
  • Permissions are rows, not annotations. Adding a route does not grant access; grant it in the role UI or by inserting menu rows. Conversely, deleting a controller leaves orphan rows — prune them.
  • Superadmin bypass masks bugs. Always verify with a limited role.
  • plugin/admin/ is a copied install footprint. composer update + re-running the plugin installer can overwrite it. Keep it in git, review diffs after upgrades, and keep your code in app/ or your own plugin. Same rule for the generator: it overwrites, so generate into a scratch directory and diff when re-running against an evolved table.
  • Default ThinkORM, not Eloquent. ->with(), soft deletes, casts, and accessors exist with different semantics and different names. Budget an afternoon if you're porting Eloquent models; don't assume $model->relation lazy-loads identically, and check for N+1 in generated list queries — the generator produces straightforward queries, not eager-loaded ones.
  • No FormRequest equivalent. Validation is manual or via support\validation. Put rules in one place per controller (a rules() method or a validator class) or they will diverge between add and edit.
  • Table prefix is wa_ and it shares your application database. If you later want the admin isolated, that's a config decision to make on day one, not after 40 tables.
  • Debugging: run php start.php start in the foreground (not -d) to see exceptions in the console; logs land in runtime/logs/. Turn on debug/verbose error output in the admin config while developing so a 500 renders instead of a silent empty table.
  • Menu/permission changes are cached. If a grant doesn't take effect, clear the runtime cache and re-login before assuming the middleware is wrong.
  • Versioning and ecosystem risk. 377 stars, no public dependents, and release metadata that looks oddly future-dated — pin an exact version in composer.json, read the source when docs are thin (they are at workerman.net/doc/webman-admin), and expect to be your own support. It's a genuinely useful panel; just don't treat it as a black box you can upgrade blindly.
  • Extension points worth knowing: the base CRUD controller (override lifecycle hooks rather than rewriting actions), the dictionary service (single source for option lists), the access middleware (add tenant/ownership scoping here), and the generator templates (fork them if your team has house conventions — that's the cheapest way to make every future module consistent).
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource