# General PHP rules
# Code styles
First and foremost, be sure your code style follows PSR-12.
# Comments

Mostly, keep from writing comments unless they are absolutely needed to explain some code functionality.
👍
class Article
{
public function getSurnameAttribute()
{
$splitName = explode(' ', $this->name);
return $splitName[count($splitName) - 1];
}
}
👎
class Article
{
public function getSurnameAttribute(): string
{
// Split the name by the space character
$splitName = explode(' ', $this->name);
// Take the last element of the array and return it
return $splitName[count($splitName) - 1];
}
}
# Docblocks
Do not write dockblocks on methods if you can typehint them (unless you need a description).
👍
class Youtube
{
public function getIdFromUrl(string $url): string
{
//
}
}
👎
class Youtube
{
/**
* Return the Youtube ID from a passed in URL
*
* @param string $url
* @return string
**/
public function getIdFromUrl(string $url): string
{
}
}
# Strings
Prefer string interpolation to string concatenation with the . operator
👍
$url = "https://www.youtube.com/watch?v={$id}?playlist={$playlistId}";
👎
$url = 'https://www.youtube.com/watch?v=' . $id . '?playlist=' . $playlistId;
If it is absolutely not possible to use interpolation, it is perfectly ok to use concatenation 👌.
# Ternary operators
TODO
# If statements
# Brackets
Always use curly brackets.
👍
if ($condition) {
return $result;
}
👎
if ($condition) return $result;
# Else
When possible try to avoid using else. Overly relying on using if-else can quickly lead to overly nested and unreadable code.
👍
if (empty($article)) {
abort(404);
}
if (!$article->visible) {
abort(404);
}
// ...
return $article;
👎
if (!empty($article)) {
if ($article->visible) {
// ...
return $article;
} else {
abort(404);
}
} else {
abort(404);
}
# Compound ifs
Prefer separate if statements over compound conditions.
👍
if (!$article->title) {
// one condition logic
}
if (!$article->description) {
// one condition logic
}
if (!$article->url) {
// one condition logic
}
👎
if (!$article->title || !$article->description || !$article->url ) {
// logic for all 3 conditions
}
# Whitespace
Whitespaces should generally be added between statements. This makes for more readable and non-stuffy feeling code.
👍
public function index(Request $request)
{
$request->validate([
'resource' => ['required', 'exists:resources,id', 'integer'],
'query' => ['required', 'string'],
// ...
]);
$articles = $this->getArticles($query);
if (empty($articles)) {
about(404);
}
return $articles;
}
👎
public function index(Request $request)
{
$request->validate([
'resource' => ['required', 'exists:resources,id', 'integer'],
'query' => ['required', 'string'],
]);
$articles = $this->getArticles($query);
if (empty($articles)) {
about(404);
}
return $articles;
}
No extra lines between {} brackets. Unneeded and can lead to the code looking much bigger than it is.
👍
if ($article->hasTags()) {
return $tags;
}
👎
if ($article->hasTags()) {
return $tags;
}
# Configs
Use kebab-case for configuration file names.
config/
api-resource.php
Use snake_case for configuration keys.
return [
'docker_path' => env('DOCKER_PATH', null),
];
# Laravel specific
# Routing
Use plural names for API routes and singular for parameters
👍
Route::get('articles/{article}', [ArticleController::class, 'show']);
👎
Route::get('article/{article}', [ArticleController::class, 'show']);
Use fully qualified class name resolutions (::class) when writing routes instead of strings with the default namespace
👍
use App\Http\Controllers\Api\Playlists\PlaylistController;
use App\Http\Controllers\Api\Playlists\PlaylistMemberController;
Route::apiResource('playlists', PlaylistController::class);
Route::put('/playlists/{id}/members', [PlaylistMemberController::class, 'show']);
👎
Route::apiResource('playlists', 'Api\Playlists\PlaylistController');
Route::put('/playlists/{id}/members', 'Api\Playlists\PlaylistController@show');
# Controllers
Use singular naming conventions when writing controller names
👍
class ArticleController
{
...
}
👎
class ArticlesController
{
}
Make use of __invoke when writing single action controllers.
👍
class ArticleMainImageController
{
public function __invoke()
{
//
}
}
👎
class ArticleMainImageController
{
public function show()
{
//
}
}
# Views
Use kebab-case for view files.
👍
resources/
views/
simple-pagination.blade.php
# Validation
Always prefer using request validation helpers, unless you really have to use the validator instance.
👍
$request->validate([
'client_id' => ['required', 'exists:clients,id'],
'name' => ['required', 'min:2', Rule::unique(Branch::class)->where('client_id', $request->input('client_id'))],
'address' => ['nullable', 'min:2'],
'city' => ['nullable', 'min:2'],
'country' => ['nullable', 'min:2'],
'phone' => ['nullable', 'min:2'],
'email' => ['nullable', 'min:2'],
]);
👎
$validator = Validator::make($request->all(), [
'client_id' => ['required', 'exists:clients,id'],
'name' => ['required', 'min:2', Rule::unique(Branch::class)->where('client_id', $request->input('client_id'))],
'address' => ['nullable', 'min:2'],
'city' => ['nullable', 'min:2'],
'country' => ['nullable', 'min:2'],
'phone' => ['nullable', 'min:2'],
'email' => ['nullable', 'min:2'],
]);
if ($validator->fails()) {
//
}
Validator::make($request->all(), [
'client_id' => ['required', 'exists:clients,id'],
'name' => ['required', 'min:2', Rule::unique(Branch::class)->where('client_id', $request->input('client_id'))],
'address' => ['nullable', 'min:2'],
'city' => ['nullable', 'min:2'],
'country' => ['nullable', 'min:2'],
'phone' => ['nullable', 'min:2'],
'email' => ['nullable', 'min:2'],
])->validate();
Always use arrays when declaring validation rules. This prevents mixing styles when declaring custom validation rules, as shown below.
👍
$request->validate([
'client_id' => ['required', 'exists:clients,id'],
'name' => ['required', 'min:2', Rule::unique(Branch::class)->where('client_id', $request->input('client_id'))],
'address' => ['nullable', 'min:2'],
'city' => ['nullable', 'min:2'],
'country' => ['nullable', 'min:2'],
'phone' => ['nullable', 'min:2'],
'email' => ['nullable', 'min:2'],
]);
👎
$request->validate([
'client_id' => 'required|exists:clients,id',
'name' => ['required', 'min:2', Rule::unique(Branch::class)->where('client_id', $request->input('client_id'))],
'address' => 'nullable|min:2',
'city' => 'nullable|min:2',
'country' => 'nullable|min:2',
'phone' => 'nullable|min:2',
'email' => 'nullable|min:2',
]);
# Naming conventions
| Type | Rule | Suffix | Example |
|---|---|---|---|
| Class | PascalCase | MyClass.php | |
| Controller | singular | Controller | PostController.php |
| Model | singular, | Post.php | |
| Table | plural, snake_case | user_posts | |
| Columns | singular, snake_case | created_at, user_id | |
| Route | plural | users/{username}/ban | |
| Named route | dot-notation, snake-case | settings.team | |
| Method | camelCase | getUsersPosts() | |
| Variable | camelCase | $post | |
| View | kebab-case | session-expired.blade.php | |
| Config | kebab-case | services-stripe.php | |
| Event | subject for event | a verb | TeamDeleted.php |
| Provider | Provider | AppServiceProvider.php | |
| Command | Install.php | ||
| Request | Request | CreateTokenRequest.php | |
| Listener | descriptive | Notification? | UpdateTrialEndingDate.php, SendShipmentNotification.php |
| Repository | Repository | UserRepository.php | |
| Resource | singular | User.php | |
| Helper | snake_case | array_has() |
# PHPCS
The PHPCS file is available here, if you for any reason need only this specific file.