Skip to content

Instantly share code, notes, and snippets.

@nathanbarrett
Last active August 3, 2025 14:41
Show Gist options
  • Select an option

  • Save nathanbarrett/bfa0c7a2f7024760a9626a10f3531d58 to your computer and use it in GitHub Desktop.

Select an option

Save nathanbarrett/bfa0c7a2f7024760a9626a10f3531d58 to your computer and use it in GitHub Desktop.
Laravel & PHP Style Guide

Laravel & PHP Guidelines for AI Code Assistants

This file contains Laravel and PHP coding standards optimized for AI code assistants like Claude Code, GitHub Copilot, and Cursor. These guidelines are derived from Spatie’s comprehensive Laravel & PHP standards, combined with personalized practices.

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
  • Add declare(strict_types=1); to the top of all new PHP files

Class Structure

  • Use typed properties, not docblocks
  • Use 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: ?Type not Type|null
  • Document iterables with generics:
/** @return Collection<int, User> */
public function getUsers(): Collection

Docblock Rules

  • Don’t use docblocks for fully type-hinted methods (unless description needed)
  • Always import class names in docblocks — never use fully qualified names:
use Spatie\Url\Url;
/** @return Url */
  • Use one-line docblocks when possible: /** @var string */
  • Most common type should be first in multi-type docblocks
  • If one parameter needs docblock, add for all
  • For iterables, specify key and value types
  • Use array shape notation for fixed keys, each key on its own line:
/** @return array{
first: SomeClass,
second: SomeClass
} */

Control Flow

  • Happy path last: handle error conditions first
  • Avoid else: prefer early returns
  • Separate conditions: prefer multiple if statements
  • Always use curly brackets even for single-line statements
  • Ternary operators: put each part on its 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';

Laravel Conventions

Routes

  • URLs: kebab-case (/open-source)
  • Route names: camelCase (->name('openSource'))
  • Parameters: camelCase ({userId})
  • Use tuple notation: [Controller::class, 'method']
  • Use resource words (index, show, create, store, edit, update, destroy)
  • Always create a custom FormRequest to validate route inputs
  • Use FormRequests for authorization logic beyond basic auth, but NOT for authentication itself
  • Never define middleware inline via $this->middleware(); use route files or service providers
  • Never use closure-based middleware, always use named classes

Controllers

  • Use plural resource names (PostsController)
  • Stick to CRUD methods
  • 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, not new files
  • Use config() helper; avoid env() outside config files

Artisan Commands

  • Names: kebab-case (delete-old-records)
  • Always provide feedback ($this->comment('Done!'))
  • Show progress for loops, summary at end
  • Output BEFORE processing for easier debugging:
$items->each(function(Item $item) {
$this->info("Processing item id `{$item->id}`...");
$this->processItem($item);
});

$this->comment("Processed {$items->count()} items.");

Strings & Formatting

  • Use string interpolation over concatenation

Enums

  • Use PascalCase for enum values
  • Never use enum fields in migrations — use string/varchar and cast in model
  • Always use PHP Enums for reused values

Comments

  • Avoid comments — write expressive code
  • When needed, use proper formatting:
// Single line with space after //

/*
* Multi-line block
*/
  • Refactor comments into descriptive function names

Whitespace

  • Add blank lines between statements for readability
  • No extra empty lines between {}
  • Let code “breathe” — avoid cramped formatting

Validation

  • Use array notation for multiple rules:
public function rules(): array {
return [
'email' => ['required', 'email'],
];
}
  • Custom validation rules: snake_case
  • Always use strict validation rules in FormRequest

Blade Templates

  • Indent with 4 spaces
  • No spaces after control structures:
@if($condition)
Something
@endif

Authorization

  • Policies use camelCase: Gate::define('editPost', ...)
  • Use CRUD words; prefer view instead of show

Translations

  • Use __() function over @lang

API Routing

  • Use plural resource names: /errors
  • Use kebab-case: /error-occurrences
  • Limit deep nesting:
/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

File & 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
  • Views: camelCase
  • Jobs: action-based (CreateUser)
  • Events: tense-based (UserRegistering)
  • Listeners: action + Listener
  • Commands: action + Command
  • Mailables: purpose + Mail
  • Resources: plural + Resource
  • Enums: descriptive name, no prefix

Migrations

  • Only define up() method
  • Never define enum columns; use string and cast to enum
  • Sync with database/database.dbml

Entity Workflow & Architecture

  • Use sail artisan make:model MyModel -m -f to generate new models
  • Immediately follow with sail artisan make:repository MyModelRepository --model=MyModel
  • Use the repository for all interactions with entities, not models directly
  • Use Spatie\LaravelData\Data when passing data between layers for type safety
  • Encrypt and hide any sensitive fields in models
  • Use casts for JSON fields to Data objects
  • Add protected $guarded = ['id']; to all models

Repository/Service Architecture

  • Models:
  • Only manipulate their own data
  • Cannot use services, repositories, or other models
  • Repositories:
  • Can use models, but not services or other repositories
  • Services:
  • Can use repositories only
  • Controllers:
  • Can use both services and repositories
  • Repositories should use $this->modelQuery() not $this->query()

HTTP Clients

  • Use Laravel’s Http Client
  • For multiple requests to the same API, register a custom macro in the appropriate service provider
  • Use the Http Client’s mocking features in tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment