3 simple tips to improve your Laravel code today
Tags: laravel, php, tips, programming
Published: 2017-12-24 19:27:10

---
title: 3 simple tips to improve your Laravel code today
published: true
description: Step up your game with 3 simple, but effective tips to improve your PHP/Laravel code and become a better programmer
tags: laravel, php, tips, programming
---

I've been using Laravel for a little while now and, over the past few months, really focused on how to improve my code and project structure. So why not share the knowledge for Laravel padawans?

Let's examine together a few real life examples in a Laravel project and how we can refactor and learn from them. Ready to improve your code?

## 1. Refactor your collections, son

Imagine that you're developing a website where students participate in projects and are graded every week, and your job is to display to their mentors the current average score of all students in a given project, effectively grading the project's average student score, in order to track progression.


You may come up with a Project class like this:

```PHP
<?php

class Project extends Model{
    /** ... code omitted for brevity **/
    
    public function studentsAverageScore() {
        $participants = $this->participants;

        $sum = 0;
        $totalStudents = 0;
        foreach($participants as $participant) {
            if ($participant->isStudent()) {
                $totalStudents++;
                $sum += $participant->student->lastRating()->averageScore();
            }
        }

        return $sum / $totalStudents;
    }

}
```

Our method `studentsAverageScore()` seems to work quite nicely. We loop through our participants, check if the participant is a student (some participants can be their tutors, for example) and we keep summing up their last ratings average score (the average of each criteria in a given rating).

The issue here is that if someone has to come back to this method later for a bugfix or a changed requirement, your teammate (or even yourself) is going to "compile" this `foreach` in his head before doing anything. Loops are generic and, in the case of this one, we do multiple things in each pass: we check if they're a student and then add it to a sum that we only deal again in the return statement.

Of course, this is a relatively simple example, but imagine if we did more? What if we wanted to filter this to only **some** students or add different weights to each one? Maybe consider all their ratings, not only their last one? This could get out of hand quickly.

So how can we express these checks and calculations better? More semantically? Fortunately, we can use a bit of functional programming with the methods that Eloquent gives us.

Instead of checking manually if a given participant is a student, using the `filter` method can return only the students for us:

```PHP
<?php

public function studentsAverageScore() {
    $participants = $this->participants;

    $participants->filter(function ($participant) {
        return $participant->isStudent();
    });
}
```

Using the `filter` function, we can just pass a function as an argument to return only the participants that fulfill our condition. In this case, this call will return a subset of `$participants`: only the students.

Naturally, we also need to finish this by calculating their average score. Should we do a `foreach` now? It would still be suboptimal. There's a built-in solution in another function, conveniently called `average`, in our returned Eloquent collection. It follows rules similar to `filter`, where we just return which value we want to average from the whole colllection. The final code looks like this:

```PHP
<?php

public function studentsAverageScore() {
    $participants = $this->participants;

    return $participants->filter(function ($participant) {
        return $participant->isStudent();
    })->average(function ($participant) {
        return $participant->student->lastRating()->averageScore();
    });
}
```

Since average returns a number, this is exactly what we want. Pay attention how we **chained** our calls, and how much better the code looks. You can almost read it like a natural language: Get the participants filtered by who is a student, then average their last rating's score and return the value. The intention of our code is cleare and our code, cleaner and more semantic.

This applies not only to PHP or Eloquent, really - you can do similar things with javascript. It's out of the scope of this article, but if you never heard of filter, map and reduce in the context of javascript, go check it out.

## 2. Be aware of the N+1 Query problem

Let's do some piggybacking on our code from Tip #1. Note how we fetch the Student model for a given participant in the `average` function. This is highly problematic, because we're doing an additional SQL query "behind the scenes" by loading many student models one at a time.

A better solution for this would be to **eager load** them on our first query. When we do that, we can reduce the number considerably, instead of having N+1 queries (hence the name of that dreaded issue).

It's easy to do it with eloquent with the `with` method. Let's refactor the code above:

```PHP
<?php

public function studentsAverageScore() {
    $participants = $this->participants()->with('student')->get();

    return $participants->filter(function ($participant) {
        return $participant->isStudent();
    })->average(function ($participant) {
        return $participant->student->lastRating()->averageScore();
    });
}
```

Now, whenever we call `$participant->student`, the student model related to the participant was already cached during our first call (`$this->participants()`).

(By the way, there's still one non-optimized call related to this tip in the code above - can you spot it? Leave it in the comments)

## 3. Improve your Blade files

I love Blade. It's a powerful templating engine that ships with Laravel and has amazing syntax. But are you using all of its potential?

We all have to use `@foreach` in order to display some collection to the users, but what if the collection is empty? A simple answer would be to use a `@if` statement before the `@foreach`. Turns out there's a better way to write that:

```PHP
<?php

@forelse ($participants as $participant)
    <li>{{ $participant->email }}</li>
@empty
    <p>No participants in this project :(</p>
@endforelse
```

`@forelse` is very similar to `@foreach`, but with the added `@empty` section. Much cleaner than using an `@if`, isn't it?

Speaking of `@if`, blade also has another directive that I love: `@unless` and `@endunless`. It's the exact opposite of `@if` and it reads much better than just an `@if` with a negative condition. If you ever used Ruby, you know how it works.

There's also some shortcuts now in Laravel 5.5 for authentication: `@auth`/`@endauth` and `@guest`/`@endguest`. It's really similar to just using `@if(Auth::check())`, but reads much better. Here's a quick example from Laravel 5.5 docs:

```PHP
<?php

@auth
    // The user is authenticated...
@endauth

@guest
    // The user is not authenticated...
@endguest
```

There's much more in the [official docs](https://laravel.com/docs/5.5/blade), and you can write your own directives too. I highly recommend it - makes your template files much easier to reason instead of a bunch of meaningless ifs.

## Got more useful tips?

That's it for now. Leave a comment if you have a cool tip that you like to use in your projects! And remember: better code is less time spent refactoring and fixing bugs. Your team will appreciate :)