Joseph Silber https://josephsilber.com A blog by Joseph Silber Thu, 19 May 2022 13:03:30 +0000 en-US hourly 1 Releasing Bouncer: Roles and Permissions for Laravel apps https://josephsilber.com/posts/2022/05/17/releasing-bouncer-laravel-roles-and-permissions Tue, 17 May 2022 00:00:00 +0000 https://josephsilber.com/posts/2022/05/17/releasing-bouncer-laravel-roles-and-permissions A personal post with some musings about my journey through the years - from inception to final release

]]>
Releasing Bouncer: Roles and Permissions for Laravel apps

Bouncer

After many years in development, 56 releases, 1.3 million downloads, and over 2,800 unsolicited stars, Bouncer has finally reached version 1.0. It has been extremely reliable and stable for quite a while now, and is being used in production by countless apps worldwide.

This is a personal update, with some musings about my journey through the years - from inception to final release. For technical information on how to use Bouncer day to day, check out the extensive documentation or listen to me discuss it with Matt Stauffer on The Laravel Podcast.


What is Bouncer?

Before diving into my personal journey, here's a quick overview of what Bouncer is, and how it fits into the greater Laravel ecosystem.

Bouncer is an open source package for dynamically managing roles and permissions in the database, fully integrated with Laravel's Gate.

Without getting into detail, here's a short list of some of its key features:

...and much more. For details, check out the full documentation, or just glance over the cheat sheet.

The original idea for Bouncer

Back in August of 2015, Taylor added a new Authorization system in Laravel 5.2, called the Gate. This provided a nice API for defining permission checks in your app for various actions, through simple define callbacks and full on policies, as well as hooks throughout the system for checking permissions against what you've defined.

As soon as I started playing with this, I knew it will be the future of ACL for all Laravel apps. It's just so good. Taylor has this amazing sense for clear and intuitive APIs, and the Gate abstraction really brought that to light.

However, one thing was missing from the built-in authorization system: dynamic permissions, stored in the database. The way the gate is built, all checks are performed by hard-coded functions defined in your app, so there's no way to allow your admins to control any of it at runtime via some dashboard UI. As Taylor's original commit clearly states:

[The built-in gate] gives a structure to organizing logic that authorizes actions on entities. It does not make any determination on how "user roles" are stored

At the time, there were many other popular ACL systems which did support tweaking permissions at runtime, but they had one major drawback: they were all built prior to Laravel's Gate. They were completely separate systems; if you decided to use them, you would forgo all of the niceties and beautiful integrations that Laravel's gate offered.

So I decided to build an open source package that would get you the best of both worlds: dynamic DB-driven permissions, fully integrated with Laravel's gate. We made some improvements to the gate checks in Laravel 5.3, making it more streamlined and predictable, thus easier to store those abilities in the DB.

The Bouncer name and logo

The name Bouncer came to me pretty early on. A bouncer's duty is to provide security at the gate and to check people's permissions. So this was a very natural paring to the Gate in Laravel.

Interestingly, the logo designer I was working with at the time (who is not a native English speaker) did not get the reference. Here are some original logos he designed:

Bouncer early logo drafts

The two to the right are clearly inspired by a bouncing motion. Oof! Hopefully native speakers do get it right away.

After quickly clarifying the meaning of the word bouncer, we started iterating on actual bouncers. We tried friendly bouncers, threatening bouncers, bearded bouncers, square-jawed bouncers, and a ton of different variations. Here are just a few:

Bouncer early logo drafts

I'm quite fond of what we ultimately ended up with:

Bouncer

It exudes a strong sense of security, but the roundness makes it feel friendlier and less threatening.

Technical underpinnings

Bouncer's raison d'etre is to seamlessly integrate with Laravel's gate. To achieve that, I set out with a single goal in mind: you should only have to interact with Bouncer when assigning roles and abilities to users. For the actual authorization checks, Laravel's own hooks throughout the system should work automatically, without any special Bouncer syntax.

The way Bouncer hooks into Laravel's gate checks is pretty simple. The Gate lets you define a global before callback, which gets called before any other checks you've defined: if your before callback allows or disallows an action, no further checks will run.

While the before callback was originally intended for stuff like "allow everything for admins", I immediately realized that this would be the perfect place to hook up dynamic checks, allowing me to query the database for any permissions. This is how it originally worked (we later switched it to using the after callback - you can read more about that in this thread).

Documentation

From the get-go, documentation has been extremely important to me. Open source projects live or die by their documentation, so I wanted Bouncer's docs to be the best it possibly can. And especially in the Laravel ecosystem, Taylor has set an extremely high bar for meticulous documentation.

In a way, clear docs are sometimes even more important than the code itself. Without telling your users how to use your tool, very few of them will source dive to figure it out. They'll just move on to the next thing.

I attribute a lot of Bouncer's success to the clear documentation, but there's still a lot more to do on that front. Being the creator and having a clear picture of the whole puzzle, it's easy to forget what people new to the tool struggle with.

For example: as stated earlier, Bouncer is only meant to be used for assigning roles and permissions to users. The actual authorization checks are to be handled like you would in any standard Laravel app. So I figured I don't have to repeat all that, since it's clearly outlined in the Laravel docs. Nevertheless, I still see people struggle with that a lot. They set up their their roles and permissions, then don't know where to go from their. That's an area I still want to flesh out in the docs.

Working up to the release

Let me just state this right now: postponing the 1.0 release till now has done my users a disservice. Bouncer has been stable for years, and actively used in production the world over. And yet, I was always hesitant to release it, since I knew there's so much more I want to add to it. I discussed this at length with Matt on the podcast: I fell down the trap of wanting to make it perfect before releasing it, which is obviously impossible. As Voltaire has warned: "Perfect is the enemy of good".

So as I'm releasing version 1.0 of Bouncer, there are still 2 outstanding features that I really wanted to have included in the initial release, but didn't make the cut:

  1. Per-model roles. For the longest time, people have been clamoring to have a way of assigning roles to users only for given model (or model class). Here's what that code would look like:

    // Note: this is not implemented yet
    Bouncer::allow('editor')->to(['view', 'edit'])->everything();
    Bouncer::assign('editor')->to($user)->for(Invoice:class);

    With that, the user would be able to do see and edit all invoices, but nothing else. This can of course now be done directly without a role, but doing it through a role would provide another level of flexibility.

    I've tried tackling this many times, but it turns out to be quite tricky, because caching becomes a real nightmare. I still hope to tackle it one day. We'll see.

  2. Ability constraints. Allowing arbitrary constraints on a given ability would add way more granular control:

    // Note: this is not implemented yet
    Bouncer::allow($user)
        ->to('view', Post::class)
        ->where('is_confidential', false);

    If you go source diving in Bouncer, you'll find some code and tests where I started implementing this. It's far from done, but stay tuned.

