Пример настройки конфигурации Moonshine с сторонней авторизацией на примере пакета Spatie-Permission

Пример настройки конфигурации Moonshine с сторонней авторизацией на примере пакета Spatie-Permission

Андрей Семенов
Андрей Семенов
25.06.2023 в 09:31

Рассмотрим пример настройки конфигурации Moonshine с сторонней авторизацией на примере пакета Spatie-Permission в самой базовой версии

Используемая версия Moonshine не ниже v1.60.1

1) Устанавливаем пакет spatie-permision через composer

 composer require spatie/laravel-permission

2) Необязательно: service provider автоматически зарегистрируется. Или вы можете вручную добавить поставщика услуг в свой файл config/app.php:

'providers' => [
    // ...
    Spatie\Permission\PermissionServiceProvider::class,
];

3) Опубликуйте миграцию и файл конфигурации config/permission.php с помощью:

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

4) Очистите кеш конфигурации с помощью любой из этих команд:

 php artisan optimize:clear
 # или
 php artisan config:clear

5)Запустите миграцию: после публикации и настройки конфигурации и миграции вы можете создать таблицы для этого пакета, выполнив:

 php artisan migrate

5.1) Добавьте trait HasRoles в вашу модель пользователя и HasMoonShineChangeLog(Документация) для просмотра лога изменений

class User extends Authenticatable
{

    use  HasFactory, Notifiable;

    use HasRoles ;
    use HasMoonShineChangeLog; 

6)Создайте свою миграцию для добавления доп. полей в существующие модели Spatie.

Так как в стандартной миграции Spatie нет описания полей, ролей и прав ,поле description ,а также нет дефолтного значения поля guard_name советую создать дополнитнльную миграцию для добавления и изменения этих полей.

Поле description даст некоторое удобство при создании ролей и прав - вы сможете добавлять свое описание .

php artisan make:migration alter_column_in_spaties_table

примерно такого содержания:



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

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

        if (empty($tableNames)) {
            throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
        }
        if ($teams && empty($columnNames['team_foreign_key'] ?? null)) {
            throw new \Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
        }

        Schema::table($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) {

            $table->string('description')->nullable();       // For MySQL 8.0 use string('name', 125);
            $table->string('guard_name')->default('web')->change(); // For MySQL 8.0 use string('guard_name', 125);

        });

        Schema::table($tableNames['permissions'], function (Blueprint $table) use ($teams, $columnNames) {

            $table->string('description')->nullable();       // For MySQL 8.0 use string('name', 125);


        });
    }

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

        if (empty($tableNames)) {
            throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
        }

        Schema::dropColumns($tableNames['roles'],'description');
        Schema::dropColumns($tableNames['permissions'],'description');


    }
};

Запустите миграцию если нужно внести дополнительные изменения выполнив:

 php artisan migrate

7) Внесите изменения в файл config/moonshine.php - места где внесены изменения помечены (//<-Изменили)



use MoonShine\Exceptions\MoonShineNotFoundException;
use MoonShine\Models\MoonshineUser;

return [
    'dir' => 'app/MoonShine',
    'namespace' => 'App\MoonShine',

    'title' => env('MOONSHINE_TITLE', 'MoonShine'),
    'logo' => env('MOONSHINE_LOGO', ''),

    'route' => [
        'prefix' => env('MOONSHINE_ROUTE_PREFIX', 'admin'), //<-Изменили
        'middleware' => ['moonshine'], 
        'custom_page_slug' => 'custom_page',
        'notFoundHandler' => MoonShineNotFoundException::class
    ],
    'use_migrations' => true,
    'use_notifications' => true,
    'auth' => [
        'enable' => true,
        'fields' => [
            'username' => 'email',
            'password' => 'password',
            'name' => 'name',
            'avatar' => 'avatar'
        ],
        'guard' => 'web',       //'moonshine', //<-Изменили
        'guards' => [
            'web' => [
                'driver' => 'session',
                'provider' => 'users',
            ], //<-Изменили
            'moonshine' => [
                'driver' => 'session',
                'provider' => 'moonshine',
            ],
        ],
        'providers' => [
            'users' => [
                'driver' => 'eloquent',
                'model' => App\Models\User::class, //<-Изменили
            ],
            'moonshine' => [
               'driver' => 'eloquent',
               'model' => App\Models\User::class, //<-Изменили MoonshineUser::class,
            ],
        ],
        'footer' => ''
    ],
    'locales' => [
        'ru'
    ],
    'middlewares' => [],
    'tinymce' => [
        'file_manager' => false, // or 'laravel-filemanager' prefix for lfm
        'token' => env('MOONSHINE_TINYMCE_TOKEN', ''),
        'version' => env('MOONSHINE_TINYMCE_VERSION', '6')
    ],
    'socialite' => [
        // 'driver' => 'path_to_image_for_button'
    ],
    'header' => null, // blade path
    'footer' => [
        'copyright' => 'Made with ❤️ by CutCode',
        'nav' => [
            'https://github.com/moonshine-software/moonshine/blob/1.5.x/LICENSE.md' => 'License',
            'https://moonshine.cutcode.dev' => 'Documentation',
            'https://github.com/moonshine-software/moonshine' => 'GitHub',
        ],
    ]
];

Если вы не хотите использовать аватар, то укажите в конфиге 'avatar'=>'' или 'avatar'=>false в ином случае создайте миграцию php artisan make:migration add_avatar_in_users_table

php artisan make:migration add_avatar_in_users_table

и заполните ее



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

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {

        Schema::table('users', function (Blueprint $table) {

          $table->string('avatar')->nullable();

        });


    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {    
        Schema::dropColumns('users','avatar');

    }
};

Это необходимо для использования avatar в персональном профиле , но вы можете переопредилить название этого поля в config/moonshine.php (Документация) .

8) Создайте ( php artisan moonshine:resource PermissionResource ) (Документация) , 2 ресурса которые есть по умолчанию можно отредактировать или создать свои . В данном случае мы отредактируем UserResourse, UserRolesResourse

