From superpowers-sage
Sets up Laravel routing with Acorn in WordPress/Sage/Bedrock for custom endpoints, APIs, forms, dashboards, webhooks using controllers, middleware, and route model binding.
npx claudepluginhub codigodoleo/superpowers-sage --plugin superpowers-sageThis skill uses the workspace's default tool permissions.
- Custom endpoints not mapped to WordPress content (forms, APIs, dashboards, webhooks)
Implements Laravel-style HTTP middleware in Acorn for WordPress/Sage/Bedrock routes, including JWT auth, rate limiting, CORS, custom guards, and role checks.
Promotes Laravel route best practices: map requests to controllers only, avoid inline business logic, validation, or DB operations. Includes anti-patterns and refactoring examples for clean routes.
Builds, extends, and debugs WordPress REST API endpoints/routes using register_rest_route, WP_REST_Controller, schema/argument validation, permissions, response shaping, and custom fields/meta.
Share bugs, ideas, or general feedback.
register_rest_route() for block editoradd_menu_page() / add_submenu_page()RouteServiceProvider registered in config/app.phproutes/web.php and/or routes/api.php present in the theme// app/Providers/RouteServiceProvider.php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->routes(function () {
Route::middleware('web')
->group($this->app->basePath('routes/web.php'));
Route::middleware('api')
->prefix('api')
->group($this->app->basePath('routes/api.php'));
});
}
}
Register in config/app.php:
'providers' => [
// ...
App\Providers\RouteServiceProvider::class,
],
# Create a standard controller (PascalCase name required)
bash skills/acorn-routes/scripts/create-controller.sh HomeController
# Create a full resource controller (index/create/store/show/edit/update/destroy)
bash skills/acorn-routes/scripts/create-controller.sh ProjectController --resource
# Create an API controller (no create/edit HTML methods)
bash skills/acorn-routes/scripts/create-controller.sh ProjectController --api
# Create a single-action invokable controller
bash skills/acorn-routes/scripts/create-controller.sh ExportReportController --invokable
Script: scripts/create-controller.sh
// routes/web.php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ContactController;
Route::get('/contact', [ContactController::class, 'show'])->name('contact.show');
Route::post('/contact', [ContactController::class, 'submit'])->name('contact.submit');
Route::put('/profile/{user}', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/account/{user}', [AccountController::class, 'destroy'])->name('account.destroy');
// Full resource: index, create, store, show, edit, update, destroy
Route::resource('projects', ProjectController::class);
// Partial resource
Route::resource('events', EventController::class)->only(['index', 'show']);
Route::resource('comments', CommentController::class)->except(['destroy']);
Route::prefix('admin')->middleware('auth')->group(function () {
Route::get('/reports', [ReportController::class, 'index'])->name('admin.reports');
Route::post('/reports/export', [ReportController::class, 'export'])->name('admin.reports.export');
});
Always name routes. Use route() helper in Blade and controllers — never hardcode URLs:
Route::get('/projects/{project:slug}', [ProjectController::class, 'show'])->name('projects.show');
// In controllers
return redirect()->route('projects.show', ['project' => $project->id]);
// In Blade
<a href="{{ route('projects.show', $project) }}">View</a>
Controllers live in app/Http/Controllers/. See references/controllers.md for:
wp_set_current_user note (use in middleware, not constructors)class ProjectController extends Controller
{
public function __construct(protected ProjectService $projects) {}
public function index(): View
{
return view('projects.index', [
'projects' => $this->projects->getPublished(),
]);
}
}
Boilerplate templates with {{PLACEHOLDER}} tokens:
{{CLASS_NAME}}, {{VIEW_PREFIX}}, {{ROUTE_PREFIX}}.{{CLASS_NAME}}.When the route parameter name matches the type-hinted variable, Laravel resolves the model automatically:
Route::get('/projects/{project}', [ProjectController::class, 'show']);
// Controller
public function show(Project $project): View { /* $project auto-resolved */ }
Resolve by slug:
Route::get('/projects/{project:slug}', [ProjectController::class, 'show']);
See references/route-model-binding.md for:
resolveRouteBinding() override// Single route
Route::get('/dashboard', [DashboardController::class, 'index'])->middleware('auth');
// Group
Route::middleware(['auth', 'verified'])->group(function () {
Route::resource('projects', ProjectController::class);
});
// With parameters
Route::post('/admin/users', [UserController::class, 'store'])->middleware('role:admin');
For creating custom middleware, see superpowers-sage:acorn-middleware.
See references/middleware-groups.md for:
web vs api groups// routes/api.php — automatically prefixed /api, stateless
Route::apiResource('projects', \App\Http\Controllers\Api\ProjectController::class);
Route::post('/webhooks/stripe', [StripeWebhookController::class, 'handle'])
->middleware('verify.stripe.signature');
See references/api-endpoints.md for:
JsonResourceEnsureJsonResponse middleware (Accept: application/json)RateLimiter/app/, /portal/, or /api/ to avoid colliding with WP content URLs./wp-admin/) and WP REST API (/wp-json/) are unaffected.# List all routes with methods, URIs, controllers, names, middleware
lando acorn route:list
# Filter by path segment
lando acorn route:list --path=api
# Filter by HTTP method
lando acorn route:list --method=POST
# Cache routes for production (no closure routes allowed)
lando acorn route:cache
# Clear route cache (always do this during development)
lando acorn route:clear
routes/web.php or routes/api.php — never define Acorn routes in functions.php.Route::middleware() for auth/access control — never add_action('init') or WP hooks on Acorn routes.lando acorn route:list before deploying.->name('resource.action') — so route() helper and redirects work.wp_set_current_user() belongs in auth middleware, not in controller constructors.lando acorn route:list and verify the route has correct method, URI, controller, name, middleware.lando acorn tinker → route('route.name') returns the expected URL.See references/troubleshooting.md for:
current_user_can() returning false in controllersDeep content loaded on demand — zero tokens until needed.
wp_set_current_user note for constructors.resolveRouteBinding(), WP post ID binding, scoped bindings.web/api groups, ordering.Accept: application/json detection, versioning./wp-json/): use register_rest_route() — see superpowers-sage:wp-rest-api.superpowers-sage:acorn-middleware for Kernel setup and JWT.Before adding routes that reference controllers or post types, query:
execute-ability routes/list
Use real route slugs and controller names from the query.
See sageing/references/mcp-query-patterns.md.