Logo
Code Ranks ×

Laravel Best Practices

22/06/2019  .   8 minutes, 18 seconds to read  .   Posted by Admin
#best-practices #Laravel

Environment

Local Environment

There are multiple ways to run Laravel in the l environment as outlined below:

You are free to choose which local environment that works for you and your team as long as the following conditions are met:

  • Your local environment MUST be able to duplicate the production server or close to its specifications such as PHP version, extensions installed and MySQL version

  • .env file MUST NOT be committed into the repository

  • You SHOULD NOT be connecting to your any production server when you are debugging locally, to prevent accidental corruption of data, unintended API call or similar incident.

  • You SHOULD NOT be using personally identifiable information (PII) of your end-users data or any data that could potentially identify a specific individual such as first/last name, address, a medical condition so on and so forth unless you are explicitly authorized by your company or client to do so.

  • You MUST update the readme.MD file for any special instruction on how to run the app in a local environment, so that other developers who will set up the app in their local machine can follow them properly.

  • While it is possible to WAMP or XAMP for Laravel, this is un-common practice so you should try to familiarize yourself on how server components work and be comfortable in dealing with them.

Staging Environment

Staging servers is used to test software, website or service in a production-similar environment before being set live. It is the role of a staging environment or staging site, to serve as a temporary hosting and testing server for any new software or feature.

  • It is RECOMMENDED to use Continous Integration to automatically run your Tests and keep a record of the results.

Production Environment

You MUST regularly rotate your APP_KEY

APP_KEYS are set when you initialized a new Laravel application or executed the following command

php artisan key:generate

Laravel uses the key for all encrypted cookies, including the session cookie, before handing them off to the user’s browser, and it uses it to decrypt cookies read from the browser. This prevents the client from making changes to their cookies and granting themselves admin privileges or impersonating another user in your application.

Configuration

Environment Variables

You MUST put sensitive information into .env files

Use .env files to store any secure information and retrieve it via env function. There should be no instance on which you will put it inside models/controllers and commit it to Git.

Good

// .env 
API_HOST=https://example.com/api 
API_USERNAME=myuser API_PASSWORD=secret 
// access the value from app/config.php file
 return [ 
    ... 
    'api_host' => env('API_HOST', 'https://defaultdomain.com') 
    'api_username' => env('API_USER', 'defaultuser') 
    'api_password' => env('API_USER', 'defaultpassword') 
    ... 
]

Bad

define('API_HOST', 'https://defaultdomain.com'); 
define('API_USERNAME', 'defaultuser'); 
define('API_PASSWORD', 'defaultpassword'); 
class DomainController extends Controller 
{ 
    public function index() 
    { 
        $api_username 
    }
}

your application key MUST be set. This is the APP_KEY variable in your .env file. You can generate one via

php artisan key:generate

Application Namespace

It is recommended to give your application a name. That is, instead of using the default Approot namespace given by Laravel install, set it to what the application is all about. This can be set via

php artisan app:name YourAppName

The above code makes controllers/models etc resolve into YourAppName\Controllers and YourAppName\Models

Package Configuration

Custom or Package configuration filename MUST be in snake_case

Good

config/my_config.php

Bad

config/MyConfig.php

Config and language files indexes SHOULD be in snake-case

Good

// config/myconfig.php 
return [ 
    'my_api' => [ 
        'domain' => env('API_DOMAIN'), 
        'secret' => env('API_SECRET'), 
    ],
]

Bad

// config/myconfig.php 
return [ 
    'MyApi' => [ 
        'DOMAIN' => env('API_DOMAIN'), 
        'SECRET' => env('API_SECRET'), 
    ],
]
The best way to figure out if you have implemented best-practice in configuring your app, is if the codebase could be made open source at any moment without compromising any credentials

Naming Conventions

The following is the generally accepted naming conventions being used by Laravel Community:

Controllers

Controller name MUST start with a noun (in singular form) followed by the word “Controller”.

Good

class ArticleController extends Controller {}

Bad

class ArticlesController extends Controller{}

class wp_articlesController extends Controller{}

class Article extends Controller{}

You SHOULD Use Resource Controllers unless you have any particular reason not to do so

Good

class DomainController extends Controller { 
    public function index(){

        // list domains 
    } 
    public function create(){

        // show create form 
    } 
    public function store(Request $request){ 

        // handle the form POST 
    } 
    public function show($id){

        // show a single domain 
    }
    public function edit($id){

        // show edit page 
    } 
    public function update(Request $request, $id){

        // handle show edit page POST 
    } 

    public function destroy($id){

        // delete a domain 
    } 
}

Bad

class DomainController extends Controller { 
    public function list(){
    // list domains 
    } 
    public function create_or_save(){
        // show create form then handle save 
    } 
    public function show_edit($id){
        // show a single domain then show edit page 
    } 
    public function delete($id){
        // delete a domain
    }  
}

Models

Model names MUST be in singular form with its first letter in uppercase

Good

class Flight extends Model { ... }

Bad

class Flights extends Model{ ... }
class flight extends Model{ ... }

hasOne or belongsTo relationship methods MUST be in the singular form