Overall, Bouncer is in a really great place. Every good product has a long roadmap, and thinking I can get to the end of that road before releasing a 1.0 was both foolish and unrealistic.

So there, live on air during the podcast, I released Bouncer. It felt great!

Enjoy using it!

Well that's a wrap. I hope you try Bouncer in your app, and enjoy using it. Bouncer's API is designed to feel like prose, with every method call reading like a proper English sentence. Try it, and let me know if you get that feeling too!

]]>
Lazy Collections in Laravel https://josephsilber.com/posts/2020/07/29/lazy-collections-in-laravel Wed, 29 Jul 2020 00:00:00 +0000 https://josephsilber.com/posts/2020/07/29/lazy-collections-in-laravel A deep dive into Laravel's Lazy Collections, and how to process enormous amounts of data with very little memory.

]]>
Lazy Collections in Laravel

Laravel's LazyCollection class is a powerful tool that lets you process enormous amounts of data with very little memory. It's a recent addition to the framework (introduced in Laravel 6), and is not that well known yet.

To get people more familiar with the power of lazy collections, I gave a talk at the first Laravel Worldwide Meetup:

Doing More With Less: Lazy Collections in Laravel

A deep dive into Lazy Collections. We'll learn what they are, how they work under the hood, and how to use them to vastly decrease your app's memory footprint.

This post is the written counterpart of that talk. There's only so much you can cram into a 30 minute talk, so this post also has a ton of additional information that didn't make the cut for the talk.


We'll start by looking at regular collections and how they're not really suitable for huge amounts of data. Then we'll see how lazy collections can help us with that.

Regular Collections

The Collection class has been a staple of Laravel since time immemorial. As the documentation says, regular collections wrap a native PHP array, providing a fluent, convenient API to interact with the underlying array.

To create a regular collection, you pass it an array of values. To play around, let's start with a simple array of numbers:

use Illuminate\Support\Collection;

new Collection([1, 2, 3, 4, 5]);

In fact, Laravel collections have a handy times method, which is a convenient way to create a collection with a range of numbers:

Collection::times(100); // [1, 2, 3, ... 98, 99, 100]

Once we have a collection instance, we can start chaining methods onto it:

Collection::times(100)  // [1, 2, 3, ... 98, 99, 100]
    ->map(fn ($number) => $number * 2)  // [2, 4, 6, ... 196, 198, 200]
    ->filter(fn ($number) => $number % 20 == 0); // [20, 40, 60, ... 160, 180, 200]

While this simplified example isn't really useful in real life, it shows an important fact about regular collections: all values are held in memory, and every method call creates a new in-memory array of values (wrapped in a new Collection instance).

Running Out of Memory

Keeping all values in memory is okay when we have a relatively short list, but as the amount of data we're dealing with starts to grow, we'll quickly run out of memory.

To illustrate, let's try creating a collection class with a billion values:

Collection::times(1000 * 1000 * 1000);

If you try to run this on your computer, you'll most likely get an error that you've run out of memory:

Allowed memory size of 3154116608 bytes exhausted (tried to allocate 34359738376 bytes)

The reason for this is quite simple: the times method creates a collection that stores all its values in memory. Trying to allocate memory for a billion numbers will obviously exceed the amount of memory available.

Furthermore, even if we only want to work with a small subset of the collection (for example, to take the first 1,000 even numbers):

Collection::times(1000 * 1000 * 1000)
    ->filter(fn ($number) => $number % 2 == 0)
    ->take(1000);

...it'll still blow up, because each step builds an entire collection in memory. When we call the times method, it has no way of knowing that we're about to filter its values; that only happens in the next step.

Switching to Lazy Collections

If we try the above code using a lazy collection:

use Illuminate\Support\LazyCollection;

$collection = LazyCollection::times(1000 * 1000 * 1000)
    ->filter(fn ($number) => $number % 2 == 0)
    ->take(1000);

...we won't run out of memory, since none of those values have been generated yet (more on this shortly). In fact, this code snippet uses virtually no memory at all!

How's that possible? Through the power of Generators.

To fully understand lazy collections, we first need a solid understanding of PHP generators:

Generator Functions in PHP

The "generator function" is a powerful construct introduced in PHP 5.5. Trying to read the PHP documentation on it can be quite daunting, so let's break it down step by step, learning it in mini, bite-sized lessons.

Generator functions may return multiple values

Regular functions in PHP can only return a single value. After returning the first value, all subsequent return statements will be ignored.

function run() {
    return 1;
    return 2;
}

dump(run());

This will only dump 1. After the first return statement, the function is terminated, and no futher code in the function is executed.

To return multiple values from a function, we can use the yield keyword instead of return.

function run() {
    yield 1;
    yield 2;
}

dump(run());

But hold on! If you actually run this code, you'll probably be surprised by its output, which will be something like this:

Generator {
    executing: {...}
    closed: false
}

A Generator object? We never created this object, nor did we return that from our run function. So where is it coming from?

Lesson 1: This is what "generator functions" are. The mere presence of the yield keyword in a function instructs PHP that this is not an ordinary function, but a generator function. Generator functions are treated entirely different from regular functions.

We can see this by adding a dump into our generator function:

function run() {
    dump('Did we get here?');
    yield 1;
    yield 2;
}

run();

This will not dump anything at all. Why?

Lesson 2: Calling a generator function does not even execute any of the code inside of the function body. Instead, we get a Generator object, which is a mechanism by which we can step through the function's code step by step, pausing at each yield statement.

Using current and next to get the generator function's values

To start stepping through the code in the generator function, use the Generator's current method. This will actually start executing the code until the first yield statement, and return the value that was yielded.

function run() {
    yield 1;
    yield 2;
}

$generator = run();

$firstValue = $generator->current();

dump($firstValue);

Running the code above will give us 1, which is the first value that is yielded from within the generator function.

Lesson 3: Use the Generator's current method to "power up" the generator function. The code in the function will execute till the first yield statement, and its value will be returned.

Unlike regular functions, the generator function is not terminated after the first value is returned. The function is still alive, waiting for us to get the next value from the Generator.

This time, instead of dumping the current value right away, let's first move the generator to the next yield statement, and only log that second value:

function run() {
    yield 1;
    yield 2;
}

$generator = run();

$firstValue = $generator->current();
$generator->next();
$secondValue = $generator->current();

dump($secondValue);

This will dump 2, which is the value returned by the second yield statement.

Lesson 4: Use the Generator's next method to advance the generator function to the next yield statement.

Using yield in a loop

Up until now, we've dealt with multiple hard-coded yield statements inside the generator function. But the true power of the yield statement can only be realized once we start using it in a loop:

function generate_numbers()
{
    $number = 1;

    while (true) {
        yield $number;

        $number++;
    }
}

$generator = generate_numbers();

dump($generator->current()); // Dumps: 1
$generator->next();
dump($generator->current()); // Dumps: 2
$generator->next();
dump($generator->current()); // Dumps: 3

