From superpowers-sage
Defines and manages Laravel-style routes in WordPress/Sage/Bedrock using Acorn, including controllers, middleware, route model binding, and API endpoints.
How this skill is triggered — by the user, by Claude, or both
Slash command
/superpowers-sage:acorn-routesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- Custom endpoints not mapped to WordPress content (forms, APIs, dashboards, webhooks)
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.
npx claudepluginhub hekivo/superpowers-sageCreates, modifies, and debugs WordPress REST API endpoints, including authentication, schema validation, and coexistence with Acorn Routes.
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.
Build RESTful APIs with Laravel using API Resources, Sanctum authentication, rate limiting, and versioning. Use when creating API endpoints, transforming responses, or handling API authentication.