Good

class User extends Model { 
    public function phone() { 
        return $this->hasOne('App\Phone'); 
    } 
}

Bad

class User extends Model { 
    public function phones() { 
        return $this->hasOne('App\Phone'); 
    } 
}

Any other relationships other than above MUST be in the plural form

Good

class Post extends Model { 
    public function comments() { 
        return $this->hasMany('App\Comment'); 
    } 
}

Bad

class Post extends Model { 
    public function comment() { 
        return $this->hasMany('App\Comment'); 
    } 
}

Model properties should be in snake_case

Good

$user->created_at

Bad

$user->createdAt

Methods should be in camelCase

Good

class User extends Model { 
    public function scopePopular($query) { 
        return $query->where('votes', '>', 100); 
    }
}

Bad

class User extends Model { 
    \public function scope_popular($query) { 
        return $query->where('votes', '>', 100); 
    }
}

Functions

Laravel comes with a lot of useful helper functions, but you can also define your own helper functions, given the following conditions:

You SHOULD place your custom helper functions by creating a file called helper.php

Good

project_folder/app/helper.php 
project_folder/app/Http/helper.php

Bad

project_folder/functions.php

You MUST use the Composer’s autoloading capability to load your functions

Good

// file composer.json 
"autoload": { 
    "files": [ "app/helpers.php" ],
}

Bad

// file app/Http/Controllers/HomeController.php 
class HomeController.php { 
    function index(){ 
        require_once(app_path("helpers.php")); 
    } 
}

You MUST check if the function exists before defining it

Good

if (! function_exists('my_custom_helper')) { 
    function my_custom_helper($key, $default = null) { 
        // ... 
    } 
}

Bad

function my_custom_helper($key, $default = null) { 
    // ... 
}

Other General guides with functions

  • If the function length exceeds 25 lines, you SHOULD break it down to multiple functions
  • Each function SHOULD have a Unit Test associated with it

Routes

Routes should be in the plural form of the resource it is trying to manipulate and SHOULD be all lower-case

Good

Route::get('/users', 'UserController@index');

Route::resource('photos', 'PhotoController');

Bad

Route::get('/user', 'UserController@index');

Route::get('/UsersList', 'UserController@index');

Route::resource('PHOTO', 'PhotoController');

Named Routes SHOULD use snake_case and dot notation

Good

Route::get('/user', 'UserController@active')->name('users.show_active');

Bad

Route::get('/user', 'UserController@active')->name('users.show-active');

Route::get('/user', 'UserController@active')->name('show-active-users');

Variables

The general rule for the variable is it SHOULD be in camelCase

Good

$articlesWithAuthor

Bad

$articles_with_author

Collection names SHOULD be descriptive and in the plural form

Good

$activeUsers = User::active()->get()

Bad

$users = User::active()->get();
$user = User::active()->get();
$User = User::active()->get();

Single Object SHOULD be descriptive and in the singular form

Good

$activeUser = User::active()->first();

Bad

$users = User::active()->first();

Views

You SHOULD use snake_case as the file name of your Blade templates

Good

show_filtered.blade.php

Bad

showFiltered.blade.php show-filtered.blade.php

Good

// $api_results is passed by controller 
<ul> 
    @foreach($api_results as $result) 
        <li>{{ $result->name }}</li> 
    @endforeach 
</ul>

Bad

@php 
    $api_results = json_decode(file_get_contents("https://api.example.com")); 
@endphp 
<ul> 
    @foreach($api_results as $result) 
        <li>{{ $result->name }}</li> 
    @endforeach 
</ul>

Database Conventions

Table and Fields Naming

Table names MUST be in the plural form and MUST be all lower-case

Good

class CreateFlightsTable extends Migration { 
    public function up() { 
        Schema::create('flights', 
        function (Blueprint $table) {
        })
    }
}

Bad

class CreateFlightsTable extends Migration
{
    public function up()
    {
        Schema::create('flight', function (Blueprint $table) {
        })
    }
}

class CreateUsersTable extends Migration
{
    public function up()
    {
        Schema::create('MyUsers', function (Blueprint $table) {
        })
    }
}

Pivot table names MUST be in singular model names in alphabetical order

Good

post_user
articles_user
photo_post

Bad

posts_users
user_articles
post_photos

Table column names SHOULD be in snake_case without the model name

Good

username
title
thumb_url

Bad

UserName
_title
ThumbUrl
post_title

Foreign keys MUST be the singular model name with the _id suffix

Good

user_id

Bad

userid
siteid
Memberid
TransactionID

Primary Keys SHOULD be “id”

Good

id

Bad

ID
pkid
guid

Database Alterations

You MUST not be changing the database schema directly, use Database Migrations instead

Good

php artisan migrate

Bad

  • use of PHPMyAdmin
  • directly executing ALTER statement in mysql console / CLI
  • using SQL file to change the DB

Migration filenames MUST follow to the following pattern

creation of table

yyyy_mm_dd_<timestamp>_create_<table name>_table

Good

2019_06_06_164210_create_domains_table.php

Bad

2019_06_06_164210_domains.php