简体   繁体   中英

Laravel Sanctum with uuid column in User model doesn't save tokenable_id

I'm try to use Laravel 8.x and Laravel sanctum 2.14.2 to authenticate my API and UUIDs as the primary key for my User model.

My custom PersonalAccessToken model

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;

class PersonalAccessToken extends SanctumPersonalAccessToken
{
    use HasFactory;

    protected $table = 'personal_access_tokens';

    public function tokenable()
    {
        return $this->morphTo('tokenable', "tokenable_type", "tokenable_id", "uuid");
    }
}

My personal_access_tokens migration schema

...
    public function up()
    {
        Schema::dropIfExists('personal_access_tokens');

        Schema::create('personal_access_tokens', function (Blueprint $table) {
            $table->id();
            $table->uuidMorphs('tokenable');
            $table->string('name');
            $table->string('token', 64)->unique();
            $table->text('abilities')->nullable();
            $table->timestamp('last_used_at')->nullable();
            $table->timestamps();
        });
    }
...

My AppServiceProvider

...
use App\Models\PersonalAccessToken;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
use Laravel\Sanctum\Sanctum;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        Sanctum::ignoreMigrations();
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        if($this->app->environment('production')) {
            URL::forceScheme('https');
        }

        Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
    }
}

When I try to get the token with $user->createToken($user->email)->plainTextToken , I get this error:

SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'tokenable_id' cannot be null (SQL: insert into `personal_access_tokens` (`name`, `token`, `abilities`, `tokenable_id`, `tokenable_type`, `updated_at`, `created_at`) values (admin@gmail.com, 85dbe44c32a999a01f4a97d9c9eab0710125a6ac5f861ab546a5822f61015b23, [\"*\"], ?, App\\Models\\User, 2022-03-20 19:16:43, 2022-03-20 19:16:43))

I think the cause of the error is that I am using uuid as the primary key in the users table

Schema::create('users', function (Blueprint $table) {
      $table->uuid('uuid')->primary();
      ...
});

UPDATE

My User Model

...
class User extends Authenticatable
{
    use HasUUID;
    use HasApiTokens;
    use HasFactory;
    use Notifiable;
    use HasRoles;

    ...

    public function tokens()
    {
        return $this->morphMany(Sanctum::$personalAccessTokenModel, 'tokenable', "tokenable_type", "tokenable_id");
    }
    
    ...
}

Any help would be appreciated.

Is there any particular reason for you to create custom PersonalAccessToken model?

If it's just UUID that you want for the primary key of your User model, you can achieve it without creating the custom PersonalAccessToken model.

Your personal_access_tokens migration schema seems fine.

I think the cause of the error is that I am using uuid as the primary key in the users table

Schema::create('users', function (Blueprint $table) { $table->uuid('uuid')->primary(); ... });

This could be the issue. Try changing the column name to just id from uuid and see if it works

$table->uuid('id')->primary();

If you must use the column name as uuid for primary key, then try adding the following to your User model

protected $primaryKey='uuid'

By default eloquent assumes the name of the primary key column as 'id' . This will let eloquent know to look for 'uuid' as primary key column for User model.

Also since you are not using the default integer data type for primary key make sure you have the following in your User model

public $incrementing=false
protected $keyType='string'

You can refer to Laravel Documentation for Primary Keys

Sorry for late reply. I answer with the solution for anyone who is having the same problem as above. The problem is in my UUId Traits . We should use boot magic method as Laravel suggested when we want to create our own Traits.

Solution:

Using App\Traits\HasUUID with the correct code

<?php

namespace App\Traits;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;

trait HasUUID
{
    /**
     * Boot functions from Laravel.
     */
    // protected static function boot() <- This line is INCORRECT
    protected static function bootHasUUID()
    {
        static::creating(function (Model $model) {
            $model->primaryKey = 'uuid';
            $model->keyType = 'string'; // In Laravel 6.0+ make sure to also set $keyType
            $model->incrementing = false;

            if (empty($model->{$model->getKeyName()})) {
                $model->{$model->getKeyName()} = Str::uuid()->toString();
            }
        });
    }

    /**
     * Get the value indicating whether the IDs are incrementing.
     *
     * @return bool
     */
    public function getIncrementing()
    {
        return false;
    }

    /**
     * Get the auto-incrementing key type.
     *
     * @return string
     */
    public function getKeyType()
    {
        return 'string';
    }
}

And finally, add the App\Traits\HasUUID in User Model.

...
use App\Traits\HasUUID;
...
class User extends Authenticatable
{
    use HasUUID;
    ...
}

No need to customize Sanctum's Model . Thank you so much @Hussain, @Dharman

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM