CRUD Controller
Laraventus includes a ready-to-use generic controller called ModelController that allows you to quickly create full CRUD endpoints for any Eloquent model with minimal code.
This controller automatically handles listing, creation, updating, and deletion of records, all fully compatible with the Aventus $type response system and AventusJs integration.
How It Works
Section titled “How It Works”ModelController is an abstract class that defines all the common CRUD operations you need for a model:
| Method | HTTP Verb | Description |
|---|---|---|
index() | GET | Lists all resources |
show($id) | GET | Returns a single resource |
store() | POST | Creates a new resource |
storeMany() | POST | Creates multiple resources |
update($id) | PUT/PATCH | Updates a resource |
updateMany() | PUT/PATCH | Updates multiple resources |
destroy($id) | DELETE | Deletes a resource |
destroyMany() | DELETE | Deletes multiple resources by IDs |
Each method automatically returns typed resources, using the $type metadata expected by AventusJs.
Implementation Example
Section titled “Implementation Example”<?php
namespace App\Http\Controllers;
use App\Http\Requests\UserRequest;use App\Http\Resources\UserResource;use App\Models\User;use Aventus\Laraventus\Controllers\ModelController;
/** * @extends ModelController<User, UserRequest, UserResource> */class UserController extends ModelController{ public function defineModel(): string { return User::class; }
public function defineRequest(): string { return UserRequest::class; }
public function defineResource(): string { return UserResource::class; }}This single controller gives you a complete CRUD API, including batch operations.
Defining Routes
Section titled “Defining Routes”Instead of manually registering each route, Laraventus provides a helper method that generates all CRUD endpoints at once, including the Many operations.
To use it, import the Laraventus Route class then, register your routes:
<?php
use App\Http\Controllers\UserController;use Aventus\Laraventus\Routes\Route;
Route::resourceWithMany('user', UserController::class);This automatically creates the following endpoints:
| HTTP Verb | URI | Controller Method |
|---|---|---|
GET | /user | index |
GET | /user/{id} | show |
POST | /user | store |
POST | /user/many | storeMany |
PUT | /user/{id} | update |
PUT | /user/many | updateMany |
DELETE | /user/{id} | destroy |
DELETE | /user/many | destroyMany |
Each route returns a Laraventus-formatted JSON response containing $type, errors, and result.
Example Response
Section titled “Example Response”GET /user
Section titled “GET /user”{ "result": [ { "$type": "App.Http.Resources.UserResource", "name": "Alice", "email": "alice@example.com" }, { "$type": "App.Http.Resources.UserResource", "name": "Bob", "email": "bob@example.com" } ], "errors": [], "$type": "Aventus.Laraventus.Helpers.LaravelResult"}POST /user
Section titled “POST /user”{ "result": { "$type": "App.Http.Resources.UserResource", "name": "Charlie", "email": "charlie@example.com" }, "errors": [], "$type": "Aventus.Laraventus.Helpers.LaravelResult"}AventusJs RAM
Section titled “AventusJs RAM”One of the biggest advantages of using Laraventus together with AventusJs is the ability to connect your backend controllers directly to the frontend, without manually writing HTTP requests or managing REST endpoints.
When you use ModelController on the backend, AventusJs can automatically generate the corresponding .lib.avt classes, giving you type-safe and reactive access to your Laravel data, right from the frontend.
import { UserController } from "../generated/app/Http/Controllers/UserController.lib.avt";import { UserResource } from "../generated/app/Http/Resources/UserResource.lib.avt";
export class UserRAM extends AventusPhp.RamHttp<UserResource> implements Aventus.IRam { /** * @inheritdoc */ public override defineRoutes(): AventusPhp.RamQuery<UserResource> { return new UserController(); }}With this code, your Ram is now connected to your backend
Permission Control
Section titled “Permission Control”Laraventus allows you to add custom permission checks or access control directly at the route level, thanks to its powerful Attribute-based middleware system.
Once the AventusAttributesMiddleware has been added to the web middleware group, any controller method can declare an Attribute class that extends Aventus\Laraventus\Attributes\Middleware.
Example
Section titled “Example”You can easily restrict access to specific endpoints by applying a custom Attribute. For example, Deny:
<?php
namespace App\Http\Controllers;
use App\Http\Middlewares\Deny;use App\Http\Requests\UserRequest;use App\Http\Resources\UserResource;use App\Models\User;use Aventus\Laraventus\Controllers\ModelController;use Aventus\Laraventus\Attributes\NoExport;
/** * @extends ModelController<User, UserRequest, UserResource> */class UserController extends ModelController{ /** * @return class-string<User> */ public function defineModel(): string { return User::class; }
/** * @return class-string<UserRequest> */ public function defineRequest(): string { return UserRequest::class; }
/** * @return class-string<UserResource> */ public function defineResource(): string { return UserResource::class; }
#[Deny] // use a custom attribute to implement your permission logic #[NoExport] // use no export because already define inside the parent class public function index(): array { return parent::index(); }}The #[Deny] attribute is automatically detected by the AventusAttributesMiddleware.
When the route is called, the system executes the handle() method defined in your custom middleware before running the controller method.
Your middleware can prevent the controller logic to be executed.
Using a Detailed Resource
Section titled “Using a Detailed Resource”In addition to the standard Model, Request, and Resource types, ModelController supports an optional fourth type : the Detailed Resource.
This allows you to provide a richer representation of your model when fetching a single record, without overloading the data returned by list endpoints.
When listing entities (via index()), you usually want a lightweight representation for performance reasons. For example, listing users with only their id, name, and email.
However, when fetching a single resource (via show($id)), you may want to include additional related data, computed attributes, or more complete metadata.
The Detailed Resource type allows you to separate these two representations cleanly, while keeping the same controller logic.
<?php
namespace App\Http\Controllers;
use App\Http\Requests\UserRequest;use App\Http\Resources\UserResource;use App\Http\Resources\UserResourceDetails;use App\Models\User;use Aventus\Laraventus\Controllers\ModelController;
/** * @extends ModelController<User, UserRequest, UserResource, UserResourceDetails> */class UserController extends ModelController{ public function defineModel(): string { return User::class; }
public function defineRequest(): string { return UserRequest::class; }
public function defineResource(): string { return UserResource::class; }
public function defineResourceDetails(): string { return UserResourceDetails::class; }}When defined, ModelController automatically uses the detailed resource for:
showupdatestore
Meanwhile, the index continue to use the standard Resource class.
AventusJs RAM Integration
Section titled “AventusJs RAM Integration”When using AventusJs, this fourth type provides seamless frontend integration.
If your controller defines a detailed resource, the corresponding RamHttp class automatically knows that:
- Items retrieved via
getAlluse the standard Resource - When a single item is accessed or expanded, the RAM automatically upgrades it to the Detailed Resource, fetching additional information from the backend if it’s not already available
This behavior allows your frontend to stay reactive and efficient: users first see a lightweight list, and additional data loads transparently when an item is viewed in detail.
Overriding Controller Logic
Section titled “Overriding Controller Logic”While ModelController provides ready-to-use CRUD behavior, it is also designed to be fully customizable.
Every CRUD operation is split into two layers:
- A public method (e.g.
store,update,destroy) that handles validation, transactions, and resource conversion. - A protected method (ending with
Action, e.g.storeAction,updateAction,destroyAction) that contains the actual business logic.
By overriding these *Action methods in your custom controller, you can inject your own logic without losing any of the built-in Laraventus behavior such as resource formatting, automatic transactions, or AventusJs integration.
Available Override Points
Section titled “Available Override Points”| Method | Called By | Purpose |
|---|---|---|
| indexAction() | index() | Define how items are listed |
| showAction($id) | show() | Define how a single item is loaded |
| showManyAction(array $ids) | showMany() | Define how multiple items are loaded |
| storeAction($item) | store() / storeMany() | Define how new items are persisted |
| updateAction($item) | update() / updateMany() | Define how existing items are updated |
| destroyAction($id) | destroy() | Define how a single item is deleted |
| destroyManyAction(array $ids) | destroyMany() | Define how multiple items are deleted |
Each of these methods is protected, so they will not be exported to the AventusJs frontend.
That means you don’t need to add the #[NoExport] attribute, your backend logic remains private by default.
Example: Custom Creation Logic
Section titled “Example: Custom Creation Logic”<?php
namespace App\Http\Controllers;
use App\Http\Requests\UserRequest;use App\Http\Resources\UserResource;use App\Models\User;use Aventus\Laraventus\Controllers\ModelController;
/** * @extends ModelController<User, UserRequest, UserResource> */class UserController extends ModelController{ public function defineModel(): string { return User::class; }
public function defineRequest(): string { return UserRequest::class; }
public function defineResource(): string { return UserResource::class; }
/** * Customize how a new user is stored * * @param User $item */ protected function storeAction($item): void { // Example: hash password before saving $item->password = bcrypt($item->password);
// Example: set default role $item->role = 'user';
$item->save(); }}This override replaces the default storeAction logic with your own, while still benefiting from:
- Automatic request validation (
UserRequest) - Safe database transaction handling
- Standardized response wrapping (
UserResource)
Safe by Design
Section titled “Safe by Design”All public CRUD methods in ModelController (store, update, destroy, etc.) are wrapped in database transactions.
This means that if an exception occurs inside your overridden *Action method, the entire operation is automatically rolled back, ensuring your database stays consistent.
protected function updateAction($item): void{ // Your custom logic $item->save();
// If something goes wrong here... throw new \Exception('Unexpected error');
// The transaction will be rolled back automatically}This makes ModelController both powerful and flexible, letting you adapt it to your project’s specific needs without rewriting its core logic.
Using templates
Section titled “Using templates”To save time and maintain consistency when creating new Laraventus CRUD controller, you can use prebuilt templates available through the Aventus VS Code extension.
Installing a Template
Section titled “Installing a Template”- Open the Aventus Shop
- Search for
"Laraventus.CRUD" - Click
Downloadto add it to your template list.
This template automatically generates all the necessary files for a complete Laraventus CRUD controller including the Controller, Request, Resource, ResourceDetails - following the same structure as the examples above.
Adding the Template to Quick Actions
Section titled “Adding the Template to Quick Actions”Once the template is installed, you can assign it to a Quick Action for faster access:
- Press
Ctrl + K,Ctrl + Shift + Vto open the Aventus Quick Action Manager. - Check the box next to
"Laraventus.CRUD". - Validate your choice. The template is now available as a quick command.
Using the Template as Quick Actions
Section titled “Using the Template as Quick Actions”When working in your Laravel project, you can now quickly generate a new HTTP function using the shortcut: Ctrl + K, Ctrl + V
This will:
- Ask for the name of the CRUD (for example, User).
- Prompt you to select if you need a Resource with details.
- Automatically create all selected files under app/Http/Controllers/… using the proper Laraventus structure.
Within seconds, you’ll have a fully functional controller setup ready to use, following the Laraventus conventions and compatible with AventusJs.