• Czas czytania ~5 min
  • 29.03.2023

Podczas korzystania z Laravel Jetstream Teams domyślne role Jetstream mogą być nieelastyczne dla niektóre potrzeby. Jeśli potrzebujesz więcej funkcji dotyczących zarządzania rolami w zespole, możesz połączyć Laravel Jetstream z popularny pakiet Spatie Permission.

Pierwsze kroki

Zakładam, że masz świeżą instalację Laravel Jetstream z włączoną funkcją zespołów, jeśli nie wiesz, jak to zrobić że należy postępować zgodnie z dokumentacją Laravel Jetstream.

W tym przykładzie używam stosu Livewire, ale ponieważ skupiam się na rzeczach backendowych, ten samouczek powinien równie dobrze działać ze stosem bezwładnościowym.

Instalowanie pakietu

uprawnień Spatie Instalujemy pakiet za pomocą kompozytora.

composer require spatie/laravel-permission

Teraz publikujemy plik konfiguracyjny migracji i permission.php za pomocą:

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

Teraz dodaj cechę HasRoles uprawnienia Spatie do modelu użytkownika.

use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasApiTokens;
    use HasFactory;
    use HasProfilePhoto;
    use HasTeams;
    use Notifiable;
    use TwoFactorAuthenticatable;
    use HasRoles;
}

Teraz musimy włączyć funkcję uprawnień zespołu. Głównie śledzimy dokumentacja Spatie z niektórymi Dostosowywanie strumienia strumieniowego.

Włącz konfigurację uprawnień zespołów:

// config/permission.php
'teams' => true,

Teraz musimy dodać oprogramowanie pośredniczące, które stosuje bieżący zakres zespołu użytkowników do pakietu uprawnień Spatie.

php artisan make:middleware TeamsPermission

W oprogramowaniu pośredniczącym TeamsPermission musimy wyodrębnić bieżący identyfikator zespołu użytkowników i zastosować go do uprawnień Spatie Zakres zespołu.

use Closure;
use Illuminate\Http\Request;
use Spatie\Permission\PermissionRegistrar;

class TeamsPermission
{
    public function handle(Request $request, Closure $next)
    {
        if (!empty($user = auth()->user()) && !empty($user->current_team_id)) {
            app(PermissionRegistrar::class)->setPermissionsTeamId($user->current_team_id);
        }

        return $next($request);
    }
}

To oprogramowanie pośredniczące musi być stosowane przy każdym żądaniu HTTP przychodzącym do naszej aplikacji. Aby to zrobić, musimy rozszerzyć Jądro.php plik.

        //app/Http/Kernel.php
        'web' => [
            ...
            \App\Http\Middleware\TeamsPermission::class
        ],

W moim przypadku nie chcę, aby to oprogramowanie pośredniczące było stosowane do żądań api, więc dodałem je tylko do grupy oprogramowania pośredniczącego "web". Jeśli chcesz, aby był stosowany do wszystkich żądań, możesz dodać go do globalnego stosu oprogramowania pośredniczącego.

Po zakończeniu ogólnej konfiguracji możemy zacząć bawić się naszymi nowymi rolami i uprawnieniami specyficznymi dla zespołu.

Konfigurowanie seederów dla danych

demonstracyjnych Aby pobawić się naszą nową bazą kodu, potrzebujemy danych demonstracyjnych. Ustawmy kilka szybkich i brudnych siewników.

php artisan make:seeder PermissionSeeder

Role początkowe i uprawnienia

//Database\Seeders\PermissionSeeder.php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;

class PermissionSeeder extends Seeder
{
    public function run(): void
    {
        //Some initially role configuration
        $roles = [
            'Admin' => [
                'view posts',
                'create posts',
                'update posts',
                'delete posts',
            ],
            'Editor' => [
                'view posts',
                'create posts',
                'update posts'
            ],
            'Member' => [
                'view posts'
            ]
        ];

        collect($roles)->each(function ($permissions, $role) {
            $role = Role::findOrCreate($role);
            collect($permissions)->each(function ($permission) use ($role) {
                $role->permissions()->save(Permission::findOrCreate($permission));
            });
        });
    }
}

Umieść użytkownika demonstracyjnego z osobistym i 3 innymi powiązanymi członkostwami

//Database\Seeders\DatabaseSeeder.php
namespace Database\Seeders;

use App\Models\Team;
use App\Models\User;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        $this->call([PermissionSeeder::class]);

        User::factory(['email' => '[email protected]'])
            ->withPersonalTeam()
            ->hasAttached(Team::factory()->count(3))
            ->create();

    }
}

w zespole Zarządzanie rolami w interfejsie Zakładam, że chcesz pokazać użytkownikom aktualne role w interfejsie

i chcesz mieć takich administratorów, którzy mogą Zarządzanie rolami członków zespołu. Ponieważ baza kodu Jetstream ulegnie zmianie w przyszłości, nie rozszerzymy domyślnej bazy kodu Jetstream zarządza rolami / działaniami członkostwa w zespole w tym celu.

Podam więc kilka przykładów kodu, jak używać wielu ról w zespołach Jetstream.

