AdminPanelBuilder — Developer Documentation
AdminPanelBuilder is a JSON-driven generator that produces a Laravel admin panel foundation (Admin Auth, User Auth, Role/Permission management, Landing page) and your business modules (CRUD + API + Admin UI). Developers define what to build using a schema; the system generates how to build it.
Quickstart
Start with 1–2 modules, verify output, then expand schema. Keep schema in version control — it becomes both the blueprint and the team’s contract.
1) Write your schema
{
"config": { "preset": "full" },
"modules": {
"brand": {
"label": "Brand",
"is_modal": true,
"fields": {
"name": { "type": "text", "db": "string", "required": true },
"status": { "type": "radio", "options": ["active","inactive"], "db": "string", "required": true }
},
"api": { "public": { "endpoints": ["index","show"] } }
}
}
}
2) Generate & download zip
- Submit schema through your generator UI/endpoint.
- Download zip and unzip locally.
3) Install & run
composer install
cp .env.example .env
php artisan key:generate
php artisan migrate
php artisan serve
Admin auth area, user auth area, role/permission pages, a basic landing page, and your generated modules.
Generated Foundation (Built-in)
The generated project includes a stable foundation so teams don’t waste time rebuilding common admin features. Documenting these is important because developers must know what already exists.
| Feature | What it provides | Why it matters |
|---|---|---|
| Admin authentication | Admin login + admin-protected routes | Separates admin access from public/user |
| User authentication | User login/registration for user-scoped features | Enables user APIs safely (ownership rules) |
| Role management | Roles + permissions + assign access to modules/actions | Prevents “everyone is super admin” |
| Landing page | Simple public entry page (replaceable) | Gives a clean default public route |
| Scopes | admin/user/public route exposure per module | Same module can be private/admin-only or partially public |
Admin scope uses admin authentication; user scope uses user authentication; public scope requires no auth.
Schema Overview
The schema has two top-level keys:
config (global settings) and modules (entities).
{
"config": { ... },
"modules": {
"module_key": { ... },
"another_module": { ... }
}
}
Missing keys usually do not break generation. The system applies safe defaults so new developers can move fast.
Legacy compatibility
Older schema styles may be accepted, such as:
api.endpoints or web.endpoints.
The adapter typically normalizes them to api.admin.endpoints and web.admin.endpoints.
config Reference
config.preset
| Value | Meaning | Use-case |
|---|---|---|
full | Generate DB + Models + API + Admin UI (+ extras) | Most projects |
api_only | DB + Models + API | Mobile/API-first |
admin_only | DB + Models + Admin UI | Internal admin tools |
db_only | Migrations + Models only | Foundation first |
full.
config.generate (global feature switches)
Optional override flags. Use when you need fine control beyond preset.
| Flag | Effect |
|---|---|
migration | Generate migrations |
model | Generate models (+ casts/relations) |
api | Generate API layer |
api_routes | Write API routes |
api_controller | Generate API controllers |
views | Generate admin blade views |
web_routes | Write web/admin routes |
web_controller | Generate web/admin controllers |
postman | Generate Postman collection |
sql | Generate SQL output |
Each module can define generate to override global flags for that module only.
config.pack (zip packaging)
| Key | Meaning | Recommended |
|---|---|---|
pack | Controls export size | lite for dev, full for deployment |
include_env | Include .env inside zip | Usually false (dev sets env) |
include_vendor | Include vendor/ inside zip | Usually false (composer install) |
config.sidebar
{
"config": {
"sidebar": [
{
"group": "Catalog",
"icon": "layers",
"items": [
{ "module": "category", "label": "Categories" },
{ "module": "sub_category", "label": "Sub Categories" },
{ "module": "child_category", "label": "Child Categories" }
]
},
{
"group": "Products",
"icon": "box",
"items": [
{ "module": "brand", "label": "Brands", "icon": "tag" },
{ "module": "product", "label": "Products" }
]
}
]
}
}
config.dashboard
{
"config": {
"dashboard": {
"widgets": [
{ "type": "stats", "modules": ["category","brand","product"] },
{ "type": "recent", "module": "product", "limit": 10 },
{ "type": "status_summary", "module": "brand", "field": "status" },
{ "type": "chart_count_by_day", "module": "product", "days": 14 },
{ "type": "line_chart", "module": "order", "days": 30, "label": "Orders trend" },
{ "type": "bar_chart", "module": "product", "field": "category_id", "limit": 8 },
{ "type": "pie_chart", "module": "order", "field": "status" },
{ "type": "kpi_card", "module": "user", "metric": "count", "compare_days": 7 },
{ "type": "table_recent", "module": "order", "columns": ["id","invoice","status","created_at"], "limit": 8 },
{ "type": "trend_compare", "module": "order", "days": 7 }
]
}
}
}
stats— quick counts per modulerecent— recent rows table (auto columns)status_summary— grouped counts by status fieldchart_count_by_day— activity bars by dayline_chart— time-series trend linebar_chart— top values by fieldpie_chart— distribution by fieldkpi_card— single metric with optional deltatable_recent— custom recent tabletrend_compare— current vs previous period
modules Reference
| Key | Type | What it controls | If missing |
|---|---|---|---|
label | string | UI name for module | Auto from module key |
is_modal | boolean | Modal create/edit UX | false |
generate | object | Override generation flags | Uses global |
fields | object | DB columns + forms + validation | Empty |
api | object | API scopes + endpoints | Defaults if API enabled |
web | object | Web scopes + endpoints | Defaults if Web enabled |
compose | object | Multi-table store/update | Off |
Module-level generate override example
{
"config": { "preset": "api_only" },
"modules": {
"brand": {
"generate": { "views": true, "web_routes": true, "web_controller": true },
"fields": { "name": { "required": true } },
"web": { "admin": { "endpoints": ["index","create","edit"] } }
}
}
}
fields Reference (Types, DB, UI, Validation)
A field definition drives: migration column type (db), admin input rendering (type),
and validation (required, unique, array rules for json fields, etc.).
Supported input type values
| type | Meaning | Common keys | Notes |
|---|---|---|---|
text | Single-line input | label, required, unique | Default type if missing |
number | Numeric input UX | db, precision, scale | DB decides integer/decimal |
textarea | Multi-line input | db, rich | Rich editor enabled by default |
select | Dropdown | options, multiple | Use db=json for multiple |
checkbox | Multi choice | options, multiple | Usually db=json |
radio | Single choice | options | Good for status, gender, etc. |
file | Upload | mimes, is_image, size | Stores path/string usually |
image | Image upload alias | mimes, size | Behaves like file + is_image |
relation | FK relation picker | relation.kind/target/display | db=foreignId recommended |
Supported db / column types
| db | Common usage | Extra params |
|---|---|---|
string | Short text | length |
text | Long text | — |
integer | Counts, quantity | — |
bigInteger | Large counters | — |
boolean | True/false | — |
decimal | Money/price | precision, scale |
float | Approx numbers | — |
double | Approx numbers | — |
json | Arrays/objects | — |
enum | Constrained string | options |
foreignId | Relations | relation target |
date | Date only | — |
datetime | Date + time | — |
timestamp | Unix style | — |
Field helper & UI keys (IMPORTANT)
These keys improve developer experience and UI without custom blade edits:
| Key | Meaning | Notes |
|---|---|---|
label | Override human label | Default label derived from field key |
help / hint / description / note | Help text below input | Multiple aliases supported |
attributes | Extra HTML attributes | placeholder, readonly, min, max, step, etc. |
full_width | Force UI full row width | Useful for long text or editors |
required | Validation required | Default false |
unique | Validation unique | Use for SKU, slugs, etc. |
Textarea rich editor behavior
type: "textarea" typically renders as a WYSIWYG editor (e.g., Summernote).
To render a plain textarea, set rich: false.
{
"fields": {
"description": { "type":"textarea", "db":"text" },
"short_note": { "type":"textarea", "db":"text", "rich": false }
}
}
Multiple select / checkbox (store as JSON array)
If multiple: true, use db: "json" so the model can cast to array and validation can treat it as an array.
{
"fields": {
"tags": {
"type": "select",
"options": ["new","featured","sale"],
"multiple": true,
"db": "json"
}
}
}
Status field special behavior
If the field key is status, the system may generate a dedicated status action/UI.
The display mode depends on data type and option count:
| Status definition | Behavior |
|---|---|
db: "boolean" or numeric boolean-like | Toggle mode |
db: "string" or enum with exactly 2 options | Active/Inactive style toggle |
| Other option sets | Normal select/radio behavior |
Auto UI sections (Basic / SEO / Media)
The admin form is automatically grouped into sections:
- Basic: normal fields
- SEO: fields starting with
meta_(example: meta_title, meta_description) - Media: file/image fields
{
"fields": {
"name": { "type":"text", "db":"string" },
"meta_title": { "type":"text", "db":"string" },
"image": { "type":"file", "db":"string", "is_image": true }
}
}
Relations
Relation field format
{
"fields": {
"category_id": {
"type": "relation",
"db": "foreignId",
"required": true,
"relation": {
"kind": "belongsTo",
"target": "category",
"display": "name"
}
}
}
}
Supported relation kinds
Common supported kinds include: belongsTo hasOne hasMany belongsToMany
If relation_between: true is set on a relation field, the generator attempts to create the inverse relation on the target model automatically.
{
"fields": {
"category_id": {
"type": "relation",
"db": "foreignId",
"relation": { "kind": "belongsTo", "target": "category", "display":"name" },
"relation_between": true
}
}
}
Scopes & Endpoints (api/web)
Both API and web layers support scopes: admin user public
Allowed endpoints
| Layer | Common endpoints | Notes |
|---|---|---|
| API | index, show, store, update, destroy, status, track |
track is commonly used with token ownership (public tracking). |
| Web | index, create, store, edit, update, destroy, status |
Admin UI endpoints |
Scope configuration example
{
"api": {
"admin": { "endpoints": ["index","show","store","update","destroy","status"] },
"user": { "endpoints": ["index","show","store"] },
"public": { "endpoints": ["index","show"] }
},
"web": {
"admin": { "endpoints": ["index","create","edit","status"] }
}
}
Middleware per scope (optional)
If your system supports it, you can override middleware per scope:
{
"api": {
"admin": { "endpoints": ["index"], "middleware": ["auth:admin"] },
"user": { "endpoints": ["index"], "middleware": ["auth:sanctum"] },
"public": { "endpoints": ["index"], "middleware": [] }
}
}
Ownership (auth mode & token mode)
Ownership prevents user A from reading/updating user B’s records in user-scoped endpoints.
Ownership: auth mode
{
"api": {
"user": {
"endpoints": ["index","show","store","update","destroy"],
"ownership": {
"mode": "auth",
"field": "user_id",
"create_owner": true
}
}
}
}
| Key | Meaning |
|---|---|
mode: "auth" | Ownership comes from authenticated user |
field | DB field storing owner id (commonly user_id) |
create_owner | Auto-set owner field on create |
admin_bypass | Admin can bypass ownership filter (if enabled) |
Ownership: token mode (public tracking patterns)
Token ownership allows access based on a token (query param or path param) matching a stored field.
Commonly used with a public track endpoint.
{
"api": {
"public": {
"endpoints": ["store","track"],
"ownership": {
"mode": "token",
"token_field": "tracking_token",
"param": "token",
"source": "query",
"admin_bypass": true
}
}
}
}
| Key | Meaning |
|---|---|
mode: "token" | Ownership based on token lookup |
token_field | DB field holding token (e.g. tracking_token) |
param | Request param name (e.g. token) |
source | Where token is read from (query/path) |
admin_bypass | Admin can bypass (optional) |
Compose (Multi-table store/update)
Compose solves real admin workflows: Product + Images + Variations + Stock in one submit.
Compose keys
| Key | Meaning |
|---|---|
compose.store.with | Child modules to create during parent store |
compose.update.with | Child modules to update during parent update |
compose.payload.parent_key | Payload key where parent object lives |
compose.payload.children_keys | Child module → request payload key mapping |
compose.single | Child module treated as object instead of array |
Compose template
{
"compose": {
"store": { "with": ["childA","childB"] },
"update": { "with": ["childA","childB"] },
"payload": {
"parent_key": "parent",
"children_keys": {
"childA": "itemsA",
"childB": "itemsB"
}
},
"single": ["childB"]
}
}
Full example (Product + images + variations + stock)
{
"modules": {
"product": {
"label": "Product",
"fields": {
"name": { "type":"text", "db":"string", "required": true },
"status": { "type":"select", "options":["active","inactive"], "db":"string", "required": true }
},
"compose": {
"store": { "with": ["product_image","product_variation","product_stock"] },
"update": { "with": ["product_image","product_variation","product_stock"] },
"payload": {
"parent_key": "product",
"children_keys": {
"product_image": "images",
"product_variation": "variations",
"product_stock": "stock"
}
},
"single": ["product_stock"]
}
},
"product_image": {
"label": "Product Images",
"fields": {
"product_id": {
"type":"relation","db":"foreignId","required": true,
"relation": { "kind":"belongsTo", "target":"product", "display":"name" }
},
"image": { "type":"image", "db":"string", "required": true, "mimes":["jpg","jpeg","png"] }
}
},
"product_variation": {
"label": "Product Variations",
"fields": {
"product_id": {
"type":"relation","db":"foreignId","required": true,
"relation": { "kind":"belongsTo", "target":"product", "display":"name" }
},
"sku": { "type":"text","db":"string","required": true, "unique": true },
"color": { "type":"select","options":["Red","Green","Blue"], "db":"string" },
"size": { "type":"select","options":["S","M","L","XL"], "db":"string" },
"price": { "type":"number","db":"decimal","precision":10,"scale":2,"required": true }
}
},
"product_stock": {
"label": "Product Stock",
"fields": {
"product_id": {
"type":"relation","db":"foreignId","required": true,
"relation": { "kind":"belongsTo", "target":"product", "display":"name" }
},
"quantity": { "type":"number","db":"integer","required": true },
"warehouse": { "type":"text","db":"string" }
}
}
}
}
Client must send payload keys matching compose.payload:
product, images, variations, stock.
Recipes (Copy/Paste)
1) Minimal module
{
"modules": {
"brand": {
"fields": {
"name": { "type":"text", "db":"string", "required": true }
}
}
}
}
2) Catalog relations (category → sub_category → child_category)
{
"modules": {
"category": {
"fields": { "name": { "type":"text","db":"string","required": true } }
},
"sub_category": {
"fields": {
"category_id": {
"type":"relation","db":"foreignId","required": true,
"relation": { "kind":"belongsTo","target":"category","display":"name" },
"relation_between": true
},
"name": { "type":"text","db":"string","required": true }
}
},
"child_category": {
"fields": {
"subcategory_id": {
"type":"relation","db":"foreignId","required": true,
"relation": { "kind":"belongsTo","target":"sub_category","display":"name" }
},
"name": { "type":"text","db":"string","required": true }
}
}
}
}
3) Multiple select stored as JSON
{
"modules": {
"product": {
"fields": {
"name": { "type":"text","db":"string","required": true },
"tags": { "type":"select","options":["new","featured","sale"], "multiple": true, "db":"json" }
}
}
}
}
4) Textarea rich editor OFF
{
"modules": {
"page": {
"fields": {
"title": { "type":"text","db":"string","required": true },
"body": { "type":"textarea","db":"text", "rich": false, "full_width": true }
}
}
}
}
5) Token tracking endpoint (public)
{
"modules": {
"delivery": {
"fields": {
"tracking_token": { "type":"text","db":"string","required": true, "unique": true },
"status": { "type":"select","options":["pending","shipped"], "db":"string","required": true }
},
"api": {
"public": {
"endpoints": ["track"],
"ownership": {
"mode": "token",
"token_field": "tracking_token",
"param": "token",
"source": "query"
}
}
}
}
}
}
Gotchas (Common mistakes)
If multiple: true and you store as string, saving + reading will become inconsistent.
Use db: "json".
relation.target must match an existing module key exactly.
Client payload keys must match compose.payload.children_keys.
- Keep schema in Git, version it (e.g., add
config.version). - Start admin-only, then enable user/public scopes intentionally.
- Use
label, help text, andattributes.placeholderto make admin UI self-explanatory.
If you want this doc to be even more Laravel-like, next step is splitting into multiple pages (Field Types page, Ownership page, Compose page) and adding “Edit this page” links. But the current single-page version is production-friendly and easy to ship inside your generator.