You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中User、Document、Permission三模型关联关系定义咨询

Hey there! Let's walk through setting up these Laravel model relationships exactly how you need them. You already know User and Document are many-to-many, and we need to add granular read/write permissions per user-document pair. Here's a step-by-step breakdown that fits your example scenarios perfectly:

1. Database Migration Setup

First, we'll build the table structure to support permissions and their associations. We'll cover two approaches—one flexible for future permission expansion, and a simpler version if you only ever need read/write.

Flexible Approach (Supports Adding Permissions Later)

Create a permissions table to store permission types:

Schema::create('permissions', function (Blueprint $table) {
    $table->id();
    $table->string('name')->unique(); // Stores 'read' or 'write' (add more later if needed)
    $table->string('description')->nullable();
    $table->timestamps();
});

Then create a junction table to link users, documents, and their assigned permissions:

Schema::create('user_document_permissions', function (Blueprint $table) {
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->foreignId('document_id')->constrained()->onDelete('cascade');
    $table->foreignId('permission_id')->constrained()->onDelete('cascade');
    $table->primary(['user_id', 'document_id', 'permission_id']); // Prevents duplicate permissions for the same user-document pair
});

Simplified Approach (Only Read/Write)

If you're 100% sure you'll never need more than read and write, skip the separate permissions table and add a permission field directly to the user-document junction:

Schema::create('user_document', function (Blueprint $table) {
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->foreignId('document_id')->constrained()->onDelete('cascade');
    $table->enum('permission', ['read', 'write']);
    $table->primary(['user_id', 'document_id']); // One permission per user-document pair
});
2. Model Definitions

Let's define the models for each approach. We'll start with the flexible version first.

Flexible Approach Models

Permission Model

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Permission extends Model
{
    protected $fillable = ['name', 'description'];

    public function userDocumentPermissions(): HasMany
    {
        return $this->hasMany(UserDocumentPermission::class);
    }
}

User Model

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;

class User extends Model
{
    // Many-to-many with Document, including permission data
    public function documents(): BelongsToMany
    {
        return $this->belongsToMany(Document::class)
            ->through(UserDocumentPermission::class)
            ->withPivot('permission_id');
    }

    // Direct access to permission associations for easy management
    public function userDocumentPermissions(): HasMany
    {
        return $this->hasMany(UserDocumentPermission::class);
    }

    // Helper: Check if user has a specific permission for a document
    public function hasPermissionForDocument(Document $document, string $permissionName)
    {
        $permission = Permission::where('name', $permissionName)->firstOrFail();
        return $this->userDocumentPermissions()
            ->where('document_id', $document->id)
            ->where('permission_id', $permission->id)
            ->exists();
    }

    // Helper: Get all permissions for a document
    public function getPermissionsForDocument(Document $document)
    {
        return $this->userDocumentPermissions()
            ->where('document_id', $document->id)
            ->pluck('permission_id')
            ->map(fn($id) => Permission::find($id)->name);
    }
}

Document Model

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Document extends Model
{
    protected $fillable = ['name', 'content']; // Adjust fields to your needs

    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class)
            ->through(UserDocumentPermission::class)
            ->withPivot('permission_id');
    }

    public function userDocumentPermissions(): HasMany
    {
        return $this->hasMany(UserDocumentPermission::class);
    }
}

Pivot Model (UserDocumentPermission)

Create this model to handle the junction table logic:

namespace App\Models;

use Illuminate\Database\Eloquent\Relations\Pivot;

class UserDocumentPermission extends Pivot
{
    protected $table = 'user_document_permissions';
    protected $fillable = ['user_id', 'document_id', 'permission_id'];

    public function permission()
    {
        return $this->belongsTo(Permission::class);
    }
}

Simplified Approach Models

If you went with the simplified junction table, your User and Document models get simpler:

User Model

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class User extends Model
{
    public function documents(): BelongsToMany
    {
        return $this->belongsToMany(Document::class)->withPivot('permission');
    }

    // Helper: Check permission for a document
    public function hasPermissionForDocument(Document $document, string $permission)
    {
        return $this->documents()
            ->where('documents.id', $document->id)
            ->wherePivot('permission', $permission)
            ->exists();
    }
}

Document Model

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Document extends Model
{
    protected $fillable = ['name', 'content'];

    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class)->withPivot('permission');
    }
}
3. Seed Permissions (Flexible Approach Only)

Populate the permissions table with your read and write values:

// In DatabaseSeeder or a dedicated PermissionSeeder
Permission::firstOrCreate(['name' => 'read'], ['description' => 'Read access to document']);
Permission::firstOrCreate(['name' => 'write'], ['description' => 'Write access to document']);
4. Usage Examples

Let's implement your sample scenarios:

Flexible Approach

// Get permission instances
$readPerm = Permission::where('name', 'read')->first();
$writePerm = Permission::where('name', 'write')->first();

// User1: Write to Document1, Read Document2
$user1 = User::find(1);
$doc1 = Document::find(1);
$doc2 = Document::find(2);

$user1->userDocumentPermissions()->create([
    'document_id' => $doc1->id,
    'permission_id' => $writePerm->id
]);
$user1->userDocumentPermissions()->create([
    'document_id' => $doc2->id,
    'permission_id' => $readPerm->id
]);

// User2: Write to both documents
$user2 = User::find(2);
$user2->userDocumentPermissions()->create(['document_id' => $doc1->id, 'permission_id' => $writePerm->id]);
$user2->userDocumentPermissions()->create(['document_id' => $doc2->id, 'permission_id' => $writePerm->id]);

// User3: Read both documents
$user3 = User::find(3);
$user3->userDocumentPermissions()->create(['document_id' => $doc1->id, 'permission_id' => $readPerm->id]);
$user3->userDocumentPermissions()->create(['document_id' => $doc2->id, 'permission_id' => $readPerm->id]);

// Check permissions
if ($user1->hasPermissionForDocument($doc1, 'write')) {
    // Allow write operation for User1 on Document1
}

Simplified Approach

// User1: Write to Document1, Read Document2
$user1 = User::find(1);
$user1->documents()->attach($doc1, ['permission' => 'write']);
$user1->documents()->attach($doc2, ['permission' => 'read']);

// User2: Write to both documents
$user2 = User::find(2);
$user2->documents()->attach([$doc1->id => ['permission' => 'write'], $doc2->id => ['permission' => 'write']]);

// Check permissions
if ($user3->hasPermissionForDocument($doc2, 'read')) {
    // Allow read operation for User3 on Document2
}

内容的提问来源于stack exchange,提问作者Patrick Heppler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:34:04