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:
?stringnotstring|null - •Always specify
voidreturn 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
- •Use typed properties over docblocks
- •Specify return types including
void - •Use short nullable syntax:
?TypenotType|null - •Document iterables with generics:
php
/** @return Collection<int, User> */ public function getUsers(): Collection
Docblock Rules
- •Don't use docblocks for fully type-hinted methods (unless description needed)
- •Always import classnames in docblocks - never use fully qualified names:
php
use \Spatie\Url\Url; /** @return Url */
- •Use one-line docblocks when possible:
/** @var string */ - •Most common type should be first in multi-type docblocks:
php
/** @var Collection|SomeWeirdVendor\Collection */
- •If one parameter needs docblock, add docblocks for all parameters
- •For iterables, always specify key and value types:
php
/** * @param array<int, MyObject> $myArray * @param int $typedArgument */ function someFunction(array $myArray, int $typedArgument) {} - •Use array shape notation for fixed keys, put each key on it's own line:
php
/** @return array{ first: SomeClass, second: SomeClass } */
Control Flow
- •Happy path last: Handle error conditions first, success case last
- •Avoid else: Use early returns instead of nested conditions
- •Separate conditions: Prefer multiple if statements over compound conditions
- •Always use curly brackets even for single statements
- •Ternary operators: Each part on own line unless very short
php
// Happy path last
if (! $user) {
return null;
}
if (! $user->isActive()) {
return null;
}
// Process active user...
// Short ternary
$name = $isFoo ? 'foo' : 'bar';
// Multi-line ternary
$result = $object instanceof Model ?
$object->name :
'A default value';
// Ternary instead of else
$condition
? $this->doSomething()
: $this->doSomethingElse();
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, avoidenv()outside config files
Artisan Commands
- •Names: kebab-case (
delete-old-records) - •Always provide feedback (
$this->comment('All ok!')) - •Show progress for loops, summary at end
- •Put output BEFORE processing item (easier debugging):
php
$items->each(function(Item $item) { $this->info("Processing item id `{$item->id}`..."); $this->processItem($item); }); $this->comment("Processed {$items->count()} items.");
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:
php
// Get the failed checks for this site
$checks = $site->checks()->where('status', 'failed')->get();
Do this:
php
$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):
php
public function rules() { return [ 'email' => ['required', 'email'], ]; } - •Custom validation rules use snake_case:
php
Validator::extend('organisation_type', function ($attribute, $value) { return OrganisationType::isValid($value); });
Blade Templates
- •Indent with 4 spaces
- •No spaces after control structures:
blade
@if($condition) Something @endif
Authorization
- •Policies use camelCase:
Gate::define('editPost', ...) - •Use CRUD words, but
viewinstead ofshow
Translations
- •Use
__()function over@lang:
API Routing
- •Use plural resource names:
/errors - •Use kebab-case:
/error-occurrences - •Limit deep nesting for simplicity:
code
/error-occurrences/1 /errors/1/occurrences
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 +
Listenersuffix (SendInvitationMailListener) - •Commands: action +
Commandsuffix (PublishScheduledPostsCommand) - •Mailables: purpose +
Mailsuffix (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
elsestatements when possible - •Use string interpolation over concatenation
- •Always use curly braces for control structures