Примеры: App\MoonShine\Resources\UserResourse:



namespace App\MoonShine\Resources;

use App\MoonShine\Resources\UserRolesResourse;
use Illuminate\Database\Eloquent\Model;
use App\Models\User;
use Illuminate\Support\Facades\Http;
use Illuminate\Validation\Rule;
use MoonShine\Actions\ExportAction;
use MoonShine\Decorations\Block;
use MoonShine\Decorations\Column;
use MoonShine\Decorations\Grid;
use MoonShine\Fields\BelongsTo;
use MoonShine\Fields\BelongsToMany;
use App\MoonShine\Fields\BelongToManyField;
use MoonShine\Fields\Date;
use MoonShine\Fields\Email;
use MoonShine\Fields\HasMany;
use MoonShine\Fields\Image;
use MoonShine\Fields\Password;
use MoonShine\Fields\PasswordRepeat;
use MoonShine\Fields\Text;
use MoonShine\Filters\DateFilter;
use MoonShine\Filters\TextFilter;
use MoonShine\FormComponents\ChangeLogFormComponent;
use MoonShine\FormComponents\PermissionFormComponent;
use MoonShine\Models\MoonshineUser;
use MoonShine\Models\MoonshineUserRole;
use MoonShine\Resources\MoonShineUserRoleResource;
use MoonShine\Resources\Resource;
use MoonShine\Fields\ID;
use MoonShine\Actions\FiltersAction;
use MoonShine\Traits\Resource\WithUserPermissions;




class UserResource extends Resource
{
    //use WithUserPermissions;//<-Изменили

    public static string $model = User::class; //<-Изменили

    public string $titleField = 'name';

    protected static bool $system = true;

    public function title(): string
    {
        return trans('moonshine::ui.resource.admins_title');
    }

    public function fields(): array
    {
        return [
            Grid::make([
                Column::make([
                    Block::make(trans('moonshine::ui.resource.main_information'), [
                        ID::make()
                            ->sortable()
                            ->useOnImport()
                            ->showOnExport(),



                        Text::make(trans('moonshine::ui.resource.name'), 'name')
                            ->required()
                            ->useOnImport()
                            ->showOnExport(),

                        Image::make(trans('moonshine::ui.resource.avatar'), 'avatar')
                            ->removable()
                            ->showOnExport()
                            ->disk(config('filesystems.default'))
                            ->dir('moonshine_users')
                            ->allowedExtensions(['jpg', 'png', 'jpeg', 'gif']),

                        Date::make(trans('moonshine::ui.resource.created_at'), 'created_at')
                            ->format("d.m.Y")
                            ->default(now()->toDateTimeString())
                            ->sortable()
                            ->hideOnForm()
                            ->showOnExport(),

                        Email::make(trans('moonshine::ui.resource.email'), 'email')
                            ->sortable()
                            ->showOnExport()
                            ->required(),

                        BelongsToMany::make(
                            trans('moonshine::ui.resource.role'),
                            'roles',
                            'name'//,
                            //new UserRolesResourse()
                        )
                            ->showOnExport()->select()->inLine('',true),

                        //Тест
                        BelongsToMany::make(
                            trans('moonshine::ui.resource.role'),
                            'roles',
                           // 'name'//,
                        new UserRolesResourse()
                        )
                            ->showOnExport()->inLine('',true)->hideOnIndex(),

                    ]),

                    Block::make(trans('moonshine::ui.resource.change_password'), [
                        Password::make(trans('moonshine::ui.resource.password'), 'password')
                            ->customAttributes(['autocomplete' => 'new-password'])
                            ->hideOnIndex()
                            ->hideOnExport()
                            ->hideOnDetail()
                            ->eye(),

                        PasswordRepeat::make(trans('moonshine::ui.resource.repeat_password'), 'password_repeat')
                            ->customAttributes(['autocomplete' => 'confirm-password'])
                            ->hideOnIndex()
                            ->hideOnExport()
                            ->hideOnDetail()
                            ->eye(),
                    ]),
                ]),
            ]),
        ];
    }

