JaggyGauran

Freelance developer, and designer

Eloquent Scope Behaviour

I just noticed weird behavioral differences when using scopes in Laravel.

Using scopes, I usually tend to use endpoint functions (get, first [not sure what they're actually called though]) when the values are unique or the scope needs not any more parameters.

The case I'm most curious about is using first inside and outside the eloquent scope.

Preface

The nitpicky-ness of this post is purely out of curiosity. Trivial things like these always make me wonder how I'll end up doing things when I'm developing an API.

Also, I encountered this thought just because I tried refactoring out the endpoints out of the scope and some of my tests broke. Just a note to myself for the future. :D

Scenario

Let's say we have a user that has a unique activation token and we scope it out.

Inside the Scope


// User.php

public function scopeByActivationToken(Builder $query, string $token)
{
    return $query->where('activation_token', $token)
                 ->first();
}

// UserActivationController.php

public function show ()
{
    $token = 'non-existent-activation-token';
    $user = User::byActivationToken($token); // returns an App\User object

    if ($user->exists()) { // returns false
    } 
}

From this case, it seems that using first() from the scope returns the App\User object itself so the exists() method is accessible for validation.

On the other hand,

Outside the Scope


// User.php

public function scopeByActivationToken(Builder $query, string $token)
{
    return $query->where('activation_token', $token);
}

// UserActivationController.php

public function show ()
{
    $token = 'non-existent-activation-token';
    $user = User::byActivationToken($token)->first(); // Returns null

    if ($user->exists()) { // throws a method does not exist on `null`
    } 

    // We'll have to use this instead.
    if (! $user) {
    } 
}

Thoughts

As of the time of writing, I'll either do validation through Request Objects (function show(Request $request)) or use endpoints inside the scope.

It's things like these that are actually the most educational and thought provoking personally in a developer's perspective.

Hopefully I'm not doing anything too wrong.