Skip to content

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 Scope

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
}

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.

Conventions and best practices about Laravel Controllers.

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.

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).

Avoid computed properties. PHPStan doesn’t like them. See the documentation here.

We do string concatenation like this "foo {{ $bar }}"

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

Follow this example:

/** @test */
public function itShouldDoThis(): void
{
...
}

because it doesn’t start with “test”, you need to add /** @test */ on the line above

  • TODO (use repository instead of query scopes on model)