This post is from the professional Laravel developer community, original link: learnku.com/laravel/t/3…
Every line of code we add to a project increases its complexity and the possibility of bugs at any time. It could be a few minutes before a client meeting, or it could be during our weekend at the movie theater, not at our keyboard.
To prevent those dire situations, let’s write better code with these seven tips:
1. Specify descriptive names for variables, functions, arguments, and methods:
The code is written once, but it is read and interpreted many times by other developers and by you. Therefore, it is worth the extra time to name the new class or method so that its name indicates its true intent or content. Let’s compare these two lines. Which one is easier to understand?
Let’s compare these two rows. Which is easier to understand?
$evnt->add($req->q);
Copy the code
$event->addTickets($request->quantity);
Copy the code
There is a typo in the first line, the add method is not clear about what was added, and the variable $req is not clear enough to understand that q refers to quantity.
The second example, on the other hand, is easy to understand even for non-developers.
2. Use PHP standards like PSR-2
Never underestimate the importance of writing code in an orderly and consistent manner, as this will allow you to find problems faster.
Consider the following two examples:
public function addTickets($quantity)
{
foreach (range(1, $quantity) as $i)
{
$code = Code::generate(); }
$this->tickets()->create(
[
'code'= >$code,]); }Copy the code
public function addTickets($quantity)
{
foreach (range(1, $quantity) as $i) {
$code = Code::generate();
}
$this->tickets()->create([
'code'= >$code,]); }Copy the code
Both code blocks have the same error: both code blocks create only one ticket when they should create N. But in which block of code did you find the problem faster? Now imagine the consequences of dealing with complex, ill-formed code.
3. Reduce the number of temporary variables
Although we learned how to declare and use temporary variables in chapter 1 of the algorithm, they still make code difficult to read and maintain.
Consider the following example:
$contact = array();
$contact['firstname'] = $user->first_name;
$contact['surname'] = $user->last_name;
$contact['id'] = $user->id;
$contact_emails = array();
$contact_email = array();
$contact_email['email'] = $user->email;
$contact_emails[] = $contact_email;
$contact['emails'] = $contact_emails;
$this->create('contact'.$contact);
Copy the code
$contact = [
'id'= >$user->id,
'firstname'= >$user->first_name,
'surname'= >$user->last_name,
'emails' => [
[
'email'= >$user->email,
],
],
];
$this->create('contact'.$contact);
Copy the code
Which example is easier to understand?
By the way, using the equal sign is a bad habit. Not only is this a violation of PSR-2, it also makes the code difficult to maintain.
So, going back to our second example, this example can be optimized for inline writing by removing the code variable:
public function addTickets($quantity)
{
foreach (range(1, $quantity) as $i) {
$this->tickets()->create([
'code'=> Code::generate(6), ]); }}Copy the code
However, there are situations where using local variables can improve code readability, such as:
function calculateCode($price.$quantity.$deliveryCost)
{
$subtotal = $price * $quantity;
if ($subtotal < 30) {
$subtotal+ =$deliveryCost;
}
return $subtotal;
}
Copy the code
It might be clearer than the following:
<? phpfunction calculateTotal($price.$quantity.$deliveryCost)
{
if ($price * $quantity < 30) {
return $price * $quantity + $deliveryCost;
}
return $price * $quantity;
}
Copy the code
4. Don’t use magic Numbers
If an order is less than 30 yuan, then shipping will not be covered and extra shipping will be paid. At this time we should use constant to mark it. If the order price is less than 30 yuan, then shipping will be paid. Constants are configured as follows:
if ($subtotal < DELIVERY_COST_THRESHOLD) {
$subtotal+ =$deliveryCost;
}
Copy the code
The translation code is as follows:
if(Order price < price without shipping) {$Current order price += Freight price; }Copy the code
In this method, we show how easy it is to use constants, but we can also reuse them for other projects that need to use them.
If we need to change the no-post rule, we can just update one line of constant code, reducing both the repetition and the uncertainty of using fixed numbers in the code.
Divide and conquer
Many scenarios can split overly long code into smaller methods, each with different responsibilities. Such as:
The new method getContactInfo returns an array with the user’s contact information:
$this->create('contact'.$user->getContactInfo());
Copy the code
Object-oriented programming requires us to keep data and functions in one place (classes). We will assemble an array containing contact information in the User model that contains all the User information.
Let’s do another example
$subtotal = $item->price * $quantity;
$subtotal = $this->addDeliveryCost($subtotal);
Copy the code
The method addDeliveryCost returns an amount of the delivery cost, provided that the amount does not exceed a set threshold, otherwise the original amount is returned.
Now let’s remove the local variable and inline the code:
return $this->addDeliveryCost($price * $quantity);
Copy the code
Declaring and using many small methods is a good way to reduce the use of temporary variables in your code.
6. The default solution is simple
Many tutorials that promise you will write better code end up making the code too complex.
If you’re using Laravel and Eloquent, these tutorials will tell you that it’s wrong to put the following code on your controller:
// Somewhere in UserController.php
User::create([
'name'= >$request->name,
'email'= >$request->email,
'password' => bcrypt($request->password),
]);
Copy the code
You should write:
// Somewhere in UserController.php
$this->commandTransport->handleCommand(
new UserCreationCommand(
new UserNameField($request->name),
new UserEmailField($request->email),
new UserPasswordField(bcrypt($request->password)),
)
);
Copy the code
In UserCreationCommandHandler class, you can’t create user, because it violates the principle of SOLID. You should use repository:
class UserCreationCommandHandler
{
//...
public function handle(UserCreationCommand $command)
{
$this->userRepository->create(
$command->name,
$command->email,
$command->password, ); }}Copy the code
And finally, in the UserEloquentRepository class, you’re going to end up calling User::create:
class UserEloquentRepository implements UserRepository
{
//...
public function create(
UserNameField $name,
UserEmailField $email,
UserPasswordField $password
) {
return User::create([
'name'= >$name->getValue(),
'email'= >$email->getValue(),
'password' => bcrypt($password->getValue()), ]); }}Copy the code
After a while, the client asks you to add another field to the User model.
Which is easier? Which solution is more buggy (you might forget to pass a field from one method to another)?
Also, did you notice that bcrypt was called twice in Example 2? So the second example is buggy.
Unfortunately, some interfaces and classes won’t stop you from making mistakes. So, you need to test your code carefully. Speaking of test code:
7. Write automated tests
Accountants use a method called double entry bookkeeping. This method requires them to record all transactions twice. Writing unit tests requires us to write code twice, defining each test once:
function test_order_without_delivery_cost()
{
$order = new Order;
$order->addItem(new Item(['price'= > 20]), 5);$expectedTotal = 20 * 5;
$this->assertSame($expectedTotal.$order->getTotal());
}
function test_order_with_delivery_cost()
{
$order = new Order;
$order->addItem(new Item(['price'= > 20]), 1);$expectedTotal = 20 + DELIVERY_COST;
$this->assertSame($expectedTotal.$order->getTotal());
}
Copy the code
Write the implementation of the code for the second time (the hard task is left to you).
Many developers complain about this practice because it forces us to “double our work,” but by writing code twice, we reduce the likelihood of making the same mistake in the same way (the test might fail if we made two different mistakes). This is why projects that implement some unit testing tend to have minor bugs but require hours of debugging.