These rules are not enforced by PHP-CS-Fixer, so they must be followed manually.
- Target PHP 8.4+.
- Declare
declare(strict_types=1);at the top of every PHP file. - Use
camelCasefor all string-like internal variables and keys (unless public-facing). - Avoid using
finalon classes unless necessary. - Prefer Facades over Laravel helper functions (e.g.
Auth::user()overauth()->user(),Lang::get(...)over__(...),DB::transaction(...)over\DB::transaction(...)). - Prefer
CarbonImmutableoverCarbonfor date/time values in DTOs, domain logic, and module boundaries.
- Prefer
readonlyclasses for DTOs, Actions, Tasks, Services, and Presenters whenever the class has no mutable state. - Use constructor property promotion instead of declaring and assigning properties manually.
- Use typed class constants (PHP 8.3+) where they improve clarity.
- Prefer
matchexpressions overswitchstatements for value-based branching. - Use first-class callable syntax (
$obj->method(...)) when it improves clarity over closures. - Use named arguments for calls with multiple boolean/optional parameters or where readability benefits.
- Use native property types whenever possible.
- Avoid using
@varunless native typing is impossible (e.g., for inherited third-party classes).
- Omit docblocks for fully type-hinted methods unless extra description is needed.
- When used, docblock descriptions must be full sentences and provide real context.
- Do not use fully qualified class names in docblocks.
- Always write
Type|null(withnulllast), notnull|Typeor?Type. - In method signatures, prefer
Type|nullover?Type(even though PHP allows both).
-
Prefer string interpolation:
// Good $greeting = "Hi, I am $name."; // Bad $greeting = 'Hi, I am ' . $name . '.';
- Break long ternary expressions across multiple lines.
- Keep them on one line only if very short.
- Always use curly braces.
- Follow the happy-path-last pattern (fail early, return early).
- Start single-line comments with a space after
//. - Use a single
*on each line in multi-line block comments. - Prefer expressive code over explanatory comments.
- Add blank lines between logical blocks for readability.
- Don’t add extra blank lines inside
{}blocks. - Only group single-line operations if equivalent (e.g., migrations).
- File names and keys must use
snake_case. - Do not use
env()outside configuration files. Create config keys for them.
-
Use kebab-case for command names:
php artisan delete-old-records
-
Always provide terminal feedback after a command runs (e.g.,
$this->comment('Done!');).
- Public-facing URLs must use kebab-case.
- Route names must use camelCase.
- Use route methods first (e.g.,
Route::get()->name()). - Route parameters should be in camelCase.
- Only use
/at the beginning of a URL if it is the root (/).
- Use singular resource names (e.g.,
PostController). - Stick to standard CRUD methods:
index,show,store,update,destroy, etc. - Extract a separate controller if additional actions are needed.
-
Always use array syntax for rules:
'email' => ['required', 'email'],
-
Prefer custom rule classes over
Validator::extend().
- All strings (including app name, messages, etc.) must be translated.
- Use
Lang::get()(never hardcode output strings).
- Controllers: Singular resource +
ControllersuffixUserController,EventDayController - Invokable Controllers: Name by action, suffixed with
ControllerSendReportController - Resources / Transformers: Use plural form +
Resource/TransformerUsersResource,OrdersTransformer - Jobs: Verb-based names
CreateUser,ArchiveOldPosts - Events: Reflect timing
LoanApproved,ApprovingLoan - Listeners: Describe action, suffixed with
ListenerSendInvitationMailListener - Commands: Suffixed with
CommandPublishScheduledPostsCommand - Requests: Noun-first, suffixed with
RequestShipmentBatchCreationRequest,OrderUpdateRequest. Avoid verb-first names likeCreateShipmentBatchRequestorUpdateOrderRequest. - Exceptions: Suffixed with
Exception. See Error Handling for the full convention. - Repositories: See Repositories below for both interface and implementation naming.
-
Use structured error responses:
{ "message": "Invalid Vendor for Promotion", "code": 1003 } -
Use module-specific error code ranges (e.g.,
1001–1999for orders).
- Use pagination when returning multiple records.
- Never expose Eloquent models directly to frontend; use Transformers or Resources.
- Prefer timestamp columns (e.g.,
published_at) over booleans (is_published). - Avoid lazy loading, even for
hasOnerelationships.
-
Do not use
Type::cases()or dynamic enums in migrations. -
Always hard-code enum values:
$table->enum('type', ['One', 'Two', 'Three']);
- Use TitleCase for enum case names (not
UPPERCASE, notsnake_case). - Examples:
FavoritePerson,BestLake,Monthly.
- Interface:
{Subject}RepositoryExamples:UserRepository,OtpRepository,VisitorStatsRepository. - Implementation:
{Subject}Repository{Backend}— the suffix identifies the storage backend so each implementation's mechanism is obvious from the class name. Examples:UserRepositoryEloquent— relational DB via EloquentOtpRepositoryCache— cache/Redis backedVisitorStatsRepositoryClickhouse— ClickHouse backed
findByX()for single-item queries.getByX()orget()returningEnumerablefor collections.persistMany()for batch inserts.save()for single create/update operations.