Codycrunch

Laravel Tricks

April 4, 2019 • ☕️ 3 min read


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

class Project extends Model{
    /** ... code comitted 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;
    }

}

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

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

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

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

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

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

2. Try this clean, one-method way to query ‘where’

Let’s say you want to do the following SQL query:

 SELECT * FROM `users`
    WHERE
        `name` = 'some_name'
        AND `email` = 'some_email'
    LIMIT 1

You can achieve this with one 'where' method call. Eloquent will work out that the “and” in the middle means two seperate where clauses:

User::whereNameAndEmail('some_name','some@email')->first();

And as well as “and”, you can also do an “or” like this:

User::whereFooOrBar('foo value','bar value')->first();

3. Me-return hasil data setelah update querying

Untuk beberapa kasus, saya ingin mereturn hasil update data. Jika sebelumnya menggunakan method seperti di bawah ini :

$row = User::find($id)->update(request()->all());
return $row; // true

Maka response dari hasil di atas akan menjadi boolean. Untuk memenuhi kebutuhan maka kita dapat menggunakan cara seperti berikut :

$row = tap(User::find($id))->update(request()->all())->fresh();
return $row; // data: { ... }

4. String Plural

$friends = 5;
return "I have " . $friends. " " . str_plural('friend', count($friends));
/// I have 5 friends

5. Date Filtering

$q->whereDate('created_at', date('Y-m-d'));

$q->whereDay('created_at', date('d'));

$q->whereMonth('created_at', date('m'));

$q->whereYear('created_at', date('Y'));

6. Collection filters

Keeps the item only if the closure returns true

$customers = Customer::all();
$javanese_customers = $customers->filter(function($customer)
{
    return $customer->province == 'Jawa Tengah';
});

7. Where Has

$callback = function($query) {
    $query->where('something', '=', 'something');
}
$submissions = Post::whereHas('submissions', $callback)->with(['submissions' => $callback])->get();