Larablog

--redirect-to laracasts.com

8 Ways Laravel Helps You Write Clean and Terse Code

by Benjamin Crozat
--table-of-contents

The definition of clean code is of course subjective. It can even become a source for heated debates between developers on the web. For this article, we will focus specifically on features and techniques in Laravel that enable us to write less code.

1. Facades

Since their initial inception, Laravel facades have proven to be extremely controversial. Nonetheless, they provide an intuitive, straightforward interface that many people enjoy.

Facades provide a "static" interface to classes that are available in the application's service container. Laravel ships with many facades which provide access to almost all of Laravel's features. - Laravel Documentation

Here's a quick illustration of how they work. When you write Cache::get('foo'), the parent Illuminate\Support\Facades\Facade class triggers a magic __callStatic() method to redirect your call to whatever object instance is currently registered in the container. If you review Illuminate\Support\Facades\Cache, you will see that it's bound to the string, cache.

namespace Illuminate\Support\Facades;

class Cache extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'cache';
    }
}

This string is then used to fetch the appropriate cache Repository instance from the service container. Think of it as a fancy lookup table.

$lookup = [
    'cache' => Illuminate\Cache\Repository::class,
];

It's naturally a bit more complicated than the example above, but you get the general idea.

For this to make perfect sense, I recommend learning more about Laravel's service container. But don't worry, it's not as complicated as it sounds. See it as a big smart box capable of finding and instantiating objects.

Perhaps this Laracasts episode will help: Toy Chests and Contracts.

Without the facade approach, we would follow traditional dependency injection, like so:

<?php

namespace App\Http\Controllers;

use Illuminate\Cache\Repository;

class FooController extends Controller
{
    public function __construct(
        protected Repository $cache
    ) {
    }

    public function foo()
    {
        $bar = $this->cache->get('bar');
    }
}

This is a perfectly fine and common way to inject your dependencies; however, newcomers or those hoping for a more terse syntax might enjoy Laravel's facades. As with everything, they are an optional feature.

<?php

namespace App\Http\Controllers;

class FooController extends Controller
{
    public function foo()
    {
        $bar = Cache::get('bar');
    }
}

Before we move on to the next section, keep in mind that using Laravel's cache() helper function is nearly identical to calling the Cache Facade. Let's say it's just a matter of preference.

Learn more about facades on the official documentation.

2. Implicit Binding

Instead of manually fetching route resources from the database, why not delegate it to Laravel?

Implicit binding starts in your routes/web.php file. You must change your way of declaring routes as follows:

-Route::get('/blog/{id}', [PostController::class, 'show']);
+Route::get('/blog/{post}', [PostController::class, 'show']);

As long as you have specified the {post} parameter (named after the corresponding Model) in your route declaration, you can fetch the Post resource automatically, without needing to manually write a query to request it from your database:

<?php

namespace App\Http\Controllers;

use App\Models\Post;

class PostController extends Controller
{
-   public function show(int $id)
+   public function show(Post $post)
    {
-       $post = Post::findOrFail($id);
-
        return view('posts.show', compact('post'));
    }
}

Under the hood, Laravel still of course queries your database; but this part is now tucked away from view, making for a cleaner-looking controller.

Learn more on the official documentation.

3. Automatic Injection

Laravel can magically inject dependencies into your code. You've surely dealt with a request object in Laravel, yes? And I bet you've never had to create the instance yourself. Most of the time, you get it via your controller, like so:

<?php

namespace App\Http\Controllers;

class PostController extends Controller
{
    public function store(Request $request)
    {
        //
    }
}

Simply ask for a Request instance, and Laravel gives it to you! Neat!

But how does it work? Again, it's not magic. Using PHP's reflection capabilities under the hood, Laravel automatically injects an instance of whatever you type-hinted in your store() method (not just Illuminate\Http\Request).

Of course, if you'd prefer to use constructor injection, that method works as well:

<?php

namespace App\Http\Controllers;

class PostController extends Controller
{
    public function __construct(
        public Request $request
    ) {
    }

    public function store()
    {
        $this->request->validate(…);

        //
    }
}

This automatic dependency injection not only works for controllers, but also within console commands, jobs, etc.

4. Redirect Routes

Laravel's router contains a few surprises that will delight those who are constantly searching for ways to unclutter their code.

For instance, let's imagine that you want to redirect an old URL to a new one. Would you create an entire controller just for a one-line redirect? Sure, you could handle it like this:

Route::get('/old', function () {
    return redirect('/new');
});

Or, you could leverage short closures to make it slightly more concise:

Route::get('/old', fn () => redirect('/new'));

However, you can even go a step further and leverage Laravel's redirect() method, like so:

Route::redirect('/old', '/new');

