PHP Style Guide
PHP Style Guide
Section titled “PHP Style Guide”Use PSR coding conventions (e.g. methods are in $camelCase, classes are $PascalCase, consts are $ALL_CAPS)
Properties should use $camelCase. We make an exception for properties tied to database fields that are $snake_case.
We do string concatenation like "my " . $value . " string";
Avoid adding redundant comments like
/** * Class MeasureScope * @implements Illuminate\Database\Eloquent\Scope */class MeasureScope implements ScopeFunctions
Section titled “Functions”Function params must be declared with a type.
Functions must declare their return type.
Don’t use array as a param type or return type. Instead declare a class, allocate an instance of that class and pass the instance around. This ensures type safety. Where we pass around arbitrary associative arrays, we have no idea what keys should be in the array and there’s no guarantee that a given key will be present.
For one line anonymous functions, avoid the syntax like
$search = "hi";function ($q) use ($search) { $search_string = '%' . $search . '%'; // we had to "use ($search)"}and instead use the more modern syntax like
$search = "hi";fn ($q) => ( $search_string = '%' . $search . '%'; // $search is already in scope! ...etc}Laravel
Section titled “Laravel”Queries / Eloquent Models
Section titled “Queries / Eloquent Models”Raw Queries
Section titled “Raw Queries”Avoid writing queries with regular string concatenation like this:
$users = User::where(fn ($q) => $q->whereRaw("LOWER(name) LIKE '" . strtolower($search_string) . "'"))Instead use this syntax with the question mark and the array of values to fill in:
$users = User::where(fn ($q) => $q->whereRaw("name ilike ?", [$search_string]))Eloquent does some safety checks to protect against SQL injection attacks on the second form that aren’t present on the first form.
Don’t use “magic” attributes. Instead write regular PHP functions.
Controllers
Section titled “Controllers”Conventions and best practices about Laravel Controllers.
Passing Properties to a View
Section titled “Passing Properties to a View”Prefer passing values in the controller’s view or render function, rather than having the view call a public function in the controller. This makes it clearer what values the view depends on.
If a controller property isn’t required by a Livewire component, default to making it private. This improves performance, because public member variables are passed back-and-forth from the web browser with every Livewire request. See the documentation here. This also makes the code easier to read.
Security Sidebar:
DO NOT store sensitive data in public properties. Data stored in public properties is made visible to the front-end JavaScript, and therefore, to the user.
When to not use view()
Section titled “When to not use view()”Lazy loading data, such as from an Eloquent query, can be quite useful. It is convenient and can help initial page load times. The default should be to keep as much logic as possible in the component/controller unless these features are explicitly desired (and as long as you are not creating “N+1” issues).
Livewire
Section titled “Livewire”Avoid computed properties. PHPStan doesn’t like them. See the documentation here.
Blade Templates
Section titled “Blade Templates”We do string concatenation like this
"foo {{ $bar }}"
Conditional Classes
Section titled “Conditional Classes”The wrong way to add conditional classes to an element:
<button class="rfp-tab-bid-form-nav-button @if ($selectedTeamBuildingId == $teamBuilding->id) active @endif" wire:click="selectTeamBuilding({{ $teamBuilding->id }})"> {{ $teamBuilding->full_street_address }}</button>The right way to do it:
<button @class([ "rfp-tab-bid-form-nav-button", "active" => $selectedTeamBuildingId == $teamBuilding->id, ]) wire:click="selectTeamBuilding({{ $teamBuilding->id }})"> {{ $teamBuilding->full_street_address }}</button>Use $this::CONSTANT, rather than self::CONSTANT in blade templates
Naming Conventions
Section titled “Naming Conventions”Follow this example:
/** @test */public function itShouldDoThis(): void{ ...}because it doesn’t start with “test”, you need to add /** @test */ on the line above
Feature Tests
Section titled “Feature Tests”Unit Tests
Section titled “Unit Tests”MVVC + Repository Pattern
Section titled “MVVC + Repository Pattern”- TODO (use repository instead of query scopes on model)
