php-service
When to use
Use when creating a new service, extracting business logic from a controller, or refactoring into a service layer.
Do NOT use when:
- Controllers (use
laravelskill) - DTOs (use
laravel-dtoskill for Laravel/PHP; framework-native skill for other stacks) - Models (use
eloquentskill)
When to create a service
✅ Multiple steps needing orchestration (save + calculate + notify) ✅ Business rules beyond FormRequest ✅ Logic reused across controllers, jobs, commands ✅ Complex calculations or transformations
❌ Simple CRUD — $model->update($request->validated()) stays in controller
❌ One-liner logic — no class for a single Eloquent call
Procedure: Create a service
Step 0: Inspect
- Read
./agents/andAGENTS.mdfor service conventions. - Check existing services — match naming, structure, DI patterns.
- Check for repositories — see
php/patterns/repositories.mdguideline.
Step 1: Create the class
- Location:
app/Services/{Domain}/orapp/Modules/{Module}/App/Services/. declare(strict_types=1), proper namespace.- Constructor inject dependencies (repositories, other services).
- Max 4 constructor dependencies — if more, split the service.
Step 2: Implement methods
- One responsibility per method.
- Delegate data access to repositories.
- Use DTOs for structured data.
- Use
Mathhelper for calculations — never raw arithmetic.
Step 3: Wire up
// Controller
public function __invoke(
UpdateProjectRequest $request,
Project $project,
ProjectService $projectService,
): ProjectResource {
$dto = UpdateProjectDTO::fromRequest($request);
return ProjectResource::make($projectService->update($project, $dto));
}
Conventions
→ See guideline php/patterns/service-layer.md for full service layer conventions.
Validate
- Run PHPStan on the service — must pass at level 9.
- Verify single responsibility: service does one thing, no mixed concerns.
- Confirm all dependencies are constructor-injected (no
app()or facades in service). - Run affected tests — must pass.
Output format
- Service class with constructor injection and single responsibility
- Repository dependency if data access is needed
Gotcha
- Don't create "god services" with 10+ methods — split by responsibility.
- Don't inject
Requestinto services — pass specific data. - Services are framework-agnostic — no HTTP/request logic.
Do NOT
- Do NOT inject
RequestorControllerinto services — services are framework-agnostic. - Do NOT create services with more than one responsibility — split them.
Auto-trigger keywords
- service class
- business logic
- service layer
- dependency injection