Ponieważ każde żądanie frontendu przychodzące do naszej aplikacji jest obsługiwane przez nasz \ App\Http\Middleware\TeamsPermission:: class middleware pakiet Spatie Permissions jest zawsze świadomy bieżącego zakresu zespołu użytkowników. Tak więc każda rola lub Żądanie uprawnień, które zostało wywołane w interfejsie, jest ograniczone do bieżącego identyfikatora zespołu użytkowników.

Możemy więc używać pakietu Spatie Permission tak, jak zwykle. Wszystko działa zgodnie z oczekiwaniami!

//Get the user roles
auth()->user->roles()->get();
//Get the user permissions
auth()->user->permissions()->get();
//Give permission to a user
$role->givePermissionTo($permission);
//assign Roles
$permission->assignRole($role);

Zarządzanie rolami według interfejsu API / zaplecza

Załóżmy, że chcemy zarządzać zespołami aplikacji według interfejsu API. Zazwyczaj chcesz to zrobić, gdy inny zewnętrzny system lub mikroserwis chce aktualizować członków zespołu.

Aby to zrobić, potrzebujemy invokable API controller, który może aktualizować zespoły.

php artisan make:controller Api/UpdateTeamController -i

Dla uproszczenia nie utworzymy klas żądań i zasobów dla tego przykładu.

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Team;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Spatie\Permission\PermissionRegistrar;

class UpdateTeamController extends Controller
{
    public function __invoke(Request $request, Team $team): Team
    {
        $data = $request->validate([
            'name' => ['string', 'required', 'max:255'],
            'memberships' => ['array', 'sometimes'],
            'memberships.*.user_id' => ['required', 'integer', 'exists:users,id'],
            'memberships.*.roles' => ['present', 'array'],
            'memberships.*.roles.*' => ['string', Rule::exists('roles', 'name')->where('guard_name', 'web')]
        ]);

        DB::transaction(function () use (&$team, $data, $request) {
            //Update team fields
            $team->forceFill($request->except(['memberships']));
            $team->saveOrFail();

            if ($request->has('memberships')) {
                $memberships = collect($data['memberships']);
                //sync team memberships
                $team->users()->sync($memberships->map(function ($membership) {
                    return $membership['user_id'];
                })->toArray());
                //set Spatie permissions team foreign id scope
                app(PermissionRegistrar::class)->setPermissionsTeamId($team->id);
                //sync roles
                $memberships->each(function ($membership) {
                    $user = User::findOrFail($membership['user_id']);
                    $user->syncRoles($membership['roles']);
                });
            }

        });
        return $team;
    }
}

Za pomocą tego kodu kontroler może aktualizować nazwy zespołów, członkostwo w zespołach i role użytkowników zespołu. Rozłóżmy to Zakładamy,

że otrzymujemy listę użytkowników z odpowiadającą im rolą dla tego konkretnego zespołu. Ta informacja jest przechowywany w tablicy członkostw. Po sprawdzeniu poprawności danych wejściowych możemy rozpocząć aktualizację stanu naszej bazy danych.

Każde DB::transaction() żądanie bazy danych wykonane w ramach zamknięcia jest pakowane w zatwierdzeniu bazy danych. Jeśli wyjątek zostanie zgłoszony w zamknięciu, Laravel Framework wycofa wszystkie nasze zmiany. Kiedy wszystko działa dobrze, Auto zatwierdza nasze zmiany w bazie danych.

W ramach transakcji rozpoczynamy aktualizację pól zespołu. Nic w tym szczególnego. Następnie przetwarzamy pola z tablicy członkostw. Najpierw zaczynamy synchronizować wszystkie podane identyfikatory użytkowników z Zespół Jetstream przy użyciu metody sync() w relacji

członkostwa belongsToMany(). Teraz dochodzimy do konkretnych rzeczy związanych z pozwoleniem Spatie Laravel:

Na początku musimy powiedzieć pakietowi uprawnień, w którym zakresie zespołu jesteśmy app(PermissionRegistrar::class)->setPermissionsTeamId($team->id);. Tak więc wszystkie uprawnienia lub role, które są aktualizowane lub wybierane po tym wierszu kodu, są zawsze ograniczone do bieżącego zespołu Jetstream. W tym przypadku chcemy, aby był to konkretny zespół dostarczony na model trasy, wiążący się z naszym kontrolerem. Ale czasami chcesz zaktualizować uprawnienia dla różnych zespołów w pętli, musisz ustawić ten zakres za każdym razem gdy zmieniasz odpowiedni zakres zespołu.

Po zmianie zakresu zespołu możemy zaktualizować role tak, jak zwykle.

Na końcu naszej akcji kontrolera zwracamy zaktualizowany obiekt Team.

Ostatnie słowa

Nauczyliśmy się, jak połączyć Laravel Jetstream Teams z niesamowitym pakietem Spatie Permission. Dzięki niesamowitej dokumentacji z tych pakietów w większości śledzimy oficjalne Dokumenty. Ale myślę, że ten artykuł będzie pomocny, gdy Zacznij majstrować przy tych pakietach.

Cały kod źródłowy z tego artykułu jest dostępny w witrynie GitHub.All the source code from this article is available on GitHub.

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

O

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

O autorze CrazyBoy49z
WORK EXPERIENCE
Kontakt
Ukraine, Lutsk
+380979856297