All three code snippets above do the exact same thing. Let's go with the shortest one. Cool, huh?

5. View Routes

In the same vein, Laravel can also accommodate you when you only need to register a route that renders a simple view. Instead of creating a controller or a closure-based route, reach for the view() method.

Route::view('/about', 'pages.about');

The /about path will now serve the view located in resources/views/pages/about.

6. Collections

Collections in Laravel are essentially wrappers around arrays, providing a variety of incredibly useful methods for manipulating them. You'll surely find that they provide a far more consistent experience compared to PHP's native (and awkward) functions.

Need to loop through an array? Wrap it in a collection and use a more modern object-oriented syntax to do so:

collect([1, 2, 3])->each(function ($item) {
    //
});

This next one will make your head spin. Want to send a user's invoices by email? Leverage higher-order messages to keep your code tidy:

$user->invoices()->get()->each->sendByEmail();

Not only are collections concise, they're also quite a bit more intuitive. You will never ask yourself if the needle comes first or second again!

7. Conditional Trait

Conditional is a simple PHP trait that Laravel ships with. It includes just two methods: when() and unless().

namespace Illuminate\Support\Traits;

use Closure;
use Illuminate\Support\HigherOrderWhenProxy;

trait Conditionable
{
    public function when($value = null, callable $callback = null, callable $default = null)
    {
        …
    }

    public function unless($value = null, callable $callback = null, callable $default = null)
    {
        …
    }
}

You'll find it being used across countless classes in the framework, such as Builder, Factory, Logger, PendingRequest, and Carbon. It offers a way to conditionally apply logic using a fluent API instead of via traditional if statements.

One common use case is when working with Eloquent's query builder. For example, instead of writing:

$query = Model::query();

if ($something) {
    $query->where('something', true);
} else {
    $query->where('something_else', true);
}

$models = $query->get();

You could opt for a more fluent approach, using the when() method.

$models = Model::query()->when(
        $something,
        fn ($query) => $query->where('something', true),
        fn ($query) => $query->where('something_else', true),
    )->get();

I like this a lot. What about you?

8. Endless First Party Packages

Of course, one of the greatest strengths of Laravel that we still haven't discussed is its vast collection of both first and third-party packages.

These packages take care of common tasks, saving developers the nightmare of doing all the work from scratch. Imagine billing, authentication, debugging, feature flags, and so much more... just a composer require away.

Here are some examples of popular official Laravel packages:

  • Laravel Fortify is a backend implementation of all the authentication features you'd need for you app. Registration, login, password reset, profile updates, two-factor authentication, and more!
  • Laravel Cashier simplifies and handles paid subscriptions via Stripe for your next SaaS. It's such a time saver!
  • Laravel Sanctum makes API authentication for SPAs and mobile apps a breeze.

And some examples of popular third-party packages:

  • spatie/laravel-permission is an incredibly popular third-party package for Laravel that adds permissions and roles management in your app. Spatie has tons of fantastic packages that will save you a staggering amount of time.
  • barryvdh/laravel-debugbar adds a convenient interface to help you debug your projects. With it, you'll have a clear overview of slow database queries, how much time your app takes to render, how much RAM it consumes, and more.
  • maatwebsite/excel lets you import or export data from Excel sheets. The package automatically handle performance optimizations so that you can focus on building the features you need.

By leveraging these packages, you can defer responsibilities onto others who might have more experience, and who regularly work on the packages. This not only saves time and effort but also ensures that the codebase remains clean and concise.

And after all, why reinvent the wheel when there are experts who have already built and refined the components you need?

Conclusion

No matter what your coding style is, the only things that truly matter are results and having fun. Experiment with some of the various ways of writing code I mentioned here, and see how it goes!

Here's more articles you might enjoy...

TypeScript, Groups, and Heretics

I can still remember it. Eight years old, second grade, Mrs. King’s class, and I’m openly stressing to my friends about a particular embarrassment that I’m hoping to conceal from the other students. To tell you the truth, the one thing I can't remember is what this embarrassment exactly was. But, hey, it was second grade in the 90s; it could have been anything. Even a jean length that didn’t adequately cover your ankles was cause for ridicule in 1993.

by Jeffrey Way
Build a Simple Online Store Using Laravel Folio and Volt

What makes the Laravel ecosystem magical is that there are always new things to learn and experiment with. Some of the latest additions to it are Laravel Folio and Volt. Embracing these new tools and principles can enhance your productivity and transform how you build web applications with Laravel. Let me show you by building a simple online store.

by Benjamin Crozat

Newsletter? Yes.

Don't worry: we'll only send you an email when we have something important to share.

Level Up Your Programming With Laracasts

$15 a month for everything we know about programming. Everything.

Join Laracasts