Wait, what? An infinite loop???

Yep! Since the code in each iteration of the loop will not execute on its own, this loop is not actually infinite. It will only produce as many values as we pull out of it (by using a combination of current and next).

Lesson 5: Using yield from within an infinite loop does not actually cause an infinite loop. Since the generator function's execution pauses after every yield, it'll only run the loop as many iterations as the amount of values that are later requested.

Using foreach with Generators

Constantly calling current and next will get tiring really fast. So instead of having to do that manually, PHP supports passing a Generator directly to foreach!

Let's try dumping the first 20 numbers from our generator:

$generator = generate_numbers();

foreach ($generator as $number) {
    dump($number);

    if ($number == 20) break;
}

Notice the break there? Since we have an "infinite loop" in the generator function, we must take care to only pull a finite amount of values out of it. Otherwise this foreach will cause an actual infinite loop.

Lesson 6: Use foreach to easily enumerate all values in a generator. If there's an infinite loop in your generator function, take care to stop your foreach loop at some point.

Composing Generator Functions

Now, instead of having to add in a break manually, let's create a general helper function called take. We'll pass it 2 arguments: a generator, and a "limit":

function take($generator, $limit)
{
    foreach ($generator as $index => $value) {
        if ($index == $limit) break;

        yield $value;
    }
}

Note: The above take helper is intentionally very naive, to keep the sample simple. For a more robust version of it, read the source (you should be able to understand that at the end of this post).

Now that we have this take helper in place, we can compose the two generator functions together (composition is just a fancy word that means: using two functions together by passing the result of one function into the other function). We'll pass the numbers generator to the take generator function, and use that in our foreach loop:

$allNumbersGenerator = generate_numbers();
$twentyNumbersGenerator = take($allNumbersGenerator, 20);

foreach ($twentyNumbersGenerator as $number) {
    dump($number);
}

We can also do this directly inline, without the intermediate variables (shown above for clarity):

foreach (take(generate_numbers(), 20) as $number) {
    dump($number);
}

Lesson 7: You can create helper generator functions. They take a generator, and return a different generator based on the values in the first one. This lets you compose a whole chain of operations together, which is the actual secret sauce that drives Laravel's lazy collections!

Now that we have a solid understanding of generators and generator functions in native PHP, let's get back to Lazy Collections.

Lazy Collections Wrap a Generator Function

Unlike regular (eager) collections, which wrap a native PHP array, lazy collections wrap a native PHP generator function.

Let's wrap a lazy collection around our numbers generator function:

use Illuminate\Support\LazyCollection;

$collection = LazyCollection::make(function () {
    $number = 1;

    while (true) {
        yield $number++;
    }
});

We now have a collection that "holds" a sequence of all numbers. I put "holds" in quotes because, as we've seen, these numbers haven't been generated yet.

Being a collection means we have access to the countless methods provided to us by Laravel. In fact, the eager Collection class and the LazyCollection class both implement the same Enumerable interface, providing the same set of APIs to both collection classes.

Using our lazy collection of all numbers, let's just take the first 10 of them, using the collection's take method:

$firstTenNumbers = $collection->take(10);

Ah... that's a much nicer API than our own function composition above!

Each method that we chain onto our lazy collection returns a new LazyCollection instance, which internally wraps a new generator function.

To bring this full circle, let's revisit our example from the beginning of this post (with a slight twist):

$collection = LazyCollection::times(INF)
    ->filter(fn ($number) => $number % 2 == 0)
    ->take(1000);

We start out with a collection that "holds" an infinite number of items, then filter them to only even numbers, and then take the first 1,000 values.

As we already know by now, not a single value has been generated yet. That's why this code uses almost no memory at all. These numbers will only be generated once we start enumerating them (using foreach, or the collection's each method):

LazyCollection::times(INF)
    ->filter(fn ($number) => $number % 2 == 0)
    ->take(1000)
    ->each(fn ($number) => dump($number));

Now, and only now, will those numbers be generated!

Exercise: How many numbers will be generated in total? Don't just keep reading. Think for a second before you proceed. In the end, how many numbers will be generated by the first generator in this snippet above?

The answer is two thousand. The code snippet above will generate a total of two thousand values. Why? Think about how the filter function works: it pulls values out of the original generator, discards any values that don't pass the filter, and then yields only the values that do pass the filter. So to get to 1,000 even numbers, it had to throw away 1,000 odd numbers!


At this point, you hopefully understand how lazy collections work under the hood (to learn more, take a deep dive into the source). Next up, let's get away from using simple numbers, and see how we can use lazy collections in a real-world scenario.

Streaming File Downloads With Lazy Collections

One of the most useful use-cases for lazy collections is streaming data exports to a download file. Streaming an export gives us many benefits:

  1. We don't have to keep all source records in memory.
  2. We don't have to build up the whole export file in memory.
  3. The user doesn't have to wait till the whole file is built up on the server. The download can start instantly!

Let's see how we can use a lazy collection to stream a CSV file to the browser, using the versatile league/csv package.

To start, let's create a lazy collection with a million dummy login logs:

$logins = LazyCollection::times(1000000, fn () => [
    'user_id' => 24,
    'name' => 'Houdini',
    'logged_in_at' => now()->toIsoString(),
]);

