A traits, I fields

This commit is contained in:
a6a2f5842 2025-06-16 09:49:36 +02:00
parent e65ea42586
commit cf337a8bb6
14 changed files with 186 additions and 31 deletions

6
.gitattributes vendored Normal file
View File

@ -0,0 +1,6 @@
/.vscode export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/phpunit.xml.dist export-ignore
/docs export-ignore
/tests export-ignore

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"editor.formatOnSave": true,
"[php]": {
"editor.defaultFormatter": "bmewburn.vscode-intelephense-client"
},
}

View File

@ -32,6 +32,7 @@
}, },
"require": { "require": {
"php": "^8.0", "php": "^8.0",
"illuminated/helper-functions": "^8.12|^9.0|^10.0|^11.0|^12.0",
"illuminate/auth": "^8.12|^9.0|^10.0|^11.0|^12.0", "illuminate/auth": "^8.12|^9.0|^10.0|^11.0|^12.0",
"illuminate/container": "^8.12|^9.0|^10.0|^11.0|^12.0", "illuminate/container": "^8.12|^9.0|^10.0|^11.0|^12.0",
"illuminate/contracts": "^8.12|^9.0|^10.0|^11.0|^12.0", "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0|^12.0",
@ -39,21 +40,16 @@
"illuminate/support": "^8.12|^9.0|^10.0|^11.0|^12.0" "illuminate/support": "^8.12|^9.0|^10.0|^11.0|^12.0"
}, },
"require-dev": { "require-dev": {
"laravel/pint": "^1.22" "laravel/pint": "^1.22",
"laravel/framework": "*"
}, },
"extra": { "extra": {
"laravel": { "laravel": {
"providers": [ "providers": [
"Blax\\Roles\\PermissionsServiceProvider" "Blax\\Roles\\RolesServiceProvider"
] ]
} }
}, },
"minimum-stability": "dev", "minimum-stability": "dev",
"prefer-stable": true, "prefer-stable": true
"scripts": {
"post-install-cmd": [
"@php artisan vendor:publish --tag=roles-migrations --force",
"@php artisan vendor:publish --tag=roles-config --force"
]
}
} }

View File

@ -8,11 +8,13 @@ return [
'role_permission' => \Blax\Roles\Models\RolePermission::class, 'role_permission' => \Blax\Roles\Models\RolePermission::class,
'permission' => \Blax\Roles\Models\Permission::class, 'permission' => \Blax\Roles\Models\Permission::class,
'permission_usage' => \Blax\Roles\Models\PermissionUsage::class, 'permission_usage' => \Blax\Roles\Models\PermissionUsage::class,
'permission_members' => \Blax\Roles\Models\PermissionMember::class,
], ],
'table_names' => [ 'table_names' => [
'permissions' => 'permissions', 'permissions' => 'permissions',
'permission_usage' => 'permission_usages', 'permission_usage' => 'permission_usages',
'permission_members' => 'permission_members',
'roles' => 'roles', 'roles' => 'roles',
'role_member' => 'role_members', 'role_member' => 'role_members',
'role_permission' => 'role_permissions', 'role_permission' => 'role_permissions',

View File

@ -21,15 +21,26 @@ return new class extends Migration
$table->timestamps(); $table->timestamps();
}); });
// PermissionMember
Schema::create(config('roles.table_names.permission_members'), function (Blueprint $table) {
$table->id();
$table->foreignId('permission_id')->constrained('permissions')->onDelete('cascade');
$table->morphs('member');
$table->json('context')->nullable();
$table->timestamp('expires_at')->nullable();
$table->timestamps();
});
// PermissionUsage // PermissionUsage
Schema::create(config('roles.table_names.permission_usage'), function (Blueprint $table) { Schema::create(config('roles.table_names.permission_usage'), function (Blueprint $table) {
$table->id(); $table->id();
$table->foreignId('permission_id')->constrained('permissions')->onDelete('cascade'); $table->foreignId('permission_id')->constrained('permissions')->onDelete('cascade');
$table->float('usage', 8)->default(1);
$table->morphs('user'); $table->morphs('user');
$table->json('context')->nullable(); $table->json('context')->nullable();
$table->timestamps(); $table->timestamps();
}); });
// Role // Role
Schema::create(config('roles.table_names.roles'), function (Blueprint $table) { Schema::create(config('roles.table_names.roles'), function (Blueprint $table) {
$table->id(); $table->id();
@ -73,6 +84,7 @@ return new class extends Migration
Schema::dropIfExists(config('roles.table_names.role_members')); Schema::dropIfExists(config('roles.table_names.role_members'));
Schema::dropIfExists(config('roles.table_names.roles')); Schema::dropIfExists(config('roles.table_names.roles'));
Schema::dropIfExists(config('roles.table_names.permission_usage')); Schema::dropIfExists(config('roles.table_names.permission_usage'));
Schema::dropIfExists(config('roles.table_names.permission_members'));
Schema::dropIfExists(config('roles.table_names.permissions')); Schema::dropIfExists(config('roles.table_names.permissions'));
} }
}; };

