Acorn routes
Routes, controller, Acorn routes, web.php, route:list, Route::get, Route::post, Route::resource, Route::apiResource, middleware, route model binding, API endpoint, RouteServiceProvider, named routes, resource controller, single-action controller, invokable controller, route group, route prefix, JSON response, rate limiting, token auth, Accept application/json, route cache, WP rewrite conflicts — using Acorn's Laravel routing inside WordPress/Sage/BedrockFrom its SKILL.md
npx -y skills add hekivo/superpowers-sage --skill acorn-routesAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 13 stars13 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
10.5 KB, as published. Nobody here has run it
Acorn Routes
When to use
- Custom endpoints not mapped to WordPress content (forms, APIs, dashboards, webhooks)
- REST-style JSON endpoints with Laravel middleware, DI, and controller organization
- Frontend routes that render Blade views with Laravel Livewire
- Endpoints that need middleware chains (auth, rate-limit, CSRF)
- Route model binding with Eloquent models
When NOT to use
- Permalink-based content routing (posts, pages, archives) — WordPress template hierarchy
- Gutenberg REST endpoints — must stay on
register_rest_route()for block editor - Admin menus and settings pages — use
add_menu_page()/add_submenu_page() - URLs expected to participate in canonical redirects and SEO plugin hooks
Prerequisites
- Acorn installed in the theme
RouteServiceProviderregistered inconfig/app.phproutes/web.phpand/orroutes/api.phppresent in the theme
RouteServiceProvider Setup
// 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,
],
Quick Start — Scripts
# 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
Route Definitions
Basic Routes
// 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');
Resource Routes
// 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 Groups
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');
});
Named Routes
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
Controllers live in app/Http/Controllers/. See references/controllers.md for:
- Base controller setup
- Constructor injection patterns
wp_set_current_usernote (use in middleware, not constructors)- Resource, API, and invokable controller full examples
Quick Example
class ProjectController extends Controller
{
public function __construct(protected ProjectService $projects) {}
public function index(): View
{
return view('projects.index', [
'projects' => $this->projects->getPublished(),
]);
}
}
Assets
Boilerplate templates with {{PLACEHOLDER}} tokens:
- controller-resource.php.tpl — All 7 REST methods. Replace
{{CLASS_NAME}},{{VIEW_PREFIX}},{{ROUTE_PREFIX}}. - controller-api.php.tpl — API controller (no create/edit). Replace
{{CLASS_NAME}}.
Route Model Binding
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:
- Scoped bindings for nested resources
resolveRouteBinding()override- WordPress post ID binding
- Missing model customization
Middleware
// 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:
- Named middleware aliases (Kernel setup)
webvsapigroups- Middleware ordering rules
API Routes
// 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:
- Full API controller structure with
JsonResource EnsureJsonResponsemiddleware (Accept: application/json)- Rate limiting with custom
RateLimiter - Token authentication middleware
- Versioned API setup
WordPress Coexistence
- Acorn routes take precedence when they match a URL — they bypass WP rewrite rules entirely.
- Prefix Acorn routes with
/app/,/portal/, or/api/to avoid colliding with WP content URLs. - WordPress admin (
/wp-admin/) and WP REST API (/wp-json/) are unaffected. - No permalink flush needed — Acorn routes are not registered in WP's rewrite table.
Lando Commands
# 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
Critical Rules
- Always use
routes/web.phporroutes/api.php— never define Acorn routes infunctions.php. - Use
Route::middleware()for auth/access control — neveradd_action('init')or WP hooks on Acorn routes. - Check for WP rewrite conflicts with
lando acorn route:listbefore deploying. - Never use closures in route files — they cannot be cached. Use controller classes.
- Always name routes —
->name('resource.action')— soroute()helper and redirects work. - Prefix Acorn routes to avoid colliding with WordPress page slugs and content URLs.
wp_set_current_user()belongs in auth middleware, not in controller constructors.
Verification
- Visit the route URL and confirm expected response (HTML or JSON) with correct status code.
- Run
lando acorn route:listand verify the route has correct method, URI, controller, name, middleware. - Test named routes:
lando acorn tinker→route('route.name')returns the expected URL.
Failure modes
See references/troubleshooting.md for:
- 404 on a defined route
- Route conflicts with WP rewrite rules
- Middleware not firing
- Route model binding not resolving
current_user_can()returning false in controllers- CSRF token mismatches
- Route cache issues
References
Deep content loaded on demand — zero tokens until needed.
- controllers.md — Controller class structure, constructor injection, resource/API/invokable controllers,
wp_set_current_usernote for constructors. - route-model-binding.md — Implicit and explicit binding,
resolveRouteBinding(), WP post ID binding, scoped bindings. - middleware-groups.md — Middleware assignment in routes, named middleware aliases, group wrapping,
web/apigroups, ordering. - api-endpoints.md — JSON API responses, rate limiting, token auth,
Accept: application/jsondetection, versioning. - troubleshooting.md — 404 on Acorn routes, WP rewrite conflicts, middleware not firing, route cache, CSRF issues.
Escalation
- Route needs WordPress REST API (
/wp-json/): useregister_rest_route()— seesuperpowers-sage:wp-rest-api. - Middleware not behaving: see
superpowers-sage:acorn-middlewarefor Kernel setup and JWT.
Query First — MCP Integration
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.