    public function components(): array
    {
        return [

//          ChangeLogFormComponent::make('Change log')
//          ->canSee(fn ($user) => $user->moonshine_user_role_id === 1),

            ChangeLogFormComponent::make('Change log')
            ->canSee(fn ($user) => $user->hasRole('admin')),
            // ->canSee(fn ($user) => $user->moonshine_user_role_id === 1),
        ];
    }

    public function rules($item): array
    {
        return [
            'name' => 'required',
//            'moonshine_user_role_id' => 'required',
            'email' => [
                'sometimes',
                'bail',
                'required',
                'email',
//     Rule::unique('moonshine_users')->ignoreModel($item),
                Rule::unique('users')->ignoreModel($item),
            ],
            'password' => ! $item->exists
                ? 'required|min:6|required_with:password_repeat|same:password_repeat'
                : 'sometimes|nullable|min:6|required_with:password_repeat|same:password_repeat',
        ];
    }

    public function search(): array
    {
        return ['id', 'name'];
    }

    public function filters(): array
    {
        return [
            TextFilter::make(trans('moonshine::ui.resource.name'), 'name'),
            DateFilter::make(trans('moonshine::ui.resource.created_at'), 'created_at'),
        ];
    }

    public function actions(): array
    {
        return [
            ExportAction::make(trans('moonshine::ui.export')),
        ];
    }
}

App\MoonShine\Resources\UserRolesResourse



namespace App\MoonShine\Resources;

use Illuminate\Database\Eloquent\Model;

use Illuminate\Validation\Rule;
use MoonShine\Decorations\Block;
use MoonShine\Fields\BelongsTo;
use MoonShine\Fields\Text;
use MoonShine\Filters\TextFilter;
use Spatie\Permission\Models\Role as Role;
use MoonShine\Fields\BelongsToMany;
use App\MoonShine\Fields\BelongToManyField;
use Spatie\Permission\Models\Permission as Permission;

use MoonShine\Resources\Resource;
use MoonShine\Fields\ID;
use MoonShine\Actions\FiltersAction;

class UserRolesResourse extends Resource
{
    public static string $model = \Spatie\Permission\Models\Role::class; //<-Изменили

    public string $titleField = 'name';

    protected static bool $system = false;

    protected bool $createInModal = true;

    protected bool $editInModal = true;

    public function title(): string
    {
        return trans('moonshine::ui.resource.role');
    }

    public function fields(): array
    {
        return [
            Block::make(trans('moonshine::ui.resource.main_information'), [
                ID::make()
                    ->sortable()
                    ->showOnExport(),

                Text::make(trans('moonshine::ui.resource.role_name'), 'name')
                    ->required()
                    ->showOnExport(),


                BelongsToMany::make('Права доступа', 'permissions', new UserResource(),
                )->select()->inLine('',true)


            ]),
        ];
    }

    public function rules($item): array
    {
        return [
            'name' => [
                'required',
                'min:5',
                 Rule::unique('roles')->ignoreModel($item), //<-Изменили

            ],

        ];
    }

    public function search(): array
    {
        return ['id', 'name'];
    }

    public function filters(): array
    {
        return [
            TextFilter::make(trans('moonshine::ui.resource.role_name'), 'name'),
        ];
    }

    public function actions(): array
    {
        return [];
    }
}

App\MoonShine\Resources\PermissionResourse:



namespace App\MoonShine\Resources;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\Rule;
use MoonShine\Fields\Text;
use Spatie\Permission\Models\Permission;

use MoonShine\Resources\Resource;
use MoonShine\Fields\ID;
use MoonShine\Actions\FiltersAction;

class PermissionResource extends Resource
{
    public static string $model = Permission::class;

    public static string $title = 'Права доступа';

    public function fields(): array
    {
        return [
            ID::make()->sortable(),
            Text::make(trans('moonshine::ui.resource.name'), 'name')
                ->required()
                ->useOnImport()
                ->showOnExport(),



        ];
    }

    public function rules(Model $item): array
    {
        return [
            'name' => [
                'required',
                'min:5',
                Rule::unique('permissions')->ignoreModel($item), //<-Изменили

            ],

        ];
    }

    public function search(): array
    {
        return ['id'];
    }

    public function filters(): array
    {
        return [];
    }

    public function actions(): array
    {
        return [
            FiltersAction::make(trans('moonshine::ui.filters')),
        ];
    }
}

9) Добавьте пункты в Меню в классе MoonShineServiceProvider (Документация):

  MenuGroup::make('moonshine::ui.resource.system', [

                MenuItem::make('Пользователи', new UserResource())
                    ->translatable()
                    ->icon('users'),

                MenuItem::make('Роли', new UserRolesResourse())
                    ->translatable()
                    ->icon('bookmark'),

                MenuItem::make('Права', new PermissionResource())
                    ->translatable()
                    ->icon('bookmark'),

            ])->translatable(),

10) По всем возможнястям авторизации Spatie можете ознакомиться на оф. сайте spatie/laravel-permission