General
At the most simple level, each Laravel project must follow the code styles set out in the PSR-1 and PSR-2 recommendations. Project style is maintained by a .php_cs
configuration file, in combination with the friendsofphp/php-cs-fixer project. You can run the fixer manually via Composer using composer run-script fix-cs
.
Any variables declared within the context of your application must use camelCase
syntax, except where noted otherwise in this guide.
Always strive to keep your code expressive and human-readable. Make it easy for the next developer that looks at the code to be able to reason about your intent. Realise that that developer could well be you trying to fix an issue at 4pm on a Friday, six months from now.
#
Should I rename my application?Laravel ships with an app:name
command. Never rename your application. Keeping the App
namespace reduces cognitive overhead between applications such that you always import each application's classes from the same namespace when working between different projects.
#
Composer scriptsIn order to ensure tasks are consistently ran between environments, a number of scripts are included in the composer.json
file.
cs-fix
- Apply code style fixes, as defined in the project's
.php_cs
file check-security
- Check your application isn't using dependencies with known security vulnerabilities, using sensiolabs/security-checker
test
- Run your application's full test suite
#
WhitespacePHP will be interpreted whether you write it with no whitespace or if you write it with double spacing. For readability, it is better to cater to your fellow developers rather than for the PHP interpreter. Don't be afraid to use whitespace.
// Good
public function normalisePhoneNumber($input)
{
$input = preg_replace('/[^0-9]+/', '', $input);
if (trim($input) === '') {
return '';
}
return $input;
}
// Bad: everything is cramped together making it hard to read
public function normalisePhoneNumber($input)
{
$input = preg_replace('/[^0-9]+/', '', $input);
if (trim($input) === '') {
return '';
}
return $input;
}
Avoid extra whitespace inside conditional braces ({}
). We want the code to breath, but there's no need to be wasteful.
// Good
if (trim($input) === '') {
return '';
}
// Bad
if (trim($input) === '') {
return '';
}
#
ExceptionsExceptions are a good way to control the flow of your code and provide detailed and descriptive error messages to developers.
When you inline exceptions in your code, it can quickly become difficult to understand all the possible error cases as they may be split across different files, which in turn makes it harder to see why you might be raising exceptions wholistically.
public function index()
{
if ($this->invalidStep(request('step'))) {
throw new RuntimeException(
vsprintf('The step [%s] is invalid. Valid steps are: %s', [
request('step'),
$this->validSteps()->implode(', '),
])
);
}
// ...
}
Placing exception messages behind static methods on custom exception classes allows you to keep your code clear and concise, hiding the implementation detail i.e. the precise message and formatting in a class specifically responsible for that behaviour.
// SignupException.php
class SignupException extends RuntimeException
{
public static function invalidStep($step, $validSteps)
{
return new static(vsprintf('The step [%s] is invalid. Valid steps are: %s', [
$step,
$this->validSteps()->implode(', '),
]));
}
}
// SignupsController.php
public function index()
{
if ($this->invalidStep(request('step'))) {
throw SignupException::invalidStep(request('step'), $this->validSteps());
}
}
Co-locating messages inside your custom exception classes makes it not only easier to track each message and its formatting, but also to determine when an exceptions messages start to drift apart in focus, allowing you to split them up into new exception classes with a narrower focus and more concise API.
Learn more about formatting exception messages from Ross Tuck.
#
Don't talk to strangersThe Law of Demeter is a particularly useful design guideline that helps you to build more concise APIs. Consider the following Eloquent model.
class Post extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
}
When a new Comment
is added to a Post
you might write something along the following lines in your controller.
// PostCommentsController.php
public function store(Post $post)
{
$post->comments()->create([
'author_id' => auth()->id(),
'comment' => request('comment'),
]);
return redirect()->route('posts.show', $post);
}
The Law of Demeter helps to decouple your code by encouraging you to ensure that each unit only has limited knowledge of other units. That is to say that your controller should not need to know how specifically a Comment
is added to a Post
.
Instead, consider abstracting this implementation detail behind a new method. In doing so, your controller needn't know how a Post
and Comment
would be related, and if the implementation changes, the call to do so doesn't. SOLID!
// Post.php
public function addComment($author, $comment)
{
$this->comments()->create([
'author_id' => $author->id,
'comment' => $comment,
]);
}
// PostCommentsController.php
public function store(Post $post)
{
$post->addComment(auth()->user(), request('comment'));
return redirect()->route('posts.show', $post);
}