View File

@ -4,7 +4,8 @@ namespace Blax\Roles\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
class Permission extends Model { class Permission extends Model
{
protected $fillable = [ protected $fillable = [
'slug', 'slug',
'description', 'description',
@ -26,4 +27,9 @@ class Permission extends Model {
{ {
return $this->belongsToMany(RolePermission::class); return $this->belongsToMany(RolePermission::class);
} }
public function members()
{
return $this->hasMany(PermissionMember::class);
}
} }

View File

@ -0,0 +1,41 @@
<?php
namespace Blax\Roles\Models;
use Blax\Roles\Traits\WillExpire;
use Illuminate\Database\Eloquent\Model;
class PermissionMember extends Model
{
use WillExpire;
protected $fillable = [
'permission_id',
'member_id',
'member_type',
'context',
'expires_at',
];
protected $casts = [
'context' => 'array',
'expires_at' => 'datetime',
];
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->table = config('roles.table_names.permission_members') ?: parent::getTable();
}
public function permission()
{
return $this->belongsTo(Permission::class);
}
public function member()
{
return $this->morphTo();
}
}

View File

@ -4,10 +4,19 @@ namespace Blax\Roles\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
class PermissionUsage extends Model { class PermissionUsage extends Model
{
protected $fillable = [ protected $fillable = [
'permission_id', 'permission_id',
'usage_count' 'usage',
'context',
'user_type',
'user_id',
];
protected $casts = [
'context' => 'array',
'usage' => 'float',
]; ];
public function __construct(array $attributes = []) public function __construct(array $attributes = [])
@ -21,4 +30,9 @@ class PermissionUsage extends Model {
{ {
return $this->belongsTo(Permission::class); return $this->belongsTo(Permission::class);
} }
public function user()
{
return $this->morphTo();
}
} }

View File

@ -4,10 +4,11 @@ namespace Blax\Roles\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
class Role extends Model { class Role extends Model
{
protected $fillable = [ protected $fillable = [
'parent_id', 'parent_id',
'name', 'name',
'slug', 'slug',
'description', 'description',
]; ];
@ -19,19 +20,23 @@ class Role extends Model {
$this->table = config('roles.table_names.roles') ?: parent::getTable(); $this->table = config('roles.table_names.roles') ?: parent::getTable();
} }
public function members() { public function members()
return $this->belongsToMany(RoleMember::class); {
return $this->hasMany(RoleMember::class, 'role_id', 'id');
} }
public function permissions() { public function permissions()
return $this->belongsToMany(RolePermission::class); {
return $this->hasMany(Permission::class, 'role_id', 'id');
} }
public function parent() { public function parent()
return $this->belongsTo(Role::class, 'parent_id'); {
return $this->hasOne(Role::class, 'id', 'parent_id');
} }
public function children() { public function children()
{
return $this->hasMany(Role::class, 'parent_id'); return $this->hasMany(Role::class, 'parent_id');
} }
} }

View File

@ -2,9 +2,13 @@
namespace Blax\Roles\Models; namespace Blax\Roles\Models;
use Blax\Roles\Traits\WillExpire;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
class RoleMember extends Model { class RoleMember extends Model
{
use WillExpire;
protected $fillable = [ protected $fillable = [
'role_id', 'role_id',
'member', 'member',
@ -12,6 +16,11 @@ class RoleMember extends Model {
'expires_at', 'expires_at',
]; ];
protected $casts = [
'context' => 'array',
'expires_at' => 'datetime',
];
public function __construct(array $attributes = []) public function __construct(array $attributes = [])
{ {
parent::__construct($attributes); parent::__construct($attributes);
@ -19,11 +28,13 @@ class RoleMember extends Model {
$this->table = config('roles.table_names.role_members') ?: parent::getTable(); $this->table = config('roles.table_names.role_members') ?: parent::getTable();
} }
public function role() { public function role()
{
return $this->belongsTo(Role::class); return $this->belongsTo(Role::class);
} }
public function member() { public function member()
{
return $this->morphTo(); return $this->morphTo();
} }
} }

View File

@ -2,9 +2,13 @@
namespace Blax\Roles\Models; namespace Blax\Roles\Models;
use Blax\Roles\Traits\WillExpire;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
class RolePermission extends Model { class RolePermission extends Model
{
use WillExpire;
protected $fillable = [ protected $fillable = [
'role_id', 'role_id',
'permission_id', 'permission_id',
@ -17,11 +21,13 @@ class RolePermission extends Model {
$this->table = config('roles.table_names.role_permissions') ?: parent::getTable(); $this->table = config('roles.table_names.role_permissions') ?: parent::getTable();
} }
public function role() { public function role()
{
return $this->belongsTo(Role::class); return $this->belongsTo(Role::class);
} }
public function permission() { public function permission()
{
return $this->belongsTo(Permission::class); return $this->belongsTo(Permission::class);
} }
} }

View File

@ -2,7 +2,7 @@
namespace Blax\Roles; namespace Blax\Roles;
class PermissionsServiceProvider extends \Illuminate\Support\ServiceProvider class RolesServiceProvider extends \Illuminate\Support\ServiceProvider
{ {
/** /**
* Register the service provider. * Register the service provider.
@ -73,5 +73,6 @@ class PermissionsServiceProvider extends \Illuminate\Support\ServiceProvider
$this->app->bind(\Blax\Roles\Models\RolePermission::class, fn ($app) => $app->make($app->config['roles.models.role_permission'])); $this->app->bind(\Blax\Roles\Models\RolePermission::class, fn ($app) => $app->make($app->config['roles.models.role_permission']));
$this->app->bind(\Blax\Roles\Models\Permission::class, fn ($app) => $app->make($app->config['roles.models.permission'])); $this->app->bind(\Blax\Roles\Models\Permission::class, fn ($app) => $app->make($app->config['roles.models.permission']));
$this->app->bind(\Blax\Roles\Models\PermissionUsage::class, fn ($app) => $app->make($app->config['roles.models.permission_usage'])); $this->app->bind(\Blax\Roles\Models\PermissionUsage::class, fn ($app) => $app->make($app->config['roles.models.permission_usage']));
$this->app->bind(\Blax\Roles\Models\PermissionMember::class, fn ($app) => $app->make($app->config['roles.models.permission_members']));
} }
} }

View File

@ -0,0 +1,27 @@
<?php
namespace Blax\Roles\Traits;
trait HasPermissions
{
public function hasPermission(string $permission, array $context = []): bool
{
return $this->permissions()
->where('name', $permission)
->where(function ($query) use ($context) {
if (!empty($context)) {
$query->where('context', $context);
}
})
->exists();
}
public function permissions()
{
return $this->morphToMany(
config('roles.models.permission'),
'member',
config('roles.table_names.permission_members')
);
}
}

22
src/Traits/WillExpire.php Normal file
View File

@ -0,0 +1,22 @@
<?php
namespace Blax\Roles\Traits;
trait WillExpire
{
public static function bootWillExpire()
{
static::addGlobalScope('willExpire', function ($builder) {
$builder->where(function ($query) {
$query->whereNull('expires_at')
->orWhere('expires_at', '>', now());
});
});
}
public function isExpired(): bool
{
return $this->expires_at && $this->expires_at->isPast();
}
}