Laravel & PHP Style Guide
About Laravel
First and foremost, Laravel provides the most value when you write things the way Laravel intended you to write. If there's a documented way to achieve something, follow it. Whenever you do something differently, make sure you have a justification for why you didn't follow the defaults.
General PHP Rules
Code style must follow PSR-1 and PSR-2.
.editorConfig
Please reference this repository for our standard .editorconfig
configuration.
Variable Naming
- Choose clear, descriptive names.
- Generally speaking, everything string-like that's not public-facing should use camelCase.
- Do not abbreviate variable names as the gains added by shortening typically adds a layer of obscurity to what the variable contains.
// Good
$userName = 'jschutt';
$password = 'BigFoot123';
$lastName = 'Schutt';
$query = 'SELECT * FROM users;'
// Bad
$uName = 'jschutt';
$pw = 'BigFoot123';
$last_name = 'Schutt'; // Prefer camelCase wherever possible
$q = 'SELECT * FROM users;'
Docblocks
Because docblocks aid in IDE Intellisense, implement them.
Only add a description when it provides more context than the method signature itself. Use full sentences for descriptions, including a period at the end.
// Good
class Url
{
/**
* @param string $url
*
* @return \Zaengle\Url\Url
*/
public static function fromString(string $url): Url
{
// ...
}
}
// Bad: The description is redundant.
class Url
{
/**
* Create a url from a string.
*
* @param string $url
*
* @return \Zaengle\Url\Url
*/
public static function fromString(string $url): Url
{
// ...
}
}
Shorten docblocks by importing resources.
// Good
use \Zaengle\Workflow\Generator;
class Demo {
/**
* @param string $title
*
* @return Generator
*/
}
// Bad
class Demo {
/**
* @param string $title
*
* @return \Zaengle\Workflow\Generator
*/
}
Ternary operators
Every portion of a ternary expression should be on it's own line unless it's a really short expression.
// Good
$result = $object instanceof Model
? $object->name
: 'A default value';
$name = $isFoo ? 'foo' : 'bar';
// Bad
$result = $object instanceof Model ?
$object->name :
'A default value';
Comments
Comments should be avoided as much as possible by writing expressive code. If you do need to use a comment format it like this:
// There should be 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.
*/
Whitespace
Statements should have to breathe. In general always add blank lines between statements, unless they're a sequence of single-line equivalent operations. This isn't something enforcable, it's a matter of what looks best in it's context.
// Good
public function getPage($url)
{
$page = $this->pages()->where('slug', $url)->first();
if (! $page) {
return null;
}
if ($page['private'] && ! Auth::check()) {
return null;
}
return $page;
}
// Bad: Everything's cramped together.
public function getPage($url)
{
$page = $this->pages()->where('slug', $url)->first();
if (! $page) {
return null;
}
if ($page['private'] && ! Auth::check()) {
return null;
}
return $page;
}
// Good: A sequence of single-line equivalent operations.
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
Don't add any extra empty lines between {}
brackets.
// Good
if ($foo) {
$this->foo = $foo;
}
// Bad
if ($foo) {
$this->foo = $foo;
}
Precede a return
with a single empty line.
public function show() {
$users = User::all();
return view('users.index', compact('users');
}
Do not include more than 1 empty line for spacing purposes.
// bad
public function show() {
...
}
public function edit() {
...
}
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'),
];
Avoid using the env
helper outside of configuration files. Create a configuration value from the env
variable like above.
Artisan commands
The names given to artisan commands should all be kebab-cased.
# Good
php artisan delete-old-records
# Bad
php artisan deleteOldRecords
A command should always give some feedback on what the result is. Minimally you should let the handle
method spit out a comment at the end indicating that all went well.
// in a Command
public function handle()
{
// do some work
$this->comment('All ok!');
}
If possible use a descriptive success message eg. Old records deleted
.
Routing
Public-facing urls must use kebab-case, and be plural when referring to a resource.
https://zaengle.com/open-source
https://zaengle.com/jobs/front-end-developer
Route names will ideally consist of single words, separated by dots, but must use kebab_case if multiple words are required.
Include the default index
, show
, edit
, create
, etc as the last segment of a named route. Route name segments should be plural.
Route::get('open-source', 'OpenSourceController@index')->name('open-source.index');
<a href="{{ route('open-source.index') }}">
Open Source
</a>
Attempt to use route groups when possible.
// bad: elements are unnecessarily repeated
Route::get('users', 'UserController@index')->name('users.index');
Route::get('users/{user}', 'UserController@show')->name('users.show');
// good: shared elements are in the route group
Route::group([
'as' => 'users.',
'prefix' => 'users'
], function () {
Route::get('/', 'UserController@index')->name('index');
Route::get('{user}', 'UserController@show')->name('show');
});
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 readble. 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');
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 seperate 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, create
and destroy
.
class FavoritePostController
{
public function create(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);
}
}
Views
View files must use kebab_case and be nested within plural named folders of it's parent resource.
resources/
views/
users/
show.blade.php
index.blade.php
// visible at 'site.com/users'
class UserController
{
public function index() {
return view('users.index');
}
}
Validation
All custom validation rules must use snake_case:
Validator::extend('organisation_type', function ($attribute, $value) {
return OrganisationType::isValid($value);
});
Blade Templates
Indent using four spaces.
<a href="/open-source">
Open Source
</a>
Don't add spaces after control structures.
@if($condition)
Something
@endif
Authorization
Policies must use camelCase.
Gate::define('editPost', function ($user, $post) {
return $user->id == $post->user_id;
});
@can('editPost', $post)
<a href="{{ route('posts.edit', $post) }}">
Edit
</a>
@endcan
Try to name abilities using default CRUD words. One exception: replace show
with view
. A server shows a resource, a user views it.