简体   繁体   中英

How can I correctly implement UUIDs in Laravel with spatie/laravel-permission and webpatser/laravel-uuid?

I have a Laravel 5.7 application using spatie/laravel-permission and normal IDs for models. I want to transition to using UUIDs and I've taken the steps mentioned in webpatser/laravel-uuid Readme file. Everything works with my own models, eg with User, Model A, Model B etc. and the relations seem fine, but I cannot seem to make the uuid's work with Spatie's permissions.

When I want to assign a role (and associated permissions) to an object, I get the following error. This happens when I try to register a User and assign him a Role.

正如您在此图像中看到的,来自 Spatie 的 role_id 作为整数传输到此查询中。现在是342,其他情况类似705293

As you can see in this image, the role_id from Spatie is transmitted into this query as an integer. Now it is 342, in other cases it is similar to 705293

I have defined a Uuids.php trait in my /app folder accordingly and added for all the models, including Permission.php and Role.php the following code:

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

use Uuids;

protected $casts = [
    'id' => 'string',
];

I know that this works with any other model and relation, but just not with Spatie's permissions and it seems that the role_id is converted differently in the internal functions (like assignRole('') from a 36chars string to something else. If I query the Roles or Permissions I get the correct string id.

Anything that I might be missing or does anyone knows a fix for this?

Later edit: this is my original migration for Spatie:

    <?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePermissionTables extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        $tableNames = config('permission.table_names');
        $columnNames = config('permission.column_names');

        Schema::create($tableNames['permissions'], function (Blueprint $table) {
            $table->uuid('id');
            $table->primary('id');
            $table->string('name');
            $table->string('guard_name');
            $table->timestamps();
        });

        Schema::create($tableNames['roles'], function (Blueprint $table) {
            $table->uuid('id');
            $table->primary('id');
            $table->string('name');
            $table->string('guard_name');
            $table->timestamps();
        });

        Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames) {
            $table->uuid('permission_id');
            $table->string('model_type');
            $table->uuid($columnNames['model_morph_key']);
            $table->index([$columnNames['model_morph_key'], 'model_type', ]);

            $table->foreign('permission_id')
                ->references('id')
                ->on($tableNames['permissions'])
                ->onDelete('cascade');

            $table->primary(
                ['permission_id', $columnNames['model_morph_key'], 'model_type'],
                    'model_has_permissions_permission_model_type_primary'
            );
        });

        Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames) {
            $table->uuid('role_id');

            $table->string('model_type');
            $table->uuid($columnNames['model_morph_key']);
            $table->index([$columnNames['model_morph_key'], 'model_type', ]);

            $table->foreign('role_id')
                ->references('id')
                ->on($tableNames['roles'])
                ->onDelete('cascade');

            $table->primary(
                ['role_id', $columnNames['model_morph_key'], 'model_type'],
                    'model_has_roles_role_model_type_primary'
            );
        });

        Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames) {
            $table->uuid('permission_id');
            $table->uuid('role_id');

            $table->foreign('permission_id')
                ->references('id')
                ->on($tableNames['permissions'])
                ->onDelete('cascade');

            $table->foreign('role_id')
                ->references('id')
                ->on($tableNames['roles'])
                ->onDelete('cascade');

            $table->primary(['permission_id', 'role_id']);

            app('cache')->forget('spatie.permission.cache');
        });
    }

    //Reverse the migrations.
    public function down()
    {
        $tableNames = config('permission.table_names');

        Schema::drop($tableNames['role_has_permissions']);
        Schema::drop($tableNames['model_has_roles']);
        Schema::drop($tableNames['model_has_permissions']);
        Schema::drop($tableNames['roles']);
        Schema::drop($tableNames['permissions']);
    }
}

This is an example of how the Roles and permissions are stored (working)

在此处输入图像描述

Same is for Permissions. So their _id is correct. The issue is somewhere in Laravel or in Spatie that it sends another value to the DB when trying to add a Role to a Model.

Updating the tables in the package is not recommended. So create a trait in the app folder:

<?php

namespace YourNamespace;

use Illuminate\Support\Str;

trait Uuids
{
    /**
    * Boot function from Laravel
    */
    protected static function boot()
    {
        parent::boot();

        static::creating(function ($model) {
            $model->incrementing = false;
            $model->{$model->getKeyName()} = Str::uuid()->toString();
        });
    }
}

Create a migration to update your table structure:

php artisan migrate:make change_primary_key_type_in_roles_table --table=roles

Put the following in to the migration:

public function up()
{
    Schema::table('roles', function (Blueprint $table) {
        $table->uuid('id')->change();
    });
}

public function down()
{
    Schema::table('roles', function (Blueprint $table) {
        $table->increments('id')->change();
    });
}

Use Uuid trait in your model:

<?php

namespace YourNamespace;

use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    use Uuids;

    protected $guarded = [''];
    protected $casts = [
        'id' => 'string',
    ];

Then do composer dumpautoload and php artisan migrate

Also facing this issue. solved by changing Permission.php and Role.php with the following code:

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

use Uuids;

protected $casts = [
    'id' => 'uuid',
];

If you want all the role/permission objects to have a UUID instead of an integer, you will need to Extend the default Role and Permission models into your own namespace in order to set some specific properties.

namespace App\Models;

use App\Traits\Uuid;
use Illuminate\Database\Eloquent\Model;
use Spatie\Permission\Models\Role as SpatieRole;

class Role extends SpatieRole
{
    use Uuid;
    protected $primaryKey = 'id';

    /**
    * The attributes that should be cast to native types.
    *
    * @var array
    */
    protected $casts = [
        'id' => 'string',
    ];
}

then update config in permission.php change the model used

'permission' => Spatie\Permission\Models\Permission::class,
to
'permission' => App\Models\Permission::class,

'role' => Spatie\Permission\Models\Role::class,
to
'role' => App\Models\Role::class,

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