# General PHP rules

# Code styles

First and foremost, be sure your code style follows PSR-12.

# Comments

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.