Laravel 8 Sanctum: Is it required to extend from Authenticatable? - laravel

On the one hand, I have an Eloquent model User that extends Illuminate\Database\Eloquent\Model. On the other hand, I have SanctumUser extends Authenticatable (https://laravel.com/docs/8.x/sanctum#issuing-api-tokens).
What I would like to do is, User extends Model, SanctumUser, but multiple inheritance is not possible in PHP 7.x.
I know that some traits are used in SanctumUser according to the documentation I've linked above. These traits are: use HasApiTokens, HasFactory, Notifiable;. Do you know if they are sufficient if I remove extends Authenticatable and replace it with extends Model (User would extend SanctumUser)?

Authenticable as alias for Illuminate\Foundation\Auth\User class has traits which has methods for authentication and authorization
<?php
namespace Illuminate\Foundation\Auth;
use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\MustVerifyEmail;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\Access\Authorizable;
class User extends Model implements
AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword, MustVerifyEmail;
}
Even the default User model class which is available with any new standard Laravel installation has it extending the Authenticable class [use Illuminate\Foundation\Auth\User as Authenticatable;]
Default User Model class in a standard Laravel application has two more traits - Notifiable and HasFactory (since Laravel 8.x)
<?php
namespace App\Models;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasFactory;
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password',
'remember_token',
'two_factor_recovery_codes',
'two_factor_secret',
];
}
With Sanctum you may add trait HasApiToken to the User model class.
In order to easily integrate Laravel Sanctum or Jetstream or Laravel UI or Breeze for authentication and authorization to your app, its better to make User class extend Authenticable - plug and play

Related

Argument 1 passed to Illuminate\Foundation\Testing\TestCase::actingAs() must implement interface Illuminate\Contracts\Auth\Authenticatable

Argument 1 passed to Illuminate\Foundation\Testing\TestCase::actingAs() must implement interface Illuminate\Contracts\Auth\Authenticatable, instance of Illuminate\Database\Eloquent\Collection given
I have also tried some of the following methods, but none of them worked:
Argument 1 passed to Illuminate\Foundation\Testing\TestCase::actingAs()
Problem with testing method with actingAs
I hope there is another way I can solve this problem.
This is my ClipartTest.php file:
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class ClipartTest extends TestCase
{
use RefreshDatabase;
/**
* A basic feature test example.
*
* #return void
*/
public function test_can_get_clipart()
{
$this->withoutExceptionHandling();
$user = User::factory(3)->create();
$this->actingAs($user, 'api');
$this->getJson('/api/cliparts')
->assertStatus(201)
;
}
This is my User.php file
<?php
namespace App\Models;
use App\Http\Traits\Uuid;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use Authenticatable, HasFactory, Notifiable, HasApiTokens;
use Uuid;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
'email',
'password',
];
This is my Clipart.php file:
<?php
namespace App\Models;
use App\Http\Traits\Uuid;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Clipart extends Model
{
use HasFactory;
use Uuid;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'user_id',
'name',
'image_id',
];
You are creating 3 users with the factory and therefore the $user will actually return a collection of 3 users.
Instead of User::factory(3)->create() do User::factory()->create() so that it would return a single user model.

How to get online users and their count?

I want to use this package in my Laravel 8 projects for retrieving online users. However, I am lost. I follow the documentation but I don't understand the steps. When I login through another browser, it keeps giving me 0 users. What did I do wrong?
User.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laratrust\Traits\LaratrustUserTrait;
use Shetabit\Visitor\Traits\Visitor;
class User extends Authenticatable
{
use Visitor;
use LaratrustUserTrait;
use HasFactory, Notifiable;
protected $guarded = [];
}
DashboardController.php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\User;
class DashboardController extends Controller
{
public function index(User $user)
{
$onlineUsers = $user->visits()->count();
return view('dashboard.index', compact('onlineUsers'));
}
}
visitor()->onlineVisitors(User::class); // returns collection of online users
User::online()->get(); // another way