Now that we have this huge dataset (which hasn't actually been generated yet), let's see how we can stream it as a CSV file directly to the browser, without having to build it up as a huge CSV string in memory.

Streamed Downloads in Laravel

First off, since we're using Laravel, we need to figure out how streamed downloads work in Laravel. Laravel expects all routes to return a response (either a View, something that may be converted to JSON, or an actual Response object). Route handlers should never directly output anything to the client (by using echo et al).

For streamed downloads, use a StreamedResponse, which uses a callback to stream the response when the framework is ready for it. Within the callback, we can output stuff directly to the client:

Route::get('streamed-download', function () {
    return response()->streamDownload(function () {
        // From within here we can "echo"
        // or write to the PHP output stream
    }, 'the-filename.txt');
});

Writing to the PHP Output Buffer

Next up, let's see how to use league/csv's Writer class to stream a CSV file to the browser. The Writer expects a file or a stream, to which the CSV records will be written. We'll use the native php://output stream which, as the name suggests, lets us write to the output buffer directly. From the docs:

php://output is a write-only stream that allows you to write to the output buffer mechanism in the same way as print and echo.

Putting It All Together

Putting it all together, we end up with this:

use Illuminate\Support\LazyCollection;
use League\Csv\Writer;

Route::get('streamed-download', function () {
    $logins = LazyCollection::times(1000 * 1000, fn () => [
        'user_id' => 24,
        'name' => 'Houdini',
        'logged_in_at' => now()->toIsoString(),
    ]);

    return response()->streamDownload(function () use ($logins) {
        $csvWriter = Writer::createFromFileObject(
            new SplFileObject('php://output', 'w+')
        );

        $csvWriter->insertOne(['User ID', 'Name', 'Login Time']);

        $csvWriter->insertAll($logins);
    }, 'logins.csv');
});

To recap:

  1. We create a lazy collection with a million dummy records of login logs.
  2. The route returns a StreamedResponse, with a callback that the framework will call once it's ready for the stream.
  3. Inside the callback, we create a CSV Writer using the php://output buffer, so that we can write CSV records directly to the streaming download file.
  4. insertOne adds a single row in the CSV file, which acts as the header row of column names.
  5. The lazy collection is passed directly to insertAll, which internally uses foreach to spin through all records in the generator.

At no point do we have all of our records in memory; not the original raw data, nor the generated CSV. Each record is generated one by one, and imediately written to the streaming file in the user's browser!


We learned how to write data in a lazy manner. Now we'll learn how to read data lazily.

Reading Files Lazily With a Lazy Collection

Another area where lazy collections are extremely helpful is reading a file lazily. That is: reading a file line by line, processing each line as it comes in, never loading the whole file into memory.

NDJSON Log Files

A good example for reading files as a stream involves using the NDJSON format (also called newline-delimited JSON):

NDJSON is a convenient format for storing or streaming structured data that may be processed one record at a time. It's a great format for log files.

Here's how it would look to store recent logins as NDJSON:

{ "user_id": 2, "name": "Alice", "timestamp": "2020-07-29T22:51:30.352869Z" }
{ "user_id": 1, "name": "Jinfeng", "timestamp": "2020-07-29T22:54:05.280122Z" }
{ "user_id": 2, "name": "Alice", "timestamp": "2020-07-29T22:54:16.565840Z" }

Notice that this isn't an array, nor are there commas at the end of each line. Each line is a self-contained JSON object, which can be parsed and processed on its own.

Writing a File to Disk Lazily

To play around with this, we need an actual NDJSON file. Let's take a little detour, and see how to create such a file with a bunch of fake data, using a lazy collection:

LazyCollection::times(10 * 1000)
    ->flatMap(fn () => [
        ['user_id' => 1, 'name' => 'Jinfeng'],
        ['user_id' => 2, 'name' => 'Alice'],
    ])
    ->map(fn ($user, $index) => array_merge($user, [
        'timestamp' => now()->addSeconds($index)->toIsoString(),
    ]))
    ->map(fn ($entry) => json_encode($entry))
    ->each(fn ($json) => Storage::append('logins.ndjson', $json));

This will create a file at storage/app/logins.ndjson and add 20,000 fake login records, all without ever holding more than a single line in memory!

Reading a File Line by Line

Now that we have a log file, let's play around with it and see how we can run some statistics on this file, without loading the whole thing into memory.

To create a lazy collection that reads a single line of a file at a time, we can use the native PHP fopen and fgets functions, as shown in the 4th example of the original lazy collections PR:

$logins = LazyCollection::make(function () {
    $handle = fopen(storage_path('app/logins.ndjson'), 'r');

    while (($line = fgets($handle)) !== false) {
        yield $line;
    }
});

We now have a $logins lazy collection, where each item in the collection is a standalone JSON string.

Note: Laravel 8.0 introduced a new File::lines($path) method, which returns a LazyCollection with the lines of the file - just like our code above. In the next example, we'll use the File::lines() method directly.

Let's see how many times Alice has logged in:

$loginCountForAlice = $logins
    ->map(fn ($json) => json_decode($json))
    ->filter() // In case we have empty lines
    ->where('name', 'Alice')
    ->count();

We now have the total count of how many times Alice has logged in (100,000 times), without ever keeping more than a single log entry in memory!

Reading & Streaming Files

For our last practical example, we'll combine reading and streaming a file download, using a single lazy collection.

Combining everything we've learned so far, we can parse the log file and stream it to the user as a CSV file download:

use File;
use Illuminate\Support\LazyCollection;
use League\Csv\Writer;

Route::get('read-and-stream', function () {
    $logins = File::lines(storage_path('app/logins.ndjson'))
        ->map(fn ($json) => json_decode($json, true))
        ->filter();

    return response()->streamDownload(function () use ($logins) {
        $csvWriter = Writer::createFromFileObject(
            new SplFileObject('php://output', 'w+')
        );

        $csvWriter->insertOne(['User ID', 'Name', 'Login Time']);

        $csvWriter->insertAll($logins);
    }, 'logins.csv');
});

This will read the source file one line at a time, parse the JSON, convert it to a CSV record, and stream it to the user. All that, while using virtually no memory at all!

Converting a Regular Collection Into a Lazy Collection

Before we go, let's quickly see how we can convert a regular collection into a lazy collection, and why we would even want to do that.

Let's say we're dealing with a huge dataset that can't be streamed, such as the results of a single API call:

use Illuminate\Support\Collection;

function get_all_customers_from_quickbooks() : Collection
{
    // Run some code that gets all QuickBooks customers,
    // and return it as a regular, eager collection...
}

The actual implementation of that function is not important. All we care about is that it returns a huge eager collection that can't be streamed to us, so we have to keep it all in memory.

However, lazy collections may still come in handy. To see how, let's count the amount of customers in France who have a balance over 100 euros:

$count = get_all_customers_from_quickbooks()
    ->where('country', 'FR')
    ->where('balance', '>', '100')
    ->count();

Simple, right? But let's look at what's happening under the hood: every time we call where, we're actually creating a brand new Collection, which means we're creating another array in memory to hold all filtered values.

We can do better.

Even though the original values are all held in memory, the subsequent filters shouldn't have to store their values in memory. We can use the lazy method on the regular Collection class to convert it to a LazyCollection:

$count = get_all_customers_from_quickbooks()
    ->lazy()
    ->where('country', 'FR')
    ->where('balance', '>', 100)
    ->count();

While the original values will still all be kept in memory, no additional memory will be allocated when we filter its results. Pretty neat.

Phew! That's a Wrap

That's gonna do it! This hopefully shed some light on how lazy collections work, and how you can use them to do more in your app with far less memory than you would otherwise need.

If you want to play around with the techniques we learned here, check out this GitHub repository, which has working examples of all code in this article. All of the relevant code is in the single routes/web.php file, each in its own route, ready for you to run in your browser and toy around with. Enjoy!


P.S. We didn't even get a chance to touch on the query builder's cursor method.

We'll leave that for another day.

]]>
How to rid your database of PHP class names in Eloquent's Polymorphic tables https://josephsilber.com/posts/2018/07/02/eloquent-polymorphic-relations-morph-map Mon, 02 Jul 2018 00:00:00 +0000 https://josephsilber.com/posts/2018/07/02/eloquent-polymorphic-relations-morph-map Using Relation::morphMap() to instruct Eloquent to store more generic values for polymorphic types.

]]>
How to rid your database of PHP class names in Eloquent's Polymorphic tables

Polymorphic relations let you set up a relationship between many different model types, without the need for extra tables. This works by storing the "morphable type" (explained below) in the database, in addition to the morphable type's ID.

By default, the morphable type stored in the database is the model's full class name. While this works, it tightly couples your database to your PHP application. Let's look at how we can instruct Eloquent to use more generic values for these morphable types.

A short refresher on polymorphic relationships

If you're already familiar with polymorphic relationships, you can skip right ahead.

Polymorphic relationships are one of Eloquent's most powerful features. Let's take a quick look at what they are and why we'd want to use them:

Imagine we have an addresses table that stores our customer's addresses:

Table name:
  addresses

Columns:
  id
  customer_id
  street_number
  street_name

The customer_id column points to a given record in the customers table, connecting the address to the customer via the customer's ID. That's a pretty standard SQL relationship.

Using Eloquent, we set up the relationship in the Customer model using a HasOne relationship:

class Customer
{
    public function address()
    {
        return $this->hasOne(Address::class);
    }
}

This works great if Customer is the only entity type in our app that has an address. What if we have other entities in our app, such as Warehouse, that can also have an address? We don't want to create a separate table for warehouse addresses; we'd like to use the same addresses table for both customer and warehouse addresses.

This is what polymorphic relations are for. In addition to storing the foreign record's ID in the table, we'll now also store which type of entity the address belongs to:

Table name:
  addresses

Columns:
  id
- customer_id
+ addressable_id
+ addressable_type
  street_number
  street_name

Note that we...

  1. Renamed the customer_id column to addressable_id, since an address can now belong to any entity type (the addressable prefix (called the morph name) can really be anything; pick whatever makes sense in your context).

  2. Added the addressable_type column, which will tell us which entity type this address belongs to.

Next, we need to update our Customer model to use a MorphOne relationship:

class Customer
{
    public function address()
    {
        return $this->morphOne(Address::class, 'addressable');
    }
}

...and do the same for our Warehouse model:

class Warehouse
{
    public function address()
    {
        return $this->morphOne(Address::class, 'addressable');
    }
}

Now we can store both the customer and warehouse addresses in the same table, and Eloquent will manage it all for us behind the scenes:

$customer->address  // Both of these now work.
$warehouse->address // Polymorphic relations FTW!

This was a quick crash course on polymorphic relations. Now onto the crux of this article.

The default Morph Type

In Eloquent parlance, the "morph type" is the value that is stored in the {morph}_type column (in our example, this is the addressable_type column).

By default, Eloquent will store the model's full class name as the morph type.

Using our example above, let's assume we have two addresses: one for a Customer with an ID of 34, and another for a Warehouse with an ID of 83. Looking at our addresses table, we should see something like this:

id addressable_id addressable_type street_number street_name
1 34 App\Customer 109 Old Mill Rd.
2 83 App\Warehouse 2 Willow Rd.

As we can see, the App\Customer and App\Warehouse values are stored directly in the database. This is how Eloquent knows which entity type a given address belongs to.

This works, but it tightly couples the database to our PHP application's internal structure. If we have other systems accessing this database, the value App\Customer has no meaning to them.

What we really want is a way for us to instruct Eloquent what value to use for each entity type in the addressable_type column.

Customizing the Morph Type

Using the morphMap method on Eloquent's Relation class, we can instruct Eloquent to use a custom name for each entity instead of the class name.

We're looking for a value that would make sense to another system accessing this database. Using the foreign entities' table names sounds like a good approach.

We'll add this piece of code in the AppServiceProvider's boot method:

use Illuminate\Database\Eloquent\Relations\Relation;

public function boot()
{
    Relation::morphMap([
        'customers' => 'App\Customer',
        'warehouses' => 'App\Warehouse',
    ]);
}

With this in place, our scenario above would generate the following rows in the database:

id addressable_id addressable_type street_number street_name
1 34 customers 109 Old Mill Rd.
2 83 warehouses 2 Willow Rd.

Our PHP class names are now nowhere to be found. We've successfully decoupled our database from our application code!

Bonus: Since using the foreign table names is such a common practice, you can actually omit the keys when setting the morph map, and Eloquent will automatically use the model's table name as the morph type:

Relation::morphMap([
    'App\Customer',
    'App\Warehouse',
]);

Why doesn't Eloquent do this automatically?

After realizing the benefits of decoupling the database from the application structure, the natural question arises: why doesn't Eloquent default to using the model's table name for the morph type?

The answer to this lies in the inverse relationship.

So far, we've only looked at the code in our Customer and Warehouse models, which allows us to get at their address with $customer->address or $warehouse->address. But what about the other way around? What if we have an $address, and we want to get the related model?

Let's add the MorphTo relationship to our Address class:

class Address
{
    public function addressable()
    {
        return $this->morphTo(); // Yes. No arguments :)
    }
}

With the relationship in place, we can now access the related model via the addressable attribute on any address:

Address::first()->addressable; // A Customer or a Warehouse

Pretty neat!

Now let's think back to our original question: why doesn't Eloquent default to using the model's table name for the morph type?

The answer is that it's simply impossible. If Eloquent would've always used the table name by default, the following code would indeed properly save the addressable_type as customers:

Customer::first()->address()->create([...]);

...but if we later have an Address instance and want to know which entity it belongs to, how would a customers value in the addressable_type column help us? There's nothing that connects the App\Customer class to the customers morph type.

Since PHP classes are lazy loaded on demand, there's no outstanding list of all classes in the app. There's no way for Eloquent to know on its own where all of your models are located, or what their class names are.

That's why we need to register our models with Relation::morphMap(), so that we can always know which entity a given morph type is associated with.

In closing

To fully decouple your database from your app's structure, always register all of your models with Relation::morphMap(), and live happily ever after.

]]>
Getting the current user (or other session data) in a Laravel controller constructor https://josephsilber.com/posts/2017/01/23/getting-current-user-in-laravel-controller-constructor Mon, 23 Jan 2017 00:00:00 +0000 https://josephsilber.com/posts/2017/01/23/getting-current-user-in-laravel-controller-constructor Session data is not available directly in controller constructors. Let's see how we can work around that.

]]>
Getting the current user (or other session data) in a Laravel controller constructor

Let's discuss something many people have accidentally discovered: you cannot use session data directly in a controller's constructor.

This change was introduced in Laravel 5.3, when the middleware pipeline was rewired to make global scopes work with session data.

In this post we'll examine the problem in more detail, and look at various ways to solve it.

The Problem

There are many reasons people want session data in the controller's constructor. Let's look at 2 examples:

  1. Sharing the current user to all views:

    Instead of using auth()->user() in your views, it's sometimes nicer to just have a global $user variable available across all views. A quick way to achieve that is to add a constructor to the base controller and share it from there:

    public function __construct()
    {
        view()->share('user', auth()->user());
    }
  2. Setting a property on the controller that needs the session:

    If you need the same piece of information in all of your methods, you may opt to set it as a property on the controller:

    public function __construct()
    {
        $this->account = Account::find(session('account_id'));
    }

