| name | logics-index |
| description | Guide for creating IndexLogics in the project |
| globs | app/Logics/**/*IndexLogic.php |
AI Agent Skills Guide: Logics (IndexLogic)
This guide details how to create and use Logics within this project's ecosystem, starting with IndexLogic. These components encapsulate business logic and are used in controllers via dependency injection.
1. Usage from the Controller
Logics are injected directly into controller methods along with Data Objects. Usually, IndexData is used for these logics.
class UserController extends Controller
{
public function index(IndexData $data, UserIndexLogic $logic): JsonResponse
{
return $logic->run($data);
}
}
2. Logic Lifecycle
The main execution flow of a Logic follows this order:
before(): Pre-validations.
action(): Business logic execution (queries, filtering, pagination).
after(): Additional logic after the main action.
response(): Formatting and returning the response.
3. Validations in before()
Used for business rules or permissions. If it returns false, execution stops and an error is returned. Use the error() method inherited from the CoreLogic trait.
protected function before(): bool
{
if (! auth()->user()->can('view_users')) {
return $this->error(
message: 'You do not have permission to view users.',
status: Http::Forbidden
);
}
return true;
}
The error generated by $this->error() has this response format:
{
"status": "error",
"message": "Error message",
"data": []
}
4. Custom Query with makeQuery()
By default, IndexLogic uses the model's index() method or newQuery(). You can override it to customize the base query.
public function makeQuery(): Builder
{
return User::query()->where('is_active', true);
}
5. Column Definition with tableHeaders()
This method defines which columns and titles will be sent to the frontend to build dynamic tables.
protected function tableHeaders(): array
{
return [
'id' => 'ID',
'name' => 'Full Name',
'email' => 'Email Address',
'created_at' => 'Registration Date',
];
}
6. Response Transformation with withResource()
Allows transforming the results collection before sending it to the final response (ideal for using Laravel Resources or data transformation).
protected function withResource(): mixed
{
return UserResource::collection($this->response);
}
7. Custom Filters with customFilters()
Allows defining specific filtering logic that is not a simple "equals" or "like". It receives a callback with the filter instance.
protected function customFilters(): array
{
return [
'role' => function (Filter $filter) {
$this->queryBuilder->whereHas('roles', function ($query) use ($filter) {
$query->where('name', $filter->value);
});
},
'status' => function (Filter $filter) {
$this->queryBuilder->where('status_id', $filter->value);
}
];
}
protected function getColumnSearch(): string
{
return 'email';
}
8. Post-Action Logic with after()
Executed after the query and pagination have been processed, allowing data modification or logging.
protected function after(): bool
{
Log::info("User " . auth()->id() . " viewed the user list.");
return true;
}
Full Implementation Example
The IndexData object is generally used as the input for these logics to handle pagination, sorting, and searching.
class UserIndexLogic extends IndexLogic
{
public function __construct(User $model)
{
parent::__construct($model);
}
public function run(IndexData $input): JsonResponse
{
return $this->logic($input);
}
protected function before(): bool
{
return auth()->user()->isAdmin() ?: $this->error('Access denied');
}
public function makeQuery(): Builder
{
return $this->model->newQuery()->with('roles');
}
protected function tableHeaders(): array
{
return [
'id' => '#',
'name' => 'Name',
'email' => 'Email'
];
}
protected function withResource(): mixed
{
return UserResource::collection($this->response);
}
}