Laravel Coding Guidelines
To ensure consistent quality across different projects, we follow our own standards and recognised best practices during programming.
Our coding guidelines can also be applied to Statamic projects.
Workflow
Working on a project
Clone the project if you haven’t already:
git clone [email protected]:author/some-repo.git
Change directories so you are in the project directory.
Create a branch for your feature:
git checkout -b feature/TN-1337_feature_name
In this case, the ticket number for this feature is TN-1337. If provided, always reference the ticket number in any commit or branch name.
After creating the branch, create pull request
The Pull Request will merge into develop
Name the PR like so: “Draft: TN-1337 New feature X”
When the Pull Request is ready for review, remove the “Draft: “-prefix
Commits
Commit often and commit small, coherent parts of the code (“atomic commits”).
Commit more often during the day and keep commits smaller. The goal should be “atomic commits” that can be reverted easily, without side effects. Do not mix topics in commits.
Provide a commit note that describes the change. That makes it easier to keep track of what is being done.
Codestyle
Use php-cs-fixer or Laravel Pint for code-formatting.
In most of our projects, you can do this conveniently by running
./vendor/bin/pint
// or
./vendor/bin/php-cs-fixer
Routes for development or testing
Routes for development-purposes may not be
placed in routes/web.php
or routes/api.php
.
Create a new routes file:
routes/testing.php
Register it only in local or testing environment:
// bootstrap/app.php
<?php
// use statements
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
then: function () {
if (App::environment('local', 'testing')) {
Route::prefix('testing')
->name('testing.')
->group(base_path('routes/testing.php'));
}
},
)
Laravel Naming Conventions
Naming things is hard, thats why sticking to the rules is very worth it.
Variables
In general, stick to camel-case naming for variables:
$apiKey = config('services.myservice.key');
Use descriptive names for variables:
// Good
$users = User::all();
$user = User::find(1);
// Bad
$u = User::all();
$user = User::all();
Models
Models must be named as singular entities. Example:
// Good
class Product extends Model
{
//
}
// Bad
class Products extends Model
{
//
}
Guarded or Fillable?
In general, use the $guarded
attribute:
protected $guarded = ['id'];
An exception from this rule might be a case like the User-Model, where for security reasons, some attributes should not be mass-assignable:
protected $fillable = [
'name',
];
Model properties
Model properties must be in snake case and follow the naming scheme of their table column names.
// Good
$model->updated_at
// Bad
$model->updatedAt
Controllers
Suffix the Controller Class name with its purpose, example: ThemeSyncController
Rules for .env files
Do not commit any secrets in version control, not even in .env.example!
Only use env() in config/-Files. Example:
// Good. In config/services.php
[
'api_key' => env('API_KEY'),
]
// Bad. Outside of config/...
$key = env('API_KEY');
No trailing Slashes in URLs.
Route-Files
When defining a route, use a PHP class path to identify a Controller instead of using just a controller name or a class path as string.
Add a use-statement for the entire Controllers-directory to save space in route definitions.
// Good
use App\Http\Controllers;
Route::get('users', [Controllers\UserController::class, 'index']);
// Bad
Route::get('users', 'UserController@index');
String-Concaternation
Always aim to concaternate like this in PHP:
"{$object->attr}_doc.pdf"
Similiar to this in JS/TS:
${object.attr}_doc.pdf
Comments
Omit Docblocks if attributes and return types are defined. Otherwise use a docblock.
Do not add an @author name attribution.
Do not add redundant information, like “Constructor of Class” for the __construct method.
Rather describe why you do something, than what you do.
CRUD Best Practices
Instead of combining CREATE and EDIT to a single component or method, keep actions separated. Instead, build one or more component that share common parts, for example the form fields.
Request Validation
Define the rules of a Request-Validation-Class as an array, don’t use pipe notation. It makes it easier to use custom rules:
Component Design
Stick to the CRUD pattern
When creating components, do not mix functionalities like create and edit in one component. This will result in conditionals like this:
// Bad
if (action == 'create') {
// create record
}
if (action == 'edit') {
// edit record
}
This violates the single responsibility principle. Create a component for each action, e.g.: create.vue and edit.vue.
Vue
Nuxt Components / Pages
All components must have a name-Attribute:
export default {
name: 'DocumentEdit',
}
Working with money values
Background: Martin Fowlers take on money in software.
Store money as minor values (Cents)
Use Model Attributes for casting to moneyphp/money.
Example:
protected function price(): Attribute
{
return Attribute::make(
get: function(int $value) {
return new Money($value, new Currency( code: 'EUR' ));
},
);
}
Tailwind CSS, HTML
Custom classes go first in a class-Attribute, e.g: