supabase
Handles the full Supabase workflow from schema changes to deployment, with built-in security guardrails that catch common traps like RLS...
laravel-controllers
Thin HTTP layer controllers. Controllers contain zero domain logic, only HTTP concerns. Use when working with controllers, HTTP layer, web vs API patterns, or when user mentions controllers, routes, HTTP responses.
Full skill instructions
Controllers are extremely thin. They handle HTTP concerns only and contain zero domain logic.
Related guides:
Controllers should ONLY:
Controllers should NEVER:
Controllers should be named using the PLURAL form of the main resource:
// ✅ CORRECT - Plural resource names
CalendarsController // manages calendar resources
EventsController // manages event resources
OrdersController // manages order resources
UsersController // manages user resources
// ❌ INCORRECT - Singular form
CalendarController
EventController
For nested resources, combine both resource names (parent + child):
// Route: /calendars/{calendar}/events
CalendarEventsController // manages events within a calendar
// Route: /orders/{order}/items
OrderItemsController // manages items within an order
Pattern: {ParentSingular}{ChildPlural}Controller
Routes:
// Standard resource routes
Route::resource('calendars', CalendarsController::class);
// Nested resource routes
Route::resource('calendars.events', CalendarEventsController::class);
Controllers must only use Laravel's standard RESTful method names.
For web applications (with forms):
index - Display a listing of the resourcecreate - Show the form for creating a new resourcestore - Store a newly created resourceshow - Display the specified resourceedit - Show the form for editing the resourceupdate - Update the specified resourcedestroy - Remove the specified resourceFor APIs (no form views):
index, show, store, update, destroycreate or edit methods (those are for HTML forms)Never use custom method names in resource controllers:
// ❌ INCORRECT
class OrdersController extends Controller
{
public function all() { } // Use index
public function get() { } // Use show
public function add() { } // Use store
public function remove() { } // Use destroy
public function cancel() { } // Extract to CancelOrderController
}
If you need an endpoint that doesn't fit standard RESTful methods, extract it to its own invokable controller:
// app/Http/Api/V1/Controllers/CancelOrderController.php
class CancelOrderController extends Controller
{
public function __invoke(
Order $order,
CancelOrderAction $action
): OrderResource {
$order = $action($order);
return OrderResource::make($order);
}
}
Routes:
Route::apiResource('orders', OrdersController::class);
Route::post('/orders/{order:uuid}/cancel', CancelOrderController::class);
Why invokable controllers for non-RESTful actions?
Purpose: Serve your application's web layer (API for separate frontend, Blade views, or Inertia)
Location: app/Http/Web/Controllers/
Routes: routes/web.php
Characteristics:
Purpose: For external/third-party consumption
Location: app/Http/Api/V1/Controllers/
Routes: routes/api/v1.php
Characteristics:
/api/v1, /api/v2)Key difference: Namespace (Http\Web vs Http\Api\V1). Controller structure is identical.
<?php
declare(strict_types=1);
namespace App\Http\Web\Controllers;
use App\Actions\Order\CreateOrderAction;
use App\Actions\Order\DeleteOrderAction;
use App\Actions\Order\UpdateOrderAction;
use App\Data\Transformers\Web\OrderDataTransformer;
use App\Http\Controllers\Controller;
use App\Http\Web\Queries\OrderIndexQuery;
use App\Http\Web\Requests\CreateOrderRequest;
use App\Http\Web\Requests\UpdateOrderRequest;
use App\Http\Web\Resources\OrderResource;
use App\Models\Order;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Response;
class OrdersController extends Controller
{
public function index(OrderIndexQuery $query): AnonymousResourceCollection
{
return OrderResource::collection($query->jsonPaginate());
}
public function show(Order $order): OrderResource
{
return OrderResource::make($order->load('items', 'customer'));
}
public function store(
CreateOrderRequest $request,
CreateOrderAction $action
): OrderResource {
$order = $action(
user(),
OrderDataTransformer::fromRequest($request)
);
return OrderResource::make($order);
}
public function update(
UpdateOrderRequest $request,
Order $order,
UpdateOrderAction $action
): OrderResource {
$order = $action(
$order,
OrderDataTransformer::fromRequest($request)
);
return OrderResource::make($order);
}
public function destroy(
Order $order,
DeleteOrderAction $action
): Response {
$action($order);
return response()->noContent();
}
}
For API controllers: Same structure, different namespace (App\Http\Api\V1\Controllers).
For API filtering, sorting, and includes, use Query Objects with Spatie Query Builder:
public function index(OrderIndexQuery $query): AnonymousResourceCollection
{
return OrderResource::collection($query->jsonPaginate());
}
→ Complete query objects guide: query-objects.md
public function store(
CreateOrderRequest $request,
CreateOrderAction $action
): OrderResource {
$this->authorize('create', Order::class);
$order = $action(user(), OrderDataTransformer::fromRequest($request));
return OrderResource::make($order);
}
Or use route middleware:
Route::post('/orders', [OrdersController::class, 'store'])
->can('create', Order::class);
public function show(Order $order): OrderResource
{
return OrderResource::make($order);
}
public function index(OrderIndexQuery $query): AnonymousResourceCollection
{
return OrderResource::collection($query->jsonPaginate());
}
return OrderResource::make($order)->response()->setStatusCode(201);
return response()->noContent();
return redirect()->route('orders.show', $order);
Use route model binding for cleaner controllers:
Route::get('/orders/{order}', [OrdersController::class, 'show']);
Route::get('/orders/{order:uuid}', [OrdersController::class, 'show']); // Custom key
Controller automatically receives model:
public function show(Order $order): OrderResource
{
return OrderResource::make($order->load('items', 'customer'));
}
Feature tests for controllers:
use function Pest\Laravel\actingAs;
use function Pest\Laravel\postJson;
it('creates an order', function () {
$user = User::factory()->create();
$data = CreateOrderData::testFactory()->make();
actingAs($user)
->postJson('/orders', $data->toArray())
->assertCreated()
->assertJsonStructure(['data' => ['id', 'status']]);
});
it('requires authentication', function () {
postJson('/orders', [])->assertUnauthorized();
});
it('validates required fields', function () {
actingAs(User::factory()->create())
->postJson('/orders', [])
->assertUnprocessable()
->assertJsonValidationErrors(['customer_email', 'items']);
});
// BAD
public function store(Request $request)
{
$order = Order::create($request->validated());
$order->items()->createMany($request->items);
$total = $order->items->sum('total');
$order->update(['total' => $total]);
}
// GOOD
public function store(
CreateOrderRequest $request,
CreateOrderAction $action
): OrderResource {
$order = $action(
user(),
OrderDataTransformer::fromRequest($request)
);
return OrderResource::make($order);
}
// BAD
public function index()
{
$orders = Order::with('items')
->where('status', 'pending')
->latest()
->paginate();
}
// GOOD
public function index(OrderIndexQuery $query): AnonymousResourceCollection
{
return OrderResource::collection($query->jsonPaginate());
}
Controllers are HTTP adapters:
Every line of domain logic belongs in an Action, not a Controller.
Handles the full Supabase workflow from schema changes to deployment, with built-in security guardrails that catch common traps like RLS...
Comprehensive guides and best practices for Neon Serverless Postgres, covering setup, connection methods, authentication, and platform APIs.
Guide for setting up and using Firebase Authentication. Use this skill when the user's app requires user sign-in, user management, or secure data access using auth rules.
A skill to evaluate how secure Firestore security rules are. Use this when Firestore security rules are updated to ensure that the generated rules are extremely secure and robust.
Official skill for integrating Firebase AI Logic (Gemini API) into web applications. Covers setup, multimodal inference, structured output, and security.
Complete Better Auth server and client setup with database adapters, session management, plugins, and security configuration.
Deploy and manage projects on Vercel using token-based authentication. Use when working with Vercel CLI using access tokens rather than interactive login — e.g. "deploy to vercel", "set up vercel", "add environment variables to vercel".
Complete Cloudflare platform integration with decision trees for compute, storage, AI, networking, security, and infrastructure-as-code.
Run Azure compliance and security audits with azqr plus Key Vault expiration checks. Covers best-practice assessment, resource review, policy/compliance validation, and security posture checks. WHEN: compliance scan, security audit, BEFORE running azqr (compliance cli tool), Azure best practices, Key Vault expiration check, expired certificates, expiring secrets, orphaned resources, compliance assessment.
Send, read, and manage Gmail messages, drafts, labels, and account settings.
Comprehensive website auditing across 230+ rules in 21 categories including SEO, performance, security, and accessibility.
Shared authentication, CLI syntax, and output formatting patterns for gws Google Workspace commands.
Discover your celebrity doppelgänger with StarByFace!
GeoSpy: Pricing, Features, FAQs, and Alternatives for AI Teams
Detect AI-generated voices to protect against audio fraud.
Ensure Your Content's Originality with AI Plagiarism Checker
Upscale images by 400% without quality loss
Protect your social media from copyright disputes
Accurately Detect AI-Generated Content with TheChecker.AI
Chrome extension that detects and flags AI-generated content