| name | php-guidelines-from-spatie |
| description | Describes PHP and Laravel guidelines provided by Spatie. These rules result in more maintainable, and readable code. |
| license | MIT |
| metadata | {"author":"Spatie","tags":"php, laravel, best practices, coding standards"} |
Core Laravel Principle
Follow Laravel conventions first. If Laravel has a documented way to do something, use it. Only deviate when you have a clear justification.
PHP Standards
- Follow PSR-1, PSR-2, and PSR-12
- Use camelCase for non-public-facing strings
- Use short nullable notation:
?string not string|null
- Always specify
void return types when methods return nothing
Class Structure
- Use typed properties, not docblocks:
- Constructor property promotion when all properties can be promoted:
- One trait per line:
Type Declarations & Docblocks
Docblock Rules
Control Flow
- Happy path last: Handle error conditions first, success case last
- Avoid else: Use early returns instead of nested conditions
- Separate conditions: Split compound
if statements that use && into nested if statements for better readability
- Always use curly brackets even for single statements
- Ternary operators: Each part on own line unless very short
if (! $user) {
return null;
}
if (! $user->isActive()) {
return null;
}
$name = $isFoo ? 'foo' : 'bar';
$result = $object instanceof Model ?
$object->name :
'A default value';
$condition
? $this->doSomething()
: $this->doSomethingElse();
if ($user->isActive() && $user->hasPermission('edit')) {
$user->edit();
}
if ($user->isActive()) {
if ($user->hasPermission('edit')) {
$user->edit();
}
}
Laravel Conventions
Routes
- URLs: kebab-case (
/open-source)
- Route names: camelCase (
->name('openSource'))
- Parameters: camelCase (
{userId})
- Use tuple notation:
[Controller::class, 'method']
Controllers
- Plural resource names (
PostsController)
- Stick to CRUD methods (
index, create, store, show, edit, update, destroy)
- Extract new controllers for non-CRUD actions
Configuration
- Files: kebab-case (
pdf-generator.php)
- Keys: snake_case (
chrome_path)
- Add service configs to
config/services.php, don't create new files
- Use
config() helper, avoid env() outside config files
Artisan Commands
Strings & Formatting
- String interpolation over concatenation:
Enums
- Use PascalCase for enum values:
Comments
Be very critical about adding comments as they often become outdated and can mislead over time. Code should be self-documenting through descriptive variable and function names.
Adding comments should never be the first tactic to make code readable.
Instead of this:
$checks = $site->checks()->where('status', 'failed')->get();
Do this:
$failedChecks = $site->checks()->where('status', 'failed')->get();
Guidelines:
- Don't add comments that describe what the code does - make the code describe itself
- Short, readable code doesn't need comments explaining it
- Use descriptive variable names instead of generic names + comments
- Only add comments when explaining why something non-obvious is done, not what is being done
- Never add comments to tests - test names should be descriptive enough
Whitespace
- Add blank lines between statements for readability
- Exception: sequences of equivalent single-line operations
- No extra empty lines between
{} brackets
- Let code "breathe" - avoid cramped formatting
Validation
- Use array notation for multiple rules (easier for custom rule classes):
public function rules() {
return [
'email' => ['required', 'email'],
];
}
- Custom validation rules use snake_case:
Validator::extend('organisation_type', function ($attribute, $value) {
return OrganisationType::isValid($value);
});
Blade Templates
Authorization
- Policies use camelCase:
Gate::define('editPost', ...)
- Use CRUD words, but
view instead of show
Translations
- Use
__() function over @lang:
API Routing
Testing
- Keep test classes in same file when possible
- Use descriptive test method names
- Follow the arrange-act-assert pattern
Quick Reference
Naming Conventions
- Classes: PascalCase (
UserController, OrderStatus)
- Methods/Variables: camelCase (
getUserName, $firstName)
- Routes: kebab-case (
/open-source, /user-profile)
- Config files: kebab-case (
pdf-generator.php)
- Config keys: snake_case (
chrome_path)
- Artisan commands: kebab-case (
php artisan delete-old-records)
File Structure
- Controllers: plural resource name +
Controller (PostsController)
- Views: camelCase (
openSource.blade.php)
- Jobs: action-based (
CreateUser, SendEmailNotification)
- Events: tense-based (
UserRegistering, UserRegistered)
- Listeners: action +
Listener suffix (SendInvitationMailListener)
- Commands: action +
Command suffix (PublishScheduledPostsCommand)
- Mailables: purpose +
Mail suffix (AccountActivatedMail)
- Resources/Transformers: plural +
Resource/Transformer (UsersResource)
- Enums: descriptive name, no prefix (
OrderStatus, BookingType)
Migrations
- do not write down methods in migrations, only up methods
Code Quality Reminders
PHP
- Use typed properties over docblocks
- Prefer early returns over nested if/else
- Use constructor property promotion when all properties can be promoted
- Avoid
else statements when possible
- Split compound
if conditions using && into nested if statements
- Use string interpolation over concatenation
- Always use curly braces for control structures
- Always import namespaces with
use statements — never use inline fully qualified class names (e.g. \Exception, \Illuminate\Support\Facades\Http)
- Never use single-letter variable names — use descriptive names (e.g.
$exception not $e, $request not $r)