Controllers
Introductionâ
Controllers live in backend/app/HTTP/Controllers and hold the handling logic for a route. They are plain classes with no base class to extend:
namespace YourPlugin\HTTP\Controllers;
if (!defined('ABSPATH')) {
exit;
}
use YourPlugin\Deps\BitApps\WPKit\Http\Response;
use YourPlugin\Model\Tag;
final class TagController
{
public function index()
{
return Tag::get(['id', 'title', 'status']);
}
}
Mark controllers final and guard the file with the ABSPATH check, as every file in the plugin does.
Receiving the requestâ
Type-hint Request on the method and the router injects it:
use YourPlugin\Deps\BitApps\WPKit\Http\Request\Request;
public function destroy(Request $request)
{
$tag = Tag::findOne(['id' => $request->tagId ?? $request->id]);
if (!$tag) {
return Response::error('Tag not found');
}
$tag->delete();
return Response::success('Tag deleted successfully');
}
Route parameters are injected by name alongside the request:
// Route::get('node/{flow_id}/{node_id}', [NodeController::class, 'show']);
public function show($flow_id, $node_id)
{
// ...
}
See Request for the full input API.
Validating inputâ
Call validate() with the rules for the action. It returns only the validated (and sanitized) values:
public function store(Request $request)
{
$validated = $request->validate([
'title' => ['required', 'sanitize:text', new UniqueRule(Tag::class, 'title')],
'filter' => ['nullable', 'string', 'sanitize:text'],
]);
$validated['slug'] = Slug::generate($validated['title']);
return Response::success(Tag::insert($validated));
}
Validation failure never reaches the next line â the request is answered with a 422 JSON body. See Validation.
For rule sets that are reused or long enough to crowd the controller, move them into a form request.
Returning responsesâ
Response::success() and Response::error() build the JSON envelope:
return Response::success($tag); // 200
return Response::error('Failed to save tag'); // 400
return Response::error('Access denied')->httpStatus(411); // custom status
Both accept a string or any serializable payload. Chainable helpers cover the rest of the envelope:
return Response::success($data)
->message('Flow saved')
->code('FLOW_SAVED')
->httpStatus(201);
Returning a bare value works too â the router serializes it â but the envelope keeps client-side handling uniform:
public function index()
{
return Tag::get(['id', 'title', 'status']); // serialized as-is
}
Method namingâ
Controllers follow REST-ish naming, so a reader can guess the route from the method:
| Method | Purpose |
|---|---|
index | List records |
show | Fetch one record |
store | Create |
update | Modify |
destroy | Delete |
Anything outside that set gets a descriptive name, such as reExecuteFlow or handleCallback.