In both of these cases, the code in the constructor assumes that the session is primed and ready. But if we try to run the code, it'll fail. At this point, the request has not yet been routed and no session data is available.

Solution #1: The Authenticated event

For every request, Laravel will fire the Authenticated event when the user is authenticated. You can listen for this event in a service provider, and share the $user from there:

use Illuminate\Auth\Events\Authenticated;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->app['events']->listen(Authenticated::class, function ($e) {
            view()->share('user', $e->user);
        });
    }
}

Solution #2: Inline view composer

If you'd rather share the user from the base controller, you can use an inline view composer:

public function __construct()
{
    view()->composer('*', function ($view) {
        $view->with('user', auth()->user());
    });
}

Since the callback will only execute when the view is composed, you'll have access to the session.

Solution #3: Converting a property to a method

Let's go back to the second example in the problem section. Instead of finding the current account directly in the constructor, you can put its logic into its own method and only call it when you actually need it:

public function index()
{
    $account = $this->account();

    // use it as before...
}

protected function account()
{
    return Account::find(session('account_id'));
}

This is great for new projects, but can be a little tedious if you already have a big project that relies on the property being available across all controller methods. For such cases, you can use an inline middleware.

Solution #4: Inline middleware

Inline middleware are a little more involved than the other solutions, but are probably the most flexible.

You may have seen $this->middleware('auth') before, which registers an existing middleware directly from within the controller constructor. What you may not know is that you can actually use a closure to define the middleware on the fly.

Using an inline middleware, we can hook into the request's routing, so that we have full access to the session:

public function __construct()
{
    $this->middleware(function ($request, $next) {
        $this->account = Account::find(session('account_id'));

        return $next($request);
    });
}

We can also use an inline middleware to share the $user to all views:

public function __construct()
{
    $this->middleware(function ($request, $next) {
        view()->share('user', $request->user());

        return $next($request);
    });
}

As you can see, this is a little more complex than the other solutions, but it gives us the most flexibilty.


These 4 solutions should cover all your session needs. If there's any controller session problem that's not solved by these, or if you have any better solutions, do let me know!

]]>
Gate and authorization improvements in Laravel 5.3 https://josephsilber.com/posts/2016/08/03/authorization-improvements-in-laravel-5-3 Wed, 03 Aug 2016 00:00:00 +0000 https://josephsilber.com/posts/2016/08/03/authorization-improvements-in-laravel-5-3 With Laravel 5.2 we got a nice authorization system right out of the box. In 5.3 this got a lot of improvements and refinements. Let's take a look.

]]>
Gate and authorization improvements in Laravel 5.3

After looking at the improvements coming to authentication, let's now take a look at what's in store for authorization in Laravel 5.3.

Note: some of the features discussed below have originaly been introduced in the middle of the 5.2 cycle. However, all of these features have been tweaked and refined for 5.3, so we'll cover them here.

Laravel authorization primer

If you're already familiar with authorization in Laravel, you can skip right ahead.

Laravel's authorization is built around 2 main concepts:

  1. A Gate class. The gate is the official authority on who has what ability (i.e. who can do what). You use the gate to register a user's abilities, and later check the gate if the user can perform a given action.

  2. Policy classes. Policies are responsible for checking abilities on a single model type. For each model class in your system you want to authorize against, you'll have a matching policy class.

To see how all this works together, let's generate a policy class for a theoretical Task model, by running php artisan make:policy TaskPolicy. Once created, we'll add a simple update method that checks if the user can update the given task:

namespace App\Policies;

use App\Task;
use App\User;

class TaskPolicy
{
    public function update(User $user, Task $task)
    {
        return $user->id === $task->user_id;
    }
}

Next we should register this policy with the gate, by adding it to the AuthServiceProvider's policy mappings:

protected $policies = [
    \App\Task::class => \App\Policies\TaskPolicy::class,
];

Behind the scenes, this will register the policy with the Gate. From now on, any authorization check against a Task will be routed to the TaskPolicy.

To demonstare, let's create a simple route to update a given task:

Route::put('tasks/{task}', function (App\Task $task) {
   abort_unless(Gate::allows('update', $task), 403);

   $task->update(request()->input());
});

We check the gate if the user can update the given task. The gate will call the update method on our policy, passing it both the currently authenticated user as well as the given task (if the user is not logged in, the gate will automatically deny all ability inquiries). If the ability is not granted, we abort with a 403.

