Skip to content

Latest commit

 

History

History
118 lines (93 loc) · 7.87 KB

File metadata and controls

118 lines (93 loc) · 7.87 KB

PHP Fundamentals

  • Every PHP file must start with <?php followed immediately by declare(strict_types=1); on line 2. No exceptions. No blank lines between them.
  • Never use var_dump(), dd(), dump(), or print_r() in any file under app/, routes/, or config/. These are test-only functions.
  • All class files must be in app/ with subdirectories matching namespace: App\Models\User lives in app/Models/User.php.
  • Use final class by default. Only remove final if the class is explicitly designed for extension (document why in a comment).
  • Never use array type hint. Use array<string, mixed>, array<int, Model>, or specific collection types instead.

Eloquent and Database

  • All database queries must use Eloquent or Query Builder. Raw SQL queries are forbidden except in migrations and only with explicit DB::statement() calls.
  • Never use Model::all(). Use Model::query()->get() or Model::query()->paginate() with explicit column selection: ->select(['id', 'name']).
  • Every Eloquent query must call ->select() explicitly. Never rely on SELECT *.
  • Relationships must be defined as methods in Model classes using hasMany(), belongsTo(), belongsToMany() — never use with() in controller logic without the relationship being defined in the Model first.
  • Use eager loading with ->with() for all relationships accessed in loops. If you access a relationship in a loop without with(), that's an N+1 bug.
  • Scopes must be defined as methods prefixed with scope: scopeActive() becomes ->active() in queries. Never filter in controller logic.
  • Mutators and accessors must use Attribute::make() syntax (Laravel 9+). Never use getAttribute() or setAttribute() directly.
  • Timestamps are enabled by default. If a model doesn't need them, explicitly set public $timestamps = false; with a comment explaining why.

Validation and Form Requests

  • All user input validation must happen in app/Http/Requests/ Form Request classes. Never validate in controllers.
  • Form Request class name must match the action: StoreUserRequest, UpdateUserRequest, DeleteUserRequest. File: app/Http/Requests/StoreUserRequest.php.
  • Every Form Request must have authorize() returning a boolean and rules() returning an array. Both are required.
  • Use Rule::unique() for database uniqueness checks, not custom validation logic.
  • Custom validation rules must be in app/Rules/ as classes implementing ValidationRule. Never use closure-based rules in Form Requests.
  • Validated data must be accessed via $request->validated() in controllers, never $request->all() or $request->input().

API Responses and Resources

  • All API responses must use app/Http/Resources/ classes extending JsonResource or ResourceCollection.
  • Resource class name must match the model: UserResource for User model. File: app/Http/Resources/UserResource.php.
  • Never return raw Eloquent models from controllers. Always wrap in a Resource: return new UserResource($user);.
  • Collection responses must use UserResource::collection($users) or extend ResourceCollection for pagination metadata.
  • Resource toArray() method must explicitly list every field. Never use $this->resource->toArray() or $this->all().

Controllers

  • Controller class name must end with Controller: UserController, PostController. File: app/Http/Controllers/UserController.php.
  • One controller per resource. Never mix multiple resources in one controller.
  • Controller methods must be named after REST actions: index, show, create, store, edit, update, destroy. No other method names.
  • Controllers must be thin. Business logic goes in app/Services/ or Model methods, never in controller methods.
  • Constructor injection only. Never use app() or resolve() in controller methods.
  • Every controller method must have a return type hint: public function show(User $user): UserResource.

Error Handling

  • All exceptions must extend Exception or a specific exception class in app/Exceptions/. Never throw generic Exception.
  • Create custom exception classes in app/Exceptions/: ModelNotFoundException, ValidationFailedException, UnauthorizedException.
  • Every custom exception must have a render() method returning a JSON response with status, message, and data keys.
  • Try-catch blocks must catch specific exception types, never catch (Exception $e) or catch (Throwable $e).
  • Exceptions must be caught at the boundary (controller or command) and converted to responses. Never let them bubble up uncaught.
  • Use abort(404), abort(403), abort(422) for HTTP errors. Never throw custom exceptions for HTTP status codes.

Configuration and Secrets

  • Environment variables must be read in config/ files only, never in app/ code. Use config('app.key') instead of env('APP_KEY').
  • Never hardcode API keys, database credentials, or secrets. All must come from .env file.
  • .env file must never be committed. Add to .gitignore immediately.
  • .env.example must exist with all required keys and dummy values. Update it when adding new env vars.
  • Configuration values must be type-cast in config/ files: 'debug' => (bool) env('APP_DEBUG', false).

Testing

  • Test files must be in tests/Feature/ for integration tests or tests/Unit/ for unit tests.
  • Test class name must match the class being tested with Test suffix: UserControllerTest, UserServiceTest. File: tests/Feature/UserControllerTest.php.
  • Every public method must have at least one test. No exceptions.
  • Test method names must describe the scenario: testStoreUserWithValidDataReturnsCreatedResponse(), never testStore().
  • Use $this->actingAs($user) for authenticated requests, never manually set headers.
  • Mock external services (APIs, mail, queues) using Mail::fake(), Http::fake(), Queue::fake().
  • Never use real database in tests. Use RefreshDatabase trait or DatabaseTransactions trait.

File Organization

  • Models: app/Models/
  • Controllers: app/Http/Controllers/
  • Requests: app/Http/Requests/
  • Resources: app/Http/Resources/
  • Services: app/Services/
  • Rules: app/Rules/
  • Exceptions: app/Exceptions/
  • Migrations: database/migrations/
  • Seeders: database/seeders/
  • Tests: tests/Feature/ and tests/Unit/

Naming Conventions

  • Model names: singular, PascalCase: User, BlogPost, Comment
  • Table names: plural, snake_case: users, blog_posts, comments
  • Variable names: camelCase: $userId, $isActive, $userData
  • Method names: camelCase: getUserById(), isUserActive()
  • Constant names: UPPER_SNAKE_CASE: MAX_RETRIES, DEFAULT_TIMEOUT
  • Boolean methods/properties: prefix with is, has, can: isActive(), hasPermission(), canDelete()

Type Hints and Return Types

  • Every method must have parameter type hints and return type hints. No exceptions.
  • Use nullable types explicitly: ?string, ?User, never omit the ?.
  • Use union types for multiple possibilities: string|int, User|null (not ?User if there are other types).
  • Use void return type for methods that don't return anything.
  • Use never return type for methods that always throw or exit.
  • Generic types must be specified: Collection<User>, array<string, int>, never bare Collection or array.

Forbidden Patterns

  • Never use global keyword.
  • Never use eval() or call_user_func() with string arguments.
  • Never use extract() or compact() in production code.
  • Never use @ error suppression operator.
  • Never use goto.
  • Never use static properties for state. Use dependency injection instead.
  • Never use string-based route names without route() helper: always route('users.show', $user), never hardcoded URLs.

Source: Codelibrium — the marketplace for AI behaviour files. Browse multiple rulesets at codelibrium.com or install via CLI: npx codelibrium-cli install <ruleset-name>