Repository navigation
feat: Runtime\Dispatcher — call an action without HTTP - #19
Merged
Merged
Conversation
ApiInvocation goes through the router the way a request to the action's
URL would (middleware cascade, validation, auth, ApiRequest) and comes
back as an ApiResult read from the {success, payload} envelope. It runs
inside the current request as the same caller and restores that request,
the facades, ApiRequest and the router's current route afterwards. GET
input travels as a query string, the rest as a JSON body.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 26, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First step of stage 2 (transports): a way to call an action that isn't an HTTP request of its own — what MCP / JSON-RPC will build on.
What
ApiInvocation— version, controller, action, params, headers, optional HTTP method (default: the action's first).Dispatcher— builds aBaseApiRequestfor the action's URI and sends it throughRouter::dispatch(): the route middleware cascade,FormRequest/validation,auth(),ApiRequest, route-cache — all as over HTTP. It does not re-run the global kernel middleware: the request it runs inside already went through it. Any throwable →ApiErrorHandler, likeBaseApi::callAction.ApiResult::fromResponse()— reads the{success, payload}envelope from the body, the way an HTTP client does (so a middleware refusal or the error handler's answer reads the same as a controller's); falls back to the decoded body / raw text / empty.HTTP_*headers (not content-type/length) and cookies are passed on; the invocation's headers on top.request,Route::class, therequestandapi_requestfacades,BaseApiRequest::$_instance(newBaseApiRequest::swapInstance()), and the router's protected$current/$currentRequest.Deviation from the plan
The plan had
ApiResult extends JsonResponsereturned byApiResponseHelper::say(). Not needed: reading the body the way a client does works for every response (controller, middleware refusal, error handler) without touching the response types — so nothing in the HTTP path changes.ApiResultis a plain value object. Likewise no separate "neutral error" layer: the error handler's answers already carryerrorKey/messagein the envelope.Not in this PR
terminate()of terminable middleware isn't called for a dispatched call (no kernel). A "pure" call without a synthetic request stays a 7.0 topic.Tests
608 passing; golden-master unchanged. New
tests/Feature/Runtime/DispatcherTest.php(7): a call and its answer; middleware cascade (refused without the header, passes with it); GET query vs JSON body input; default vs explicit HTTP method (wrong method → 404http_error); unknown action and inactive version; a call inside an HTTP request — authenticated by the outer header, and afterwardsrequest(),Route::current(),request()->input()andApiRequest::input()are the outer request's again;ApiResulton non-envelope / non-JSON / empty answers.Docs: README (en/ru/zh/de) "Calling an action without HTTP", CHANGELOG, CLAUDE.md.
🤖 Generated with Claude Code