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.
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
composer require webman/adminphp webman plugin:install webman/admin) so the package is copied into plugin/admin/ and its config is merged into config/plugin/webman/admin/.wa_). Import the shipped install.sql to create the menu/role/admin/attachment/log tables and the seed admin user.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".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.
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.
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.
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:
admin. The bypass hides every permission bug you have.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.
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.
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.
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.
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".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.->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.support\validation. Put rules in one place per controller (a rules() method or a validator class) or they will diverge between add and edit.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.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.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.How can I help you explore Laravel packages today?