(By the way, don't you just love that abort_unless function? I mean, just look at that! It reads like a full sentence: "abort unless the gate allows updating the task". This is Laravel at its best.)

Easily authorizing requests in controllers

Controllers in Laravel use the AuthorizesRequests trait to easily authorize any given request. You can call the authorize method from within a controller and it'll check for the ability at the gate. If the ability is denied, the request will automatically be aborted with a 403 response.

To see this in action, let's move our route to a controller:

use App\Task;

class TaskController
{
    public function update(Task $task)
    {
        $this->authorize('update', $task);

        $task->update(request()->input());
    }
}

Just like before, the gate will consult our TaskPolicy's update method and return its result.

Automatic ability names

Laravel actually goes a tiny step further, and allows you to omit the ability name when calling the authorize method. When the ability name is not provided, Laravel will automatically pick up the ability name from the controller's method.

In our sample controller, calling $this->authorize($task) would still end up consulting TaskPolicy@update for the check. Pretty nifty!

In Laravel 5.2, the automatic ability name is always the name of the controller method that calls it. Given the following controller:

use App\Task;

class TaskController
{
    public function show(Task $task)
    {
        $this->authorize($task);

        return view('tasks.show')->with('task', $task);
    }

    public function create()
    {
        $this->authorize(Task::class);

        return view('tasks.create');
    }
}

...you would have a policy with methods whose names match up with the names of the controller's methods:

use App\Task;
use App\User;

class TaskPolicy
{
    public function show(User $user, Task $task)
    {
        // check if the user can view the given $task
    }

    public function create(User $user)
    {
        // check if the user can create new tasks
    }
}

The problem with matching ability names to method names

Update August 8, 2016: after publishing this article, Taylor made some additional changes to authorization. The following has been updated to reflect these changes.

On the surface this seems quite intuitive, but if you looks closely you'll find some cracks in this convention.

Consider these 2 samples of where this convention breaks down:

  1. Checking abilities outside of a controller. The controller's method name is not always the most intuitive way to check an ability. Let's examine this template, where we loop through and check each task:

    @foreach ($tasks as $task)
        @if ($user->can('show', $task))
            <a href="{{ url('tasks', $task) }}">View task</a>
        @endif
    @endforeach

    Hmmm... That doesn't look quite right. What does $user->can('show', $task) check for? Does it check if the user can show tasks? What does that even mean? We want to know if they can view it, not show it!?

  2. Duplicate policy methods. The check for whether we should show the form to create a new task is the very same check that determines whether we allow the user to subsequently store that task. Naively mapping the controller's method names leads to the policy having two identical methods:

    use App\User;
    
    class TaskPolicy
    {
        public function create(User $user)
        {
            // check if the user can create new tasks
        }
    
        public function store(User $user)
        {
            // check if the user can create new tasks
        }
    }

    These two run the very same check, but we need them both for the two different methods on the controller. The same duplication applies to edit and update.

Normalizing ability names

In Laravel 5.3, the automatic ability names are getting an upgrade. Instead of blindly using the method name, Laravel will pick a sensible ability name based on the action you're trying to complete. If you're trying to view an item, it'll use the view ability name. If you're trying to create a new record, it'll use the create ability name, even if you call it from the controller's store method.

Laravel does this via a simple mapping between the methods and abilities. Here's the complete map straight from the source:

[
    'show' => 'view',
    'create' => 'create',
    'store' => 'create',
    'edit' => 'update',
    'update' => 'update',
    'destroy' => 'delete',
]

This happens automatically, with no change to the code in your controller. You can now check if $user->can('view', $task) in your views, which is much more intuitive.

Now that the controller doesn't always call the gate with ability names matching the controller methods, the policies should no longer have those methods. Given this controller:

use App\Task;

class TaskController
{
    public function create()
    {
        $this->authorize(Task::class);

        return view('tasks.create');
    }

    public function store()
    {
        $this->authorize(Task::class);

        Task::create(request()->input());
    }
}

...you would have a policy with just a create method:

use App\User;

class TaskPolicy
{
    public function create(User $user)
    {
        // check if the user can create tasks
    }
}

This create method wil be called from both create and store in the controller. Much cleaner!

Full policy generator

To help out with these changes, the policy generator is getting a nice boost. If you pass a model name to the generator it'll fully stub out the necessary policy methods to cover all default resource controller methods:

php artisan make:policy TaskPolicy --model=Task

You can see the raw stub in Laravel's source code, which should give you a better picture about the methods being generated.

The can middleware

While authorizing abilities inline works well, sometimes it makes more sense to do it at the route level - such as when the same ability applies to a whole group of routes. Laravel ships with a can middleware just for this purpose.

Let's define a general ability named access-dashboard:

Gate::define('access-dashboard', function ($user) {
    return $user->isAdmin();
});

To guard a whole group of routes against unauthorized access, we can wrap them in a group with the can middleware:

Route::group(['middleware' => 'can:access-dashboard'], function () {
    // define all dashboard routes...
});

Now the gate will be checked for the access-dashboard ability before running any routes in this group.

You can also pass a second argument to the middleware. This can be either the model's class name, or - to check for a given model instance - the model's route parameter name:

Route::get('tasks/{task}', [
   'uses' => 'TaskController@show',
   'middleware' => 'can:view,task',
]);

Route::post('tasks', [
   'uses' => 'TaskController@store',
   'middleware' => 'can:create,App\Task',
]);

When passing a route parameter name, the middleware will pull the model instance from the route itself. Refer to Laravel's documentation for more on route model binding.

Authorizing a full resource controller

Another neat gem in the authorization system is the authorizeResource method now available in controllers. This has the potential to considerably clean up your controllers. So what does this magical authorizeResource do?

In short, it replaces all your calls to authorize in the various controller methods with a single call in the constructor:

use App\Task;

class TaskController
{
    public function __construct()
    {
        $this->authorizeResource(Task::class);
    }

    // ... all resource methods ...
}

That's all it takes! You can now skip all calls to authorize in the individual methods. Laravel will now automagically check the right abilities for any given action. It's pretty magical actually.

Managing roles & permissions in the database with Bouncer.

Laravel's built-in gate works by hard-coding all authorization logic in your app. If you want to manage your users' roles and permissions via the database, check out Bouncer, which integrates effortlessly with Laravel's built-in authorization system.

]]>
The new Closure::fromCallable() in PHP 7.1 https://josephsilber.com/posts/2016/07/13/closure-from-callable-in-php-7-1 Wed, 13 Jul 2016 00:00:00 +0000 https://josephsilber.com/posts/2016/07/13/closure-from-callable-in-php-7-1 Let's take a look at a neat little feature coming in PHP 7.1: easily converting any callable into a proper Closure using the new Closure::fromCallable() method.

]]>
The new Closure::fromCallable() in PHP 7.1

With PHP 5.5 going EOL earlier this week and the PHP 7.1 beta expected later this month, now sounds like a good time to look into a neat little feature coming in 7.1: easily converting any callable into a proper Closure using the new Closure::fromCallable() method.

A short refresher on closures in PHP

If you're already familiar with closures in PHP, you can skip right ahead.

PHP has had closures since version 5.3. Think of it as similar to an anonymous function in Javascript; they're usually passed off to another method, which either calls it directly or stores it for later use.

As an example, let's look at Laravel's collection class. It has a reject method, which you can use to filter out values you don't want in the collection:

$ages = collect([3, 7.5, 12, 19.5, 23]);

$adults = $ages->reject(function ($age) {
    return $age < 18;
});

Behind the scenes, the reject method will call our closure callback for every item in the collection. If the age is less than 18, it will be rejected from our collection.

When you need to convert a callable to a Closure

Now imagine you want to reject every value in the collection that is not a whole number. Basically, reject all floats. There's a built-in is_float function, so why don't we pass that in directly?

$ages = collect([3, 7.5, 12, 19.5, 23]);

$integers = $ages->reject('is_float');

Unexpectedly, this will not work. Why? In addition to a closure, Laravel also allows you to pass in a single value to reject, which will remove that value from the collection. Since we've passed it a string, Laravel thinks we want to remove the 'is_float' item from the collection. Whoops!

The way around this is to actually pass in a closure and call is_float from within our closure callback:

$ages = collect([3, 7.5, 12, 19.5, 23]);

$integers = $ages->reject(function ($age) {
    return is_float($age);
});

Introducing Closure::fromCallable()

In PHP 7.1, the Closure class has a new static fromCallable method, which automatically creates a Closure from any callbable you pass it. Using this new method, we can clean up our code a bit:

$ages = collect([3, 7.5, 12, 19.5, 23]);

$integers = $ages->reject(Closure::fromCallable('is_float'));

Pretty neat!

Creating a closure that wraps a private method

Another scenario where you'd want a closure is if you want to pass in an array-callable of a private method. As mentioned in the PHP docs, you can create a callable to a method on an object by using an array with this format: [$object, 'method']. This can be quite convenient when using a method on your class that has the logic for the callback:

$due = $orders->filter([$this, 'isDue']);

This works well, with the only caveat being that the isDue method now has to be made public - otherwise the collection class won't be able to access it.

