crud
CRUD
A CRUD feature is a stack, not a single file. The route declares one router.resource(...) scoped by auth middleware; the controller has 5–7 tiny methods that only orchestrate; each mutation has a VineJS validator; a BasePolicy gates every method; each write goes through a single-purpose action class; a transformer produces per-page shape variants; the read side lives in queries/; the UI is Inertia pages under <mod>/ui/pages/. The point is that no single layer holds more than its own responsibility, and adding the next resource is copy-shape, not think-shape.
Rules
Every HTTP method — including index — follows the same 3-step spine:
- Authorize via
bouncer.with(XPolicy).authorize(...). Awhere('owner_id', user.id)clause in the query is not a substitute — the policy is the source of truth. - Validate via
request.validateUsing(...). - Render / redirect — either
inertia.render('...', props)for reads orresponse.redirect().back()/.toRoute(...)for writes. Writes call an action in between.
Authentication is the middleware's job — controllers never call authenticate(); use auth.getUserOrFail() when the handler needs the user. Anything past .authorize() that also does an owner check is redundant; anything before it that touches the DB other than loading the resource being authorized is a leak.