Let's Go Further مجوز مبتنی بر نقش › راه‌اندازی مدل مجوزها
قبلی · فهرست مطالب · بعدی
فصل ۱۶.۳.

راه‌اندازی مدل مجوزها

در ادامه، به پکیج internal/data می‌رویم و یک PermissionModel برای مدیریت تعاملات با جداول جدیدمان اضافه می‌کنیم. فعلاً تنها چیزی که می‌خواهیم در این مدل بگنجانیم، متد GetAllForUser() است که تمام کدهای مجوز را برای یک کاربر خاص برمی‌گرداند. ایده این است که بتوانیم از این در handler‌ها و middleware‌هایمان به این صورت استفاده کنیم:

// Return a slice of the permission codes for the user with ID = 1. This would return
// something like []string{"movies:read", "movies:write"}.
app.models.Permissions.GetAllForUser(1) 

در پشت صحنه، دستور SQL که برای دریافت کدهای مجوز یک کاربر خاص نیاز داریم به این صورت است:

SELECT permissions.code
FROM permissions
INNER JOIN users_permissions ON users_permissions.permission_id = permissions.id
INNER JOIN users ON users_permissions.user_id = users.id
WHERE users.id = $1

در این کوئری از عبارت INNER JOIN استفاده می‌کنیم تا جدول permissions را به جدول users_permissions متصل کنیم و سپس دوباره از آن استفاده می‌کنیم تا آن را به جدول users متصل کنیم. سپس از عبارت WHERE برای فیلتر کردن نتیجه استفاده می‌کنیم و فقط ردیف‌هایی را که مربوط به یک user ID خاص هستند باقی می‌گذاریم.

بیایید با راه‌اندازی PermissionModel جدید پیش برویم. ابتدا یک فایل جدید internal/data/permissions.go ایجاد کنید:

$ touch internal/data/permissions.go

و سپس کد زیر را اضافه کنید:

فایل: internal/data/permissions.go
package data

import (
    "context"
    "database/sql"
    "time"
)

// Define a Permissions slice, which we will use to hold the permission codes (such as
// "movies:read" and "movies:write") for a single user.
type Permissions []string

// Add a helper method to check whether the Permissions slice contains a specific 
// permission code.
func (p Permissions) Include(code string) bool {
    return slices.Contains(p, code)
}

// Define the PermissionModel type.
type PermissionModel struct {
    DB *sql.DB
}

// The GetAllForUser() method returns all permission codes for a specific user in a 
// Permissions slice. The code in this method should feel very familiar --- it uses the
// standard pattern that we've already seen before for retrieving multiple data rows in 
// an SQL query.
func (m PermissionModel) GetAllForUser(userID int64) (Permissions, error) {
    query := `
        SELECT permissions.code
        FROM permissions
        INNER JOIN users_permissions ON users_permissions.permission_id = permissions.id
        INNER JOIN users ON users_permissions.user_id = users.id
        WHERE users.id = $1`

    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()

    rows, err := m.DB.QueryContext(ctx, query, userID)
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var permissions Permissions

    for rows.Next() {
        var permission string

        err := rows.Scan(&permission)
        if err != nil {
            return nil, err
        }

        permissions = append(permissions, permission)
    }
    if err = rows.Err(); err != nil {
        return nil, err
    }

    return permissions, nil
}

سپس آخرین کاری که باید انجام دهیم اضافه کردن PermissionModel به ساختار Model والد است تا برای handler‌ها و middleware‌هایمان در دسترس باشد. به این صورت:

فایل: internal/data/models.go
package data

...

type Models struct {
    Movies      MovieModel
    Permissions PermissionModel // Add a new Permissions field.
    Tokens      TokenModel
    Users       UserModel
}

func NewModels(db *sql.DB) Models {
    return Models{
        Movies:      MovieModel{DB: db},
        Permissions: PermissionModel{DB: db}, // Initialize a new PermissionModel instance.
        Tokens:      TokenModel{DB: db},
        Users:       UserModel{DB: db},
    }
}