This is where Closure::fromCallable() once again comes to the rescue. When used from within a class, the resulting closure will automatically be bound to that class, allowing it to access private methods:

$due = $orders->filter(Closure::fromCallable([$this, 'isDue']));

The isDue method can now stay private or protected, but the collection will still be able to access it through the wrapping closure!

None of this is really earth shattering, but it's a neat little addition that can come in handy every so often.

]]>
Improvements to authentication in Laravel 5.3 https://josephsilber.com/posts/2016/07/10/authentication-improvements-in-laravel-5-3 Sun, 10 Jul 2016 00:00:00 +0000 https://josephsilber.com/posts/2016/07/10/authentication-improvements-in-laravel-5-3 Authentication has gotten some nice improvements in 5.3, so let's examine it piece by piece. We'll start with the current state of affairs, then take it from there.

]]>
Improvements to authentication in Laravel 5.3

Authentication has gotten some nice improvements in 5.3, so let's examine it piece by piece. We'll start with the current state of affairs, then take it from there.

Authentication in Laravel 5.2

In Laravel 5.2, you generally use one of the following methods to make sure a user is authenticated:

  1. The auth middleware. Using this middleware on a route, you can restrict access to logged-in users. It will redirect unauthenticated requests (or, for an AJAX request, respond with a 401).

  2. The guard's check method. Using auth()->check() in your code, you can check if the user is logged in, and branch your logic from there.

This is all nice and dandy, but it can potentially lead to a lot of duplicated code.

If you need some custom logic for an unauthenticated user, using auth()->check() works perfectly. However, most of the time you simply want to redirect an unauthenticated user (or return with a 401 if it's an AJAX request). The logic for this currently lives in the auth middleware, so there's no way to reuse it. Utilizing auth()->check() in your code means you have to replicate this logic all over again:

public function view(Request $request)
{
    if (! auth()->check()) {
        if (($request->ajax() && ! $request->pjax()) || $request->wantsJson()) {
            return response()->json(['error' => 'Unauthenticated.'], 401);
        } else {
            return redirect()->guest('login');
        }
    }

    // The user is logged in. Proceed.
}

Clearly this is a lot of extra code that we shouldn't need here. Ideally, there should be a single global location for handling unauthenticated responses. Lucky for us, Laravel 5.3 makes this a breeze!

Introducing the authenticate method

In Laravel 5.3, there's now a better way: the authenticate method. Calling authenticate will throw an exception if the user is not logged in, so that you don't even need a conditional in your code:

public function view()
{
    $user = auth()->authenticate();

    // The user is logged in. Proceed.
}

If the user isn't logged in, it'll throw an AuthenticationException. There's no need for you to catch it - Laravel gracefully handles this for you in a single global location.

The exception handler's unauthenticated method

The global exception handler catches all authentication exceptions and calls its unauthenticated method. It'll redirect the request to the login page, or, for an AJAX request, return with a 401 error status code.

You can customize this method to your liking, giving you full flexibility on how to handle unauthenticated requests in a single centralized location.

The Authenticate middleware

In Laravel 5.2, the Authenticate middleware ships in the app skeleton. This middleware is responsible for creating a valid response for an unauthenticated request. To customize its functionality, you edit the handle method directly in the middleware class.

In Laravel 5.3, the auth middleware has been pulled into the core, allowing us to properly cover it in our tests. Since the middleware does not ship with the app skeleton anymore, you cannot customize it directly. Instead, the auth middleware now throws an AuthenticationException when the user is not logged in, allowing the global exception handler to catch it. As mentioned before, you can customize the response there.

Authenticating against multiple guards

Another neat addition to the auth middleware is authentication against multiple guards.

Imagine you're using both the web & api guards throughout your app. You may have some routes that you want to be accessible if the user is logged in with either one of those guards. The updated middleware makes this a breeze:

Route::get('users', [
    'uses' => 'UsersController@index',
    'middleware' => 'auth:web,api',
]);

The auth middleware will check each guard. If the user is logged in to any of the guards, the request will be authenticated. Additionally, that guard will be set as the default, so that any calls to auth()->user() throughout your app will properly return the user.

Route model binding and global scopes

The final enhancement to authentication we'll cover here is with regards to route model binding. Route model binding is one of my favorite features in Laravel, removing boilerplate all over your controllers. For the uninitiated, here's a short primer from the docs:

Route model binding provides a convenient way to inject model instances into your routes. For example, instead of injecting a user's ID, you can inject the entire User model instance that matches the given ID.

As people started using route model binding more and more (especially since 5.2 introduced implicit model binding), they started running into the following issue: if you have a global scope on your model that tries to access the current user, well... you can't. That's because route model binding runs before authentication, so when the model resolves the current user has not yet been authenticated.

Here's some code to explain this issue with a little more detail:

// routes.php
Route::get('invoices/{invoice}', [
    'uses' => 'InvoicesController@show',
    'middleware' => 'auth',
]);

// InvoicesController.php
public function show(Invoice $invoice)
{
    // show the invoice
}

// Invoice.php
class Invoice extends Model
{
    protected static function boot()
    {
        static::addGlobalScope(function ($query) {
            $query->where('user_id', auth()->id());
        });
    }
}

We're restricting all queries on the invoice model to just the ones that belong to the current user (this is obviously a bit contrived to keep things simple. For a real-world example, check out Teamwork's excellent UsedByTeams trait).

The problem, as mentioned above, is that auth()->id() will return null when the model is being resolved through the route's model binding, since it runs before the auth middleware.

In Laravel 5.2 there was really no way around this issue. The only remedy was to forgo route model binding and fetch the model yourself:

// InvoicesController.php
public function show($id)
{
    $invoice = Invoice::findOrFail($id);

    // show the invoice
}

Not the end of the world, I know, but once you've been spoiled by route model binding there's really no going back. Laravel's neat little features tend to have that effect on you...

If you've ever run into this issue before, you'll be happy to hear that in Laravel 5.3 this problem is no more. The middleware stack has been rewired so that authentication will now always run before route model binding and you can now safely get the authenticated user in your global scopes. Hallelujah!

Note: as part of this change, session data is no longer available directly in a controller constructor. If you ever run into that, here are some workarounds.

Bonus: the request's expectsJson method

While not directly tied to authentication, I thought it'd be nice to include this little tidbit in here.

As you can see in the first code snippet in this post, determining whether the request needs JSON can be quite tedious. If it's an AJAX request it probably wants JSON, unless it's actually a PJAX request. If it's a regular HTTP request, there may be an Accept header specifying that the response should be JSON.

To make all this easier, there's a new expectsJson method on the request. You can use that directly to determine whether to repond with JSON or not:

public function view(Invoice $invoice)
{
    if (request()->expectsJson()) {
        return $invoice;
    } else {
        return view('invoices.show', compact('invoice'));
    }
}

Just a little tip if you ever have a need for this.

]]>