Laravel's Passport: How to have an authenticatable model

The problem with the Laravel's Passport tutorial is that it assumes the reader will use the pre-installed User model, that is very different from the simple model we could create with php artisan make:model MyModel.
Here is the code of the pre-installed User model:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
}
And here is the code of a model you could create with php artisan make:model MyModel:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class MyModel extends Model
{
}
But what if I want my own custom authenticatable models, say Customer, what should I do to follow the Passport tutorial? Is there an Artisan command that implements all the interfaces, add all the traits and extend the corresponding class for us?
Thank you for your help.

Can not call Auth class inside Scope class in Laravel 5.2

I added External Scope for User Model. Simply created Scope, with the name DeveloperScope
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Auth;
class DeveloperScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
if(Auth::user()->id != 1){
$builder->where('id', '<>', 1);
}
}
}
Then, I called this scope for User model.
User Model
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Models\Scopes\DeveloperScope;
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
use SoftDeletes;
use Authenticatable, CanResetPassword;
protected $table = 'users';
protected $fillable = ['first_name', 'last_name', 'email', 'password'];
protected $hidden = ['password', 'remember_token'];
protected $dates = ['deleted_at'];
protected static function boot()
{
parent::boot();
static::addGlobalScope(new DeveloperScope);
}
}
It works well, when I don't use Auth::class inside DeveloperScope class. The reason is that, I just want to hide the main user for another users for all Eloquent methods. Of course, I can use session instead of Auth and retrieve user id. But it is still interesting for me, why browser gives an error: ERR_EMPTY_RESPONSE while using Auth::class ?
The method what you are looking for is exactly Auth::hasUser(). The method was added in Laravel 5.6 through my PR. (So you need to upgrade Laravel first)
[5.6] Add ability to determine if the current user is ALREADY authenticated without triggering side effects by mpyw · Pull Request #24518 · laravel/framework
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
class DeveloperScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
if (!Auth::hasUser() || Auth::user()->id != 1) {
$builder->where('id', '<>', 1);
}
}
}
Just call Auth::hasUser() to prevent Auth::user() from causing side effects.
Try injecting the Illuminate\Auth\Guard class and then call the user method on it. e.g.:
$this->auth->user();
You should be fine with just type-hinting it since Laravel will automatically new up an instance for you.
For more info check out the API: https://laravel.com/api/5.2/Illuminate/Auth/Guard.html

Class App\User contains 6 abstract methods and must therefore be declared abstract

I saw this error for the first time and don't really know what to do about it.
When I tried to register a new user on my website and when I clicked on submit button it shows:
FatalErrorException in User.php line 11:
Class App\User contains 6 abstract methods and must therefore be declared abstract or implement the remaining methods (Illuminate\Contracts\Auth\Authenticatable::getAuthIdentifierName, Illuminate\Contracts\Auth\Authenticatable::getAuthIdentifier, Illuminate\Contracts\Auth\Authenticatable::getAuthPassword, ...)
User Model:
<?php
namespace App;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
class User extends Model implements Authenticatable
{
protected $table = 'users';
protected $primaryKey = 'id';
}
What is it trying to say, I don't understand. Please can someone help me with this?
You are implementing Illuminate\Contracts\Auth\Authenticatable. This is interface and requires your User class to have some required methods:
public function getAuthIdentifierName();
public function getAuthIdentifier();
public function getAuthPassword();
public function getRememberToken();
public function setRememberToken($value);
public function getRememberTokenName();
If you are trying to make default User model, you should use Illuminate\Foundation\Auth\User as Authenticatable and extend it instead of Model class. There is no need to implement Authenticatable interfase.
You need to either extend Illuminate\Foundation\Auth\User instead of Illuminate\Database\Eloquent\Model, or use Illuminate\Auth\Authenticatable trait in your class
UPDATE
You need to extend Illuminate\Foundation\Auth\User like this
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
}
UPDATE 2
Also make sure you don't have native Laravel's App\User model in you app folder, named User.php

Resources