Dzianis Kotau
About Me

Мy name is Dzianis Kotau. I'm Solutions Architect and Zend Certified PHP Engineer. I'm PHP evangelist and loved in it.

MoonShine Impersonate v3

MoonShine Impersonate предоставляет возможность подмены пользователя в проектах на Laravel с использованием админ-панели MoonShine. Функциональность подмены пользователя позволяет админстратору войти на сайт как реальный пользователь. Это может быть полезно, чтобы посмотреть, как сайт виден обычному пользователю, воспроизвести баги от его имени и т.д.

Содержимое

Установка

MoonShine Impersonate спроектирован для использования в составе админ-панели MoonShine. Поэтому, сперва установите админ-панель, следуя инструкциям с официального сайте MoonShine. Затем установите данный пакет при помощи команды:

composer require jampire/moonshine-impersonate

Обновление

Настройка

MoonShine Impersonate способен работать из коробки с настройками по умолчанию. Но вы можете опубликовать его настройки в свою директорию config и изменть нужные пареметры (зачастую в файле .env):

php artisan vendor:publish --provider="Jampire\MoonshineImpersonate\ImpersonateServiceProvider" --tag=config

Также можно опубликовать файлы локализации для более тонкой настройки:

php artisan vendor:publish --provider="Jampire\MoonshineImpersonate\ImpersonateServiceProvider" --tag=lang

Использование

Базовое использование

Включение подмены пользователя

Т.к. данный пакет работает с системой авторизации, то применять его можно на модели User. Для этого создайте MoonShine ресурс, использующий модель авторизации User вашего приложения.

После создания MoonShine ресурса User, откройте файл app/MoonShine/Resources/User/Pages/UserIndexPage.php, который может выглядеть так:

<?php

declare(strict_types=1);

namespace App\MoonShine\Resources\User\Pages;

use MoonShine\Laravel\Pages\Crud\IndexPage;
use MoonShine\Contracts\UI\FieldContract;
use MoonShine\UI\Fields\Email;
use MoonShine\UI\Fields\ID;
use App\MoonShine\Resources\User\UserResource;
use MoonShine\Support\ListOf;
use MoonShine\UI\Fields\Text;

class UserIndexPage extends IndexPage
{
    protected bool $isLazy = true;

    protected function fields(): iterable
    {
        return [
            ID::make(),
            Text::make('Name'),
            Email::make('E-mail', 'email'),
        ];
    }

    protected function buttons(): ListOf
    {
        return parent::buttons();
    }

    // ...
}

Для добавления действия подмены пользователя пакет MoonShine Impersonate предоставляет свою кнопку действия EnterImpersonationActionButton. Для его добавления воспользуйтесь методом buttons() класса UserIndexPage:

<?php

declare(strict_types=1);

namespace App\MoonShine\Resources\User\Pages;

use Jampire\MoonshineImpersonate\UI\ActionButtons\EnterImpersonationActionButton;
use MoonShine\Contracts\UI\ActionButtonContract;
use MoonShine\Laravel\Pages\Crud\IndexPage;
use App\MoonShine\Resources\User\UserResource;
use MoonShine\Support\ListOf;
// ...

class UserIndexPage extends IndexPage
{
    // ...

    protected function buttons(): ListOf
    {
        return parent::buttons()
            ->prepend(EnterImpersonationActionButton::resolve());
    }

    // ...
}

За тонкую настройку этого действия отвечают секция buttons.enter файла конфигурации и секция ui.buttons.enter файлов локализации.

После подключения данного действия ваш ресурс Users может выглядеть следующим образом:

Start Impersonation Button

При нажатии на эту кнопку вы переключитесь на выбранного пользователя (Livewire Starter Kit):

Impersonation Mode

Отключение подмены пользователя

Для отключения действия подмены пользователя пакет MoonShine Impersonate предоставляет свою кнопку действия StopImpersonationActionButton. Рассмотрим способ размещения кнопки отключения в заголовке шаблона. Разместив кнопку в заголовке, вы всегда будите знать, что находитесь в режиме подмены пользователя, т.к. кнопка будет видна на всех страницах админ-панели (когда режим подмены остановлен, кнопка видна не будет).

Для того, чтобы размещать кастомные элементы в заголовке шаблона, вам нужно отредактировать шаблон app/MoonShine/Layouts/MoonShineLayout.php. Для примера, разместим кнопку как предпоследний элемент:

<?php

declare(strict_types=1);

namespace App\MoonShine\Layouts;

use Jampire\MoonshineImpersonate\UI\ActionButtons\StopImpersonationActionButton;
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\UI\Components\Layout\Header;
// ...

final class MoonShineLayout extends AppLayout
{
    // ...

    #[\Override]
    protected function getHeaderComponent(): Header
    {
        $stopImpersonation = StopImpersonationActionButton::resolve();

        $components = parent::getHeaderComponent()->getComponents();
        $components->splice($components->count() - 2, 0, [$stopImpersonation]);

        return Header::make($components);
    }
}

Stop Impersonation Button

Middleware

Для своей работы режим подмены пользователя использует ImpersonateMiddleware класс. По-умолчанию он работает с маршрутами (routes) в группе web. Если вы хотите добавить этот middleware к другим маршрутам, воспольуйтесь псевдонимом impersonate. Например, его можно подключить в контроллере:

class PostController extends Controller
{
    public function __construct()
    {
        $this->middleware('impersonate');
    }
}

Разрешения

По умолчанию, любой администратор может подменять любого пользователя. Если вы желаете более тонкую настройку разрешений (permissions), можно воспользоваться интерфейсами Impersonable и BeImpersonable и реализовать методы canImpersonate() и canBeImpersonated() соответственно.

Рассмотрим один из способов ограничения прав админа через его модель. Для этого, создадим модель Admin, унаследовавшись от MoonShine\Permissions\Models\MoonshineUser и реализовав метод canImpersonate():

<?php

namespace App\Models;

use Jampire\MoonshineImpersonate\Services\Contracts\Impersonable;
use MoonShine\Laravel\Models\MoonshineUser;
use MoonShine\Laravel\Models\MoonshineUserRole;

final class Admin extends MoonshineUser implements Impersonable
{
    public function getTable(): string
    {
        return (new parent())->getTable();
    }

    public function canImpersonate(): bool
    {
        return $this->moonshine_user_role_id === MoonshineUserRole::DEFAULT_ROLE_ID;
    }
}

После чего нужно указать в файле конфигурации config/moonshine.php, что мы хотим использовать модель Admin вместо MoonshineUser:

return [
    // Authentication and profile
    'auth' => [
        // ...
        'model' => \App\Models\Admin::class,
        // ...
    ],
];

А чтобы контролировать, кого из пользователей можно подменять, нужно реализовать метод canBeImpersonated() на моделе авторизации User:

<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Str;
use Jampire\MoonshineImpersonate\Services\Contracts\BeImpersonable;

final class User extends Authenticatable implements BeImpersonable
{
    // ...

    public function canBeImpersonated(): bool
    {
        return !Str::endsWith($this->email, '@verysecureemail.com');
    }
}

Запись событий (логирование)

Если вы желаете, то можете включить запись событий переключения пользователя в журнал MoonShine (по-умолчанию отключено). Для включение режима записи вам необходимо установить пакет MoonShine Changelog и добавить трейт HasChangeLog к модели авторизации User (не забудьте настроить и запустить очередь событий):

<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use MoonShine\ChangeLog\Traits\HasChangeLog;

final class User extends Authenticatable
{
    use HasChangeLog;

    // ...
}

При старте режима подмены пользователя в журнале (таблица moonshine_change_logs) появится следующая запись:

id: 7
moonshine_user_id: 1 # пользователь, который запустил режим подмены (админ)
changelogable_type: App\Models\User # модель авторизации пользователя
changelogable_id: 4 # пользователь, которого подменяют
states_before: "impersonation_stopped" # статус приложения до начала подмены
states_after: "impersonation_entered" # статус приложения после начала подмены
created_at: 2026-02-06 23:25:11
updated_at: 2026-02-06 23:25:11

При окончании режима подмены пользователя в журнале (таблица moonshine_change_logs) появится следующая запись:

id: 8
moonshine_user_id: 1 # пользователь, который остановил режим подмены (админ)
changelogable_type: App\Models\User # модель авторизации пользователя
changelogable_id: 4 # пользователь, которого подменяли
states_before: "impersonation_entered" # статус приложения до остановки подмены
states_after: "impersonation_stopped" # статус приложения после остановки подмены
created_at: 2026-02-06 23:25:37
updated_at: 2026-02-06 23:25:37

События

После старта и отмены режима подмены пользователя отправляются события ImpersonationEntered и ImpersonationStopped соответственно. Оба эти события обладают следующими открытыми свойствами:

Расширенное использование

Расширенное использование подразумевает, что вы хорошо владеете админ-панелью MoonShine и фреймворком Laravel.

Пакет MoonShine Impersonate предоставляет кнопки действий EnterImpersonationActionButton и StopImpersonationActionButton. Но вы можете самостоятельно реализовать свои кнопки, используя админ-панель MoonShine и ресурсы данного пакета. Например, кнопки можно отрисовать в in-line или dropdown режимах. Пример реализации кнопки включения режима подмены пользователя:

use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Jampire\MoonshineImpersonate\Services\ImpersonateManager;
use MoonShine\UI\Components\ActionButton;

ActionButton::make(
    label: 'Подменить пользователя',
    url: static fn (Model $item): string => route('impersonate.enter', [
        'resourceItem' => $item->getKey(),
    ]),
)
    ->canSee(
        callback: fn (Authenticatable $item): bool => app(ImpersonateManager::class)->canEnter($item),
    )
    ->icon('users');

Cписок ресурсов пакета MoonShine Impersonate

Дополнительные ресурсы

Функционал подмены пользователя реализуют некоторые админ-панели. Например: