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:
- Homestead - requires VirtualBox, Vagrant
- Valet - only runs in macOS
- Laradock - requires Docker Engine
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
You MUST not make non-UI-related operations inside blade templates
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