.cursor/skills/laravel-actions/references/controller.md
asController)Use this reference when exposing an action through HTTP routes.
asController(...) and response adapters.routes() registration.ActionRequest.asController, jsonResponse, htmlResponse).handle(...).AsController trait)__invokeRequired so Laravel can register the action class as an invokable controller.
$action($someArguments);
// Equivalent to:
$action->handle($someArguments);
If the method does not exist, Laravel route registration fails for invokable controllers.
// Illuminate\Routing\RouteAction
protected static function makeInvokable($action)
{
if (! method_exists($action, '__invoke')) {
throw new UnexpectedValueException("Invalid route action: [{$action}].");
}
return $action.'@__invoke';
}
If you need your own __invoke, alias the trait implementation:
class MyAction
{
use AsAction {
__invoke as protected invokeFromLaravelActions;
}
public function __invoke()
{
// Custom behavior...
}
}
ControllerDecorator + ActionRequest)asControllerCalled when used as invokable controller. If missing, it falls back to handle(...).
public function asController(User $user, Request $request): Response
{
$article = $this->handle(
$user,
$request->get('title'),
$request->get('body')
);
return redirect()->route('articles.show', [$article]);
}
jsonResponseCalled after asController when request expects JSON.
public function jsonResponse(Article $article, Request $request): ArticleResource
{
return new ArticleResource($article);
}
htmlResponseCalled after asController when request expects HTML.
public function htmlResponse(Article $article, Request $request): Response
{
return redirect()->route('articles.show', [$article]);
}
getControllerMiddlewareAdds middleware directly on the action controller.
public function getControllerMiddleware(): array
{
return ['auth', MyCustomMiddleware::class];
}
routesDefines routes directly in the action.
public static function routes(Router $router)
{
$router->get('author/{author}/articles', static::class);
}
To enable this, register routes from actions in a service provider:
use Lorisleiva\Actions\Facades\Actions;
Actions::registerRoutes();
Actions::registerRoutes('app/MyCustomActionsFolder');
Actions::registerRoutes([
'app/Authentication',
'app/Billing',
'app/TeamManagement',
]);
prepareForValidationCalled before authorization and validation are resolved.
public function prepareForValidation(ActionRequest $request): void
{
$request->merge(['some' => 'additional data']);
}
authorizeDefines authorization logic.
public function authorize(ActionRequest $request): bool
{
return $request->user()->role === 'author';
}
You can also return gate responses:
use Illuminate\Auth\Access\Response;
public function authorize(ActionRequest $request): Response
{
if ($request->user()->role !== 'author') {
return Response::deny('You must be an author to create a new article.');
}
return Response::allow();
}
rulesDefines validation rules.
public function rules(): array
{
return [
'title' => ['required', 'min:8'],
'body' => ['required', IsValidMarkdown::class],
];
}
withValidatorAdds custom validation logic with an after hook.
use Illuminate\Validation\Validator;
public function withValidator(Validator $validator, ActionRequest $request): void
{
$validator->after(function (Validator $validator) use ($request) {
if (! Hash::check($request->get('current_password'), $request->user()->password)) {
$validator->errors()->add('current_password', 'Wrong password.');
}
});
}
afterValidatorAlternative to add post-validation checks.
use Illuminate\Validation\Validator;
public function afterValidator(Validator $validator, ActionRequest $request): void
{
if (! Hash::check($request->get('current_password'), $request->user()->password)) {
$validator->errors()->add('current_password', 'Wrong password.');
}
}
getValidatorProvides a custom validator instead of default rules pipeline.
use Illuminate\Validation\Factory;
use Illuminate\Validation\Validator;
public function getValidator(Factory $factory, ActionRequest $request): Validator
{
return $factory->make($request->only('title', 'body'), [
'title' => ['required', 'min:8'],
'body' => ['required', IsValidMarkdown::class],
]);
}
getValidationDataDefines which data is validated (default: $request->all()).
public function getValidationData(ActionRequest $request): array
{
return $request->all();
}
getValidationMessagesCustom validation error messages.
public function getValidationMessages(): array
{
return [
'title.required' => 'Looks like you forgot the title.',
'body.required' => 'Is that really all you have to say?',
];
}
getValidationAttributesHuman-friendly names for request attributes.
public function getValidationAttributes(): array
{
return [
'title' => 'headline',
'body' => 'content',
];
}
getValidationRedirectCustom redirect URL on validation failure.
public function getValidationRedirect(UrlGenerator $url): string
{
return $url->to('/my-custom-redirect-url');
}
getValidationErrorBagCustom error bag name on validation failure (default: default).
public function getValidationErrorBag(): string
{
return 'my_custom_error_bag';
}
getValidationFailureOverride validation failure behavior.
public function getValidationFailure(): void
{
throw new MyCustomValidationException();
}
getAuthorizationFailureOverride authorization failure behavior.
public function getAuthorizationFailure(): void
{
throw new MyCustomAuthorizationException();
}
asController(...) delegates to handle(...).jsonResponse, htmlResponse) when useful.handle(...).asController(...) instead of delegating.Actions::registerRoutes(...) when using in-action routes().