用户模型中带有 uuid 列的 Laravel Sanctum 不保存 tokenable_id

我尝试使用 Laravel 8.xLaravel sanctum 2.14.2 来验证我的 API 和 UUID 作为我的 User 模型的主键。

我的自定义 PersonalAccessToken 模型

use IlluminateDatabaseEloquentFactoriesHasFactory;
use LaravelSanctumPersonalAccessToken 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");
    }
}

我的 personal_access_tokens 迁移模式

...
    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();
        });
    }
...

我的 AppServiceProvider

...
use AppModelsPersonalAccessToken;
use IlluminateSupportFacadesURL;
use IlluminateSupportServiceProvider;
use LaravelSanctumSanctum;

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);
    }
}

当我尝试使用 $user->createToken($user->email)->plainTextToken 获取令牌时,我收到此错误:

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, ["*"], ?, AppModelsUser, 2022-03-20 19:16:43, 2022-03-20 19:16:43))

我认为错误的原因是我使用 uuid 作为用户表中的主键

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

更新

我的 User 模型

...
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");
    }

    ...
}

任何帮助,将不胜感激。

stack overflow Laravel Sanctum with uuid column in User model doesn't save tokenable_id
原文答案
author avatar

接受的答案

抱歉回复晚了。我为遇到与上述相同问题的任何人提供解决方案。问题出在我的 UUId Traits 中。当我们想要创建自己的 Traits 时,我们应该按照 Laravel 的建议使用 boot magic method

解决方案:

使用正确的代码使用 AppTraitsHasUUID

<?php

namespace AppTraits;

use IlluminateDatabaseEloquentModel;
use IlluminateSupportStr;

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';
    }
}

最后,在 AppTraitsHasUUID 模型中添加 User

...
use AppTraitsHasUUID;
...
class User extends Authenticatable
{
    use HasUUID;
    ...
}

无需自定义 Sanctum's Model 。非常感谢@Hussain,@Dharman


答案:

作者头像

您创建自定义 PersonalAccessToken 模型有什么特别的原因吗?

如果您希望 User 模型的主键只是 UUID,您可以在不创建自定义 PersonalAccessToken 模型的情况下实现它。

您的 personal_access_tokens 迁移模式似乎很好。

我认为错误的原因是我在users表中使用uuid作为主键

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

这可能是问题所在。尝试将列名从 uuid 更改为 id 并查看它是否有效

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

如果您必须使用列名作为主键的 uuid,请尝试将以下内容添加到您的 User 模型中

protected $primaryKey='uuid'

默认情况下,eloquent 假定主键列的名称为 'id' 。这将使 eloquent 知道寻找 'uuid' 作为用户模型的主键列。

此外,由于您没有使用默认整数数据类型作为主键,请确保您的 User 模型中有以下内容

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

您可以参考 Laravel 文档中的 Primary Keys