Code style

Basic code standards and naming conventions that helps our code to be clean and neat.

General rules

  • Code style must follow PSR-1, PSR-2 and PSR-12.
  • If a method return nothing, it should be indicated with void.
  • Don’t use docblocks for methods that can be fully type hinted (unless we need a description)
  • We should type a property whenever possible. Avoid using a docblock. (PHP 7.4+)
// good
class Foo
{
    public string $bar;
}

// bad
class Foo
{
    /** @var string */
    public $bar;
}

Ternary operators

// Good
$result = $object instanceof Model
    ? $object->name
    : 'A default value';

$name = $isFoo ? 'foo' : 'bar';

// Bad
$result = $object instanceof Model ?
    $object->name :
   'A default value';

If statements

// Good
if ($condition) {
   ...
}

// Bad
if ($condition) ...

Strings

Prefer string interpolation above sprintf and the . operator.

// Good
$greeting = "Hi, I am {$name}.";

// Bad
$greeting = 'Hi, I am ' . $name . '.';

Early return

To keep readability in functions and methods, it is wise to return early if simple conditions apply that can be checked at the beginning of a method:

// Good
if (!$foo) {
    return null;
}
// do work
return $work;
// Bad
if ($foo) {
    // do work
} else {
    return null;
}

Comments

// There should be a space before a single line comment.

/*
 * If you need to explain a lot you can use a comment block. Notice the
 * single * on the first line. Comment blocks don't need to be three
 * lines long or three characters shorter than the previous line.
 */

// $this->foo = $bar; There should be a space before single line code comment.

Whitespace

In general, we should always add blank lines between statements, unless they’re a sequence of single-line equivalent operations. This isn’t something enforceable, it’s a matter of what looks best in its context.

public function getPage($url)
{
    $page = $this->pages()->where('slug', $url)->first();

    if (!$page) {
        return null;
    }

    if ($page['private'] && ! Auth::check()) {
        return null;
    }

    return $page;
}

Configuration

Configuration files must use kebab-case.

config/
  pdf-generator.php

Configuration keys must use snake_case.

// config/pdf-generator.php
return [
    'chrome_path' => env('CHROME_PATH'),
];

We should avoid using the env helper outside of configuration files. Create a configuration value from the env variable like above.

Routes

Public-facing urls must use kebab-case.

http://code.imagdic.me/docs/code-style/
http://code.imagdic.me/docs/code-style/kebab-case/my-own-documentation

Route names must use camelCase.

Route::get('open-source', 'OpenSourceController@index')->name('openSource');

<a href="{{ route('openSource') }}">
    Open Source
</a>

All routes have an http verb, that’s why we like to put the verb first when defining a route. It makes a group of routes very readable. Any other route options should come after it.

// good: all http verbs come first
Route::get('/', 'HomeController@index')->name('home');
Route::get('open-source', 'OpenSourceController@index')->middleware('openSource');

// bad: http verbs not easily scannable
Route::name('home')->get('/', 'HomeController@index');
Route::middleware('openSource')->get('OpenSourceController@index');

Route parameters should use camelCase.

Route::get('news/{newsItem}', 'NewsItemController@index');

A route url should not start with / unless the url would be an empty string.

// good
Route::get('/', 'HomeController@index');
Route::get('open-source', 'OpenSourceController@index');

//bad
Route::get('', 'HomeController@index');
Route::get('/open-source', 'OpenSourceController@index');

A route url must always be in plural.

// good
Route::get('movies', 'MovieController@index');
Route::get('movies/{movie}', 'MovieController@show');

//bad
Route::get('opportunity', 'OpportunityController@index');
Route::get('opportunities/{id}', 'OpportunityController@show');
Route::get('people', 'PeopleController@index');
Route::get('person/{id}', 'PeopleController@show');

We should define controllers as tuple in routes.

Route::get('routes/{route}', [App\Http\Controllers\RouteController::class, 'show']);

Read more about this at freek.dev

Controllers

Controllers that control a resource must use the singular resource name.

class PostController
{
    // ...
}

Try to keep controllers simple and stick to the default CRUD keywords (index, create, store, show, edit, update, destroy). Extract a new controller if you need other actions.

In the following example, we could have PostController@favorite, and PostController@unfavorite, or we could extract it to a separate FavoritePostController.

class PostController
{
    public function create()
    {
        // ...
    }

    // ...

    public function favorite(Post $post)
    {
        request()->user()->favorites()->attach($post);

        return response(null, 200);
    }

    public function unfavorite(Post $post)
    {
        request()->user()->favorites()->detach($post);

        return response(null, 200);
    }
}

Here we fall back to default CRUD words, store and destroy.

class FavoritePostController
{
    public function store(Post $post)
    {
        request()->user()->favorites()->attach($post);

        return response(null, 200);
    }

    public function destroy(Post $post)
    {
        request()->user()->favorites()->detach($post);

        return response(null, 200);
    }
}

This is a loose guideline that doesn’t need to be enforced.

Views

View files must use kebab-case.


resources/views/open-source.blade.php

class OpenSourceController
{
    public function index() {
        return view('open-source');
    }
}

Validation

When using multiple rules for one field in a form request, avoid using |, always use array notation. Using an array notation will make it easier to apply custom rule classes to a field.

// good
public function rules()
{
    return [
        'email' => ['required', 'email'],
    ];
}

// bad
public function rules()
{
    return [
        'email' => 'required|email',
    ];
}