phpauthenticationlaravel

How do i use conditional parameters in Laravel Auth::attempt?


Using Laravel 4.1.30 I got this following code which test a sign-in attempt via Auth.

//... more codes here ...

$auth = Auth::attempt(array(
    'email'     => Input::get('email'),
    'password'  => Input::get('password'),
    'active'    => 1
), $remember);

if ($auth) {
    //... more codes here ...
}

I like to implement a conditional value such as:

->active > 0

I am using the active (field) as a level of authentication for users signing in. Anything above 0 (zero) should satisfy the next condition.

How can it be done in one statement?


Solution

  • tl;dr

    You can't do this within the array passed to Auth::attempt(), because in the framework it is hard coded to use equality comparison in the generated query.

    Full review

    Framework implementation

    The attempt() function is implemented in Illuminate/Auth/Guard.php.

    public function attempt(array $credentials = array(), $remember = false, $login = true)
    {
        $this->fireAttemptEvent($credentials, $remember, $login);
    
        $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
    
        // If an implementation of UserInterface was returned, we'll ask the provider
        // to validate the user against the given credentials, and if they are in
        // fact valid we'll log the users into the application and return true.
        if ($this->hasValidCredentials($user, $credentials))
        {
            if ($login) $this->login($user, $remember);
    
            return true;
        }
    
        return false;
    }
    

    Here you can see a call for $this->provider->retrieveByCredentials($credentials);. The retrieveByCredentials() function is implemented in Illuminate/Auth/DatabaseUserProvider.php.

    public function retrieveByCredentials(array $credentials)
    {
        // First we will add each credential element to the query as a where clause.
        // Then we can execute the query and, if we found a user, return it in a
        // generic "user" object that will be utilized by the Guard instances.
        $query = $this->conn->table($this->table);
    
        foreach ($credentials as $key => $value)
        {
            if ( ! str_contains($key, 'password'))
            {
                $query->where($key, $value);
            }
        }
    
        // Now we are ready to execute the query to see if we have an user matching
        // the given credentials. If not, we will just return nulls and indicate
        // that there are no matching users for these given credential arrays.
        $user = $query->first();
    
        if ( ! is_null($user))
        {
            return new GenericUser((array) $user);
        }
    }
    

    Here you can see that the array you pass to Auth::attempt() is processed in a foreach and every key-value pairs are added as a WHERE clause to the query. Because it done with a $query->where($key, $value); call, it is limited to equality comparison.

    Possible solutions

    A workaround would be to change this line to something like:

    $query->where($key, $value['operator'], $value['value']);
    

    Then you could restructure the array given to Auth::attempt().

    $auth = Auth::attempt(array(
        'email' => array(
            'value'    => Input::get('email'),
            'operator' => '='
        ),
        'password' => array(
            'value'    => Input::get('password'),
            'operator' => '='
        ),
        'active' => array(
            'value'    => 0,
            'operator' => '>'
        )
    ), $remember);
    

    The problem with this is that you have to override every other function that uses this array, so you end up with a custom solution. With this effort you could write your own authentication query or do a check on active after Auth::attempt().