MoonShine — это open-source админ-панель для Laravel, и 10 декабря 2024 г. вышел релиз MoonShine v3. Я уже опубликовал статью про историю работы над проектом и рассказал про нововведения. 

В этом материале я расскажу что именно изменилось в самом MoonShine. Мы рассмотрим улучшения в установке и конфигурации, поддержку различных подходов к настройке, улучшения в работе с полями, новые возможности для интеграции с API, а также улучшенную работу с компонентами и меню. Также уделим внимание новым возможностям работы с фронтендом через Alpine.js, а также внедрению новых систем, таких как JSON-ответы и спецификации OpenAPI.

Давайте рассмотрим подробнее!

Установка

Документация

Начнем с установки. Что изменилось? Во-первых, у нас теперь при установке будет гораздо больше вопросов, что позволяет сразу сконфигурировать админку под ваши нужды. Теперь уже при установке указываем: нужны ли миграции, уведомления, и на основе какого драйвера они будут работать:

Если вы уже не первый раз устанавливаете MoonShine и вам нужна дефолтная конфигурация, то вы можете выполнить команду установки с опцией -Q и тем самым пропустить вопросы при установке:

php artisan moonshine:install -Q

Уведомления

Ранее в MoonShine использовался забетонированный драйвер database для уведомлений, но теперь появилась возможность менять его на любой другой.

Если вы не хотите использовать database-уведомления, а, например, предпочитаете broadcast или другой драйвер, вы можете заменить стандартную реализацию на свою (подробности — в документации).

Возьмем, к примеру, пакет Rush. В нем работа с уведомлениями основана на WebSockets, и при этом Rush не завязан на MoonShine. Он просто сервис-контейнер - прокидывает реализацию своих уведомлений, используя в данном случае драйвер broadcast.

Конфиг массив/объект

Документация

В MoonShine v3 добавлены два варианта работы с конфигурацией, чтобы предоставить гибкость для разных предпочтений: использование массива и использование объекта.

  1. Массивы, как в Laravel, — привычный подход, где настройки хранятся в файле moonshine.php.

return [
    'title' => env('MOONSHINE_TITLE', 'MoonShine'),
    'logo' => '/assets/logo.png',
    'domain' => env('MOONSHINE_DOMAIN'),
    'prefix' => 'admin',
    'auth' => [
        'enabled' => true,
        'guard' => 'moonshine',
    ],
   // …
];

2. Объектный подход, который реализуется через MoonShineServiceProvider.

$config
           ->title('My Application')
           ->logo('/assets/logo.png')
           ->guard('moonshine')
           ->authEnable();

Объектный подход был добавлен, потому что конфигурация в виде массива имеет определённые недостатки. Например, если в конфиге сделать ошибку в строке (даже простую опечатку, вместо enabled написать enable, случайно удалив d в конце), отвечающей за выключение аутентификации (auth => enabled), то в случае слабого покрытия тестами такая ошибка может попасть в продакшен, и проект будет работать без аутентификации.

С объектным подходом таких ошибок значительно меньше, так как в коде можно использовать автодополнение IDE, что делает работу с конфигом более удобной. Например, в случае с аутентификацией можно вызывать метод authDisable, и выключение аутентификации будет проще контролировать.

Конфигурация через объект имеет более высокий приоритет, чем настройки в файле конфигурации.

В документации приведены примеры, как с помощью конфигурации можно задавать такие параметры, как логотип, префиксы и изменять настройки guard.

Также в MoonShine v3 появился новый параметр resource_prefix, который влияет на структуру URL. По умолчанию URL будет иметь вид:

/admin/resource/{resourceUri}/{pageUri}

Если задать resource_prefix пустым, URL будет выглядеть так:

/admin/{resourceUri}/{pageUri}

Документация

Напомню, что для MoonShine v3 полностью переписана документация, теперь она в формате Markdown и работает на собственной платформе. Подробнее уже рассказал в этой статье.

В документации появилась новая фишка - табы. Когда используется несколько подходов, как в конфиге, то сразу можно удобно визуально оценить, как одну и ту же задачу можно сделать через Config и как это сделать через Provider. 

В третьей версии MoonShine документация стала намного лучше. Что хочу отметить:

Английский из коробки

В MoonShine v3 больше нет русского языка в коробочной версии — по умолчанию доступен только английский. Локализации на другие языки нужно устанавливать отдельно. Причина этого проста: MoonShine используется не только в СНГ, и английский язык остаётся самым популярным в мировом сообществе разработчиков. Поэтому в коробке изначально идет именно английский.

Если вам требуется другая локализация, можно установить нужный пакет из маркетплейса. На данный момент доступны русская, украинская, немецкая локализации, а недавно появился и китайский перевод. Есть и другие пакеты для локализации, но часть из них ещё не обновлены под версию 3.

Layout

Документация

В третьей версии MoonShine система шаблонов (Layout) была полностью переработана. Теперь она стала более гибкой и удобной для разработчиков, позволяя легко настраивать внешний вид и поведение админ-панели. Давайте разберем ключевые изменения и преимущества.

Система шаблонов из MoonShine v2 была полностью удалена. Вместо неё была создана новая, более гибкая и модульная система, которая позволяет легко переопределять Layout на любой странице.

Дефолтный Layout

По умолчанию Layout задается в конфигурации MoonShine. Однако теперь вы можете переопределить его для любой страницы, что делает систему более гибкой:

namespace App\MoonShine\Layouts;

use App\MoonShine\Resources\PackageCategoryResource;
use App\MoonShine\Resources\PackageResource;
use App\MoonShine\Resources\UserResource;
use MoonShine\ColorManager\ColorManager;
use MoonShine\Contracts\ColorManager\ColorManagerContract;
use MoonShine\Laravel\Components\Layout\{Locales, Notifications, Profile, Search};
use MoonShine\Laravel\Layouts\CompactLayout;
use MoonShine\MenuManager\MenuGroup;
use MoonShine\MenuManager\MenuItem;
use MoonShine\UI\Components\{Breadcrumbs,
    Components,
    Layout\Assets,
    Layout\Div,
    Layout\Body,
    Layout\Burger,
    Layout\Content,
    Layout\Favicon,
    Layout\Flash,
    Layout\Footer,
    Layout\Head,
    Layout\Header,
    Layout\Html,
    Layout\Layout,
    Layout\Logo,
    Layout\Menu,
    Layout\Meta,
    Layout\Sidebar,
    Layout\ThemeSwitcher,
    Layout\Wrapper,
    When}; 

final class MoonShineLayout extends CompactLayout
{
    public function build(): Layout
    {
        return Layout::make([
            Html::make([
                Head::make([
                    Meta::make()->customAttributes([
                        'name' => 'csrf-token',
                        'content' => csrf_token(),
                    ]),
                    Favicon::make()->bodyColor($this->getColorManager()->get('body')),
                    Assets::make(),
                ])
                    ->bodyColor($this->getColorManager()->get('body'))
                    ->title($this->getPage()->getTitle()),
                Body::make([
                    Wrapper::make([
                        Sidebar::make([
                            Div::make([
                                Div::make([
                                    Logo::make(
                                        $this->getHomeUrl(),
                                        $this->getLogo(),
                                        $this->getLogo(small: true),
                                    )->minimized(),
                                ])->class('menu-heading-logo'),

                                Div::make([
                                    Div::make([
                                        ThemeSwitcher::make(),
                                    ])->class('menu-heading-mode'),

                                    Div::make([
                                        Burger::make(),
                                    ])->class('menu-heading-burger'),
                                ])->class('menu-heading-actions'),
                            ])->class('menu-heading'),

                            Div::make([
                                Menu::make(),
                                When::make(
                                    fn(): bool => $this->isAuthEnabled(),
                                    static fn(): array => [Profile::make(withBorder: true)],
                                ),
                            ])->customAttributes([
                                'class' => 'menu',
                                ':class' => "asideMenuOpen && '_is-opened'",
                            ]),
                        ])->collapsed(),

                        Div::make([
                            Flash::make(),
                            Header::make([
                                Breadcrumbs::make($this->getPage()->getBreadcrumbs())->prepend(
                                    $this->getHomeUrl(),
                                    icon: 'home',
                                ),
                                Search::make(),
                                When::make(
                                    fn(): bool => $this->isUseNotifications(),
                                    static fn(): array => [Notifications::make()],
                                ),
                                Locales::make(),
                            ]),

                            Content::make([
                                Components::make(
                                    $this->getPage()->getComponents(),
                                ),
                            ]),

                            Footer::make()
                                ->copyright(static fn(): string
                                    => sprintf(
                                    <<<'HTML'
                                        © 2021-%d Made with ❤️ by                                      
                                            CutCode                                        
                                        HTML,
                                    now()->year,
                                ))
                                ->menu([
                                    'https:                                ]),
                        ])->class('layout-page'),
                    ]),
                ])->class('theme-minimalistic'),
            ])
                ->customAttributes([
                    'lang' => $this->getHeadLang(),
                ])
                ->withAlpineJs()
                ->withThemes(),
        ]);
    }
}

MoonShine v3 это полноценный ui kit и вы можете использовать его компоненты где угодно. Вот пример шаблона из blade:

<x-moonshine::layout>
   <x-moonshine::layout.html :with-alpine-js="true" :with-themes="true">
       <x-moonshine::layout.head>
           <x-moonshine::layout.meta name="csrf-token" :content="csrf_token()"/>
           <x-moonshine::layout.favicon />
           <x-moonshine::layout.assets>
               @vite([
                   'resources/css/main.css',
                   'resources/js/app.js',
               ], 'vendor/moonshine')
           </x-moonshine::layout.assets>
       </x-moonshine::layout.head>

       <x-moonshine::layout.body>
           <x-moonshine::layout.wrapper>
               <x-moonshine::layout.sidebar :collapsed="true">
                   <x-moonshine::layout.div class="menu-heading">
                       <x-moonshine::layout.div class="menu-heading-logo">
                           <x-moonshine::layout.logo href="/" logo="/logo.png" :minimized="true"/>
                       </x-moonshine::layout.div>

                       <x-moonshine::layout.div class="menu-heading-actions">
                           <x-moonshine::layout.div class="menu-heading-mode">
                               <x-moonshine::layout.theme-switcher/>
                           </x-moonshine::layout.div>
                           <x-moonshine::layout.div class="menu-heading-burger">
                               <x-moonshine::layout.burger/>
                           </x-moonshine::layout.div>
                       </x-moonshine::layout.div>

                   </x-moonshine::layout.div>

                   <x-moonshine::layout.div class="menu" ::class="asideMenuOpen && '_is-opened'">
                       <x-moonshine::layout.menu :elements="[['label' => 'Dashboard', 'url' => '/'], ['label' => 'Section', 'url' => '/section']]"/>
                   </x-moonshine::layout.div>

               </x-moonshine::layout.sidebar>

               <x-moonshine::layout.div class="layout-page">

                   <x-moonshine::layout.header>
                       <x-moonshine::breadcrumbs :items="['#' => 'Home']"/>
                       <x-moonshine::layout.search placeholder="Search" />
                       <x-moonshine::layout.locales :locales="collect()"/>
                   </x-moonshine::layout.header>

                   <x-moonshine::layout.content>
                       <article class="article">
                           Ваш контент
                       </article>
                   </x-moonshine::layout.content>

               </x-moonshine::layout.div>
           </x-moonshine::layout.wrapper>
       </x-moonshine::layout.body>
   </x-moonshine::layout.html>
</x-moonshine::layout>

Темы

Вместо того чтобы вручную прописывать цвета и классы для смены темы (как это было во второй версии), теперь достаточно указать, какой Layout использовать. Например, можно выбрать CompactLayout или AppLayout. В будущем добавление новых тем (например, BatmanLayout) будет таким же простым — достаточно указать его название.

final class MyLayout extends AppLayout

или

final class MyLayout extends CompactLayout

Интеграция меню, ассетов и цветов

С Layout теперь связаны не только визуальные элементы, но и меню, ассеты, цвета и набор компонентов. Это позволяет централизованно управлять всеми аспектами админ-панели.

В отличие от второй версии, где меню и другие элементы приходилось переопределять через callback-функции внутри Service Provider, теперь всё находится в одном месте. Это упрощает разработку и делает код более читаемым.

Provider как конфигурация

В новой системе Provider используется только для конфигурации. Вы объявляете настройки, и всё — никаких лишних callback-функций или сложных переопределений.

Это особенно удобно, когда нужно создать уникальный Layout для конкретной страницы или раздела админ-панели.

Только представьте что каждая страница вашей админ. панели при желании может иметь собственный шаблон, со своим меню и набором ассетов, а также собственным порядком и списком компонентов.

class CustomPage extends Page
{
   protected ?string $layout = AppLayout::class;

   // ...
}

Компоненты

С выходом MoonShine v3 количество компонентов значительно увеличилось, что связано с новой системой Layout. Теперь каждый Layout — это не просто настройка sidebar или topbar, а полноценная кастомизация страницы. Вы можете менять любые элементы, начиная от тега <HTML>, до задания Favicon или подключения ассетов для каждой страницы отдельно. Всё это делается через классы, без необходимости выносить файлы в Blade, что упрощает процесс и делает его более гибким.

Хотя такой подход может показаться немного непривычным в начале, он открывает безграничные возможности для кастомизации интерфейса. Каждый Layout может иметь собственный дизайн, что позволяет избежать использования Blade-шаблонов для кастомизаций, делая процесс удобным и интуитивно понятным.

Создание нового Layout происходит просто: вы создаёте шаблон, и в процессе настройки будет предложено сделать его дефолтным. Если вы согласитесь, этот шаблон будет автоматически прописан как основной в конфигурации. Каждый Layout может иметь свои ассеты, а панель sidebar и topbar можно быстро настроить: их можно включать или выключать по необходимости, а также использовать оба одновременно.

public function build(): Layout
{
   return Layout::make([
       Html::make([
           $this->getHeadComponent(),
           Body::make([
               Wrapper::make([
                   // $this->getTopBarComponent(),
                   $this->getSidebarComponent(),

Меню

Документация

Во-первых, у нас теперь все элементы, которые раньше были "забетонированы" (жестко зашиты в коде), стали гибкими. Теперь это ActionButton — отдельная сущность, с которой можно работать. Вы можете изменять атрибуты кнопки, например, поменять фон (background) или полностью заменить ее на другую.

Основные нововведения

1. Бесконечная вложенность меню

Теперь меню может иметь неограниченную вложенность. Неважно, сколько уровней вложенности вы создадите — система будет корректно обрабатывать и отображать их.

2. Полная кастомизация элементов меню

Если вам нужно заменить стандартные элементы, например, ActionButton, на что-то совершенно другое, это теперь возможно. Вы можете создать свою кастомную вьюху и выводить ее вместо стандартных элементов. 

3. Интеграция с пакетами и сервис-провайдерами

Если вы разрабатываете пакеты для Laravel, вы можете использовать сервис-провайдеры и метод boot, который поддерживает Dependency Injection (DI). Это позволяет внедрять основные компоненты из MoonShine, такие как AssetManager или MenuManager, и настраивать их глобально. Например, можно добавить пункты меню для всех пользователей прямо из пакета.

4. Уникальное меню для каждой страницы

Теперь каждая страница может иметь свой Layout, а значит — и уникальное меню. Это позволяет создавать специализированную навигацию для разных разделов, делая интерфейс более интуитивным и адаптивным. 

Иконки

Документация

1. Базовая настройка иконок

Теперь иконки можно указывать в упрощенном формате. Например, чтобы добавить иконку к кнопке, достаточно указать ее название, например, "users". Это работает так же, как и раньше, но теперь без необходимости использовать префикс heroicons:

->icon('cog')

Но по-настоящему прорыв при работе с иконками в MoonShine вы получите с использованием плагина MetaStorm для PHPStorm, который предоставляет автокомплит по всем иконкам системы:

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

2. Режим custom для иконок

Теперь можно использовать кастомные иконки, включая SVG, через режим custom. Это особенно полезно, если вы хотите использовать иконки из сторонних пакетов, таких как Blade Icons, или загружать свои собственные. Можно указать путь к директории с иконками, чтобы они подгружались не из вендора MoonShine, а из вашего проекта.

->icon(
    svg('path-to-icon-pack')->toHtml(),
    custom: true
)

3. Использование пакетов для иконок

Вы можете использовать пакеты, такие как Blade Icons, которые предоставляют удобные хелперы для работы с SVG. Это позволяет легко подключать иконки из таких пакетов и отображать их в интерфейсе. Также есть пакет от JeRabix для интеграции сервиса Iconify (200 тысяч Open Source иконок). И статья как работать с этим пакетом.

Asset Manager

Документация

Был значительно переработан, став более гибким и мощным инструментом для управления ассетами (CSS, JS и другими ресурсами).

Теперь Asset Manager позволяет легко подключать и использовать CSS и JS файлы. Теги link и script поддерживают атрибуты, такие как defer, а также "сахар" для упрощения их добавления. Это делает процесс подключения ассетов быстрее и удобнее, минимизируя объем кода.

use MoonShine\AssetManager\Js;

Js::make('/js/app.js');
Js::make('/js/app.js')->defer();
Js::make('/js/app.js')->customAttributes([
    'data-module' => 'main'
]);

Добавлена возможность встраивать JS и CSS прямо в код (inline), что полезно для небольших фрагментов кода, не требующих отдельного файла. Также теперь можно добавлять произвольный HTML-код, например, мета-теги или кастомные ссылки, что расширяет возможности кастомизации.

InlineJs::make(<<<'JS'
   document.addEventListener("DOMContentLoaded", function() {
       console.log("Loaded");
   });
JS);

//
use MoonShine\AssetManager\Raw;
Raw::make(' namespacesuse MoonShine\AssetManager\Raw; Raw::make('<link rel="preconnect" href="https://fonts.googleapis.com">');

use MoonShine\AssetManager\Raw;
Raw::make('<link rel="preconnect" href="https://fonts.googleapis.com">');

Asset Manager стал более гибким: ассеты можно добавлять, изменять и управлять их приоритетами через методы add, prepend и новый метод modifyAssets. Последний позволяет, например, добавлять или удалять GET-параметры, что полезно для тонкой настройки в сложных проектах.

$assetManager->modifyAssets(function($assets) {
        return array_filter($assets, function($asset) {
        return !str_contains($asset->getLink(), 'remove-this');
    });
});

Интеграция с DI (Dependency Injection) позволяет использовать Asset Manager в любом месте приложения, включая сервис-провайдеры и пакеты. Это делает его универсальным и удобным для модульной разработки.

Для локального управления ассетами добавлен метод assets и хук onLoad, которые загружают ассеты только для активного ресурса или страницы. Это оптимизирует производительность, так как ресурсы загружаются только там, где они действительно нужны. Также можно задавать условия для загрузки, например, только для страниц формы или редактирования, что делает Asset Manager более адаптивным.

use MoonShine\AssetManager\Css;
use MoonShine\AssetManager\Js;

protected function onLoad(): void
{
    parent::onLoad();

    $this->getAssetManager()
        ->add(Css::make('/css/app.css'))
        ->append(Js::make('/js/app.js'));
}

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

Сolor Manager

Документация

Позволяет легко управлять цветовой схемой вашего приложения. Настроит интерфейс под ваши нужды. 

Color Manager интегрирован в систему таким образом, что его можно использовать как через DI, так и напрямую в Layout. Это делает его универсальным инструментом, который можно применять в любом месте вашего приложения. Например, если вам нужно изменить цвет кнопки или другого элемента интерфейса, вы можете легко сделать это через Color Manager.

Можете задать собственные цвета для различных элементов, таких как кнопки, тексты или фоны, и они будут применены глобально. Например, если вы хотите изменить основной цвет (primary), достаточно задать новое значение, и все элементы, использующие этот цвет, автоматически обновятся. Это особенно удобно, если вы хотите быстро адаптировать интерфейс под новый дизайн или бренд.

$colorManager->primary('120, 67, 233');
$colorManager->secondary('236, 65, 118');
$colorManager->successBg('0, 170, 0');
$colorManager->successText('255, 255, 255');
$colorManager->warningBg('255, 220, 42');
$colorManager->warningText('139, 116, 0');
$colorManager->errorBg('224, 45, 45');
$colorManager->errorText('255, 255, 255');
$colorManager->infoBg('0, 121, 255');
$colorManager->infoText('255, 255, 255');

Color Manager также поддерживает настройку цветов по умолчанию. Это означает, что вы можете задать базовую цветовую схему, которая будет использоваться по всему приложению, но при этом при необходимости легко переопределить её для отдельных элементов. Например, вы можете изменить цвет кнопки только на одной странице, не затрагивая остальные части приложения.

Model Resource

Документация

Model Resource стала более гибкой и удобной.

Упрощение базовой структуры

Основы работы с ресурсами остались прежними: поля, таблицы и базовые методы. Однако некоторые функции, такие как import-export, были вынесены в отдельные пакеты, чтобы не перегружать ядро системы. Это сделало MoonShine более легковесным и модульным.

Поля TinyMCE и Markdown

Поля, такие как TinyMCE и Markdown, также были вынесены в отдельные пакеты. Это позволяет разработчикам подключать только те функции, которые действительно нужны в проекте. В документации подробно описано, как установить и использовать эти поля, что делает процесс быстрым и удобным.

Изменения в работе с кнопками и подтверждением действий

В третьей версии переработан механизм работы с кнопками, требующими подтверждения действий. Раньше при нажатии на кнопку запрос отправлялся сразу, а затем открывалось окно подтверждения. Это было неудобно и нелогично. Теперь запрос отправляется только после подтверждения, что делает процесс более предсказуемым и удобным.

Метод updateOnPreview

Позволяет редактировать одно конкретное поле прямо в таблице (быстрое редактирование поля). Однако теперь, если соединение с сервером пропадает, система выдает ошибку, что предотвращает потерю данных. Также добавлена возможность блокировки редактирования (с помощью "замочка"), чтобы уберечь шаловливые ручки.

Метод updateInPopover

Еще вариант редактирования одного конкретное поле прямо в таблице - в модальном окне. Это особенно полезно, если данные в таблице занимают много места или требуют более сложного редактирования. После изменения данные автоматически обновляются в таблице. Это удобно.

Валидация при обновлении данных

Раньше методы updateOnPreview и updateInPopover не учитывали правила валидации, заданные в ресурсе. Теперь валидация выполняется автоматически, и предотвращает сохранение некорректных данных.

Гибкость работы с таблицами

В третьей версии появилась возможность кастомизировать таблицы на уровне ресурса. Теперь можно легко добавлять или изменять элементы в thead, tbody и tfoot. Например, можно добавить дополнительные строки или ячейки.

Удаление компонента TD

Компонент TD был удален, так как его функциональность теперь полностью покрывается методом customWrapperAttributes - который может добавить атрибуты для ячейки прямо из поля

Text::make('Title')
   ->customWrapperAttributes([style => 'width: 200px;'])

Поле stack fields

Позволяет группировать поля в одной ячейке таблицы с учетом контекста данных. Например, в зависимости от значения данных можно отображать разные поля или компоненты. 

StackFields::make('Stack')->fields(fn(StackFields $ctx) => $ctx->getData()?->getOriginal()->id === 3 ? [
    Date::make(__('moonshine::ui.resource.created_at'), 'created_at')
        ->format("d.m.Y")
        ->sortable(),
] : [
    Date::make(__('moonshine::ui.resource.created_at'), 'created_at')
        ->format("d.m.Y")
        ->sortable(),
    LineBreak::make(),
    Email::make(__('moonshine::ui.resource.email'), 'email')
        ->sortable(),
])

Поля

Документация

MoonShine 3 значительно переработана система полей, сохраняя при этом основные концепции. В частности, динамическое отображение полей с помощью showWhen было полностью переписано.

Ранее showWhen просто скрывал поля, но данные продолжали сохраняться в запросе. Теперь же по умолчанию скрытые поля не отправляются с запросом, что позволяет избежать избыточных данных. Если же вам нужно вернуть прежнее поведение и оставить скрытые поля в запросе, вы можете указать эту настройку в конфиге.

Кроме того, добавлено новое поле — RelationRepeater. Ранее логика была встроена в JSON, что делало его громоздким и перегруженным кодом. Теперь логика перенесена в отдельное поле RelationRepeater, это упрощает структуру и улучшает читаемость кода. Теперь, если вам нужно вывести отношения типа HasMany в основной форме, вы можете использовать RelationRepeater. Это поле упрощает работу с отношениями, позволяя отображать их в табах или других местах формы.

В MoonShine 3 теперь можно легко выводить отношения, такие как HasMany и HasOne, в различных форматах, включая вкладки. Особенно полезно, когда вам нужно компактно и удобно отобразить связанные данные. Вот один из рецептов для демонстрации.

Также есть возможность в одном из табов выводить HasMany или HasOne, а в другом использовать TableBuilder, чтобы красиво представить данные в таблице. Интерфейс стал более удобным для пользователя - работать с большими объемами связанных данных стало проще.

CRUD ресурс

В MoonShine 3 появилась поддержка CRUD ресурсов, и это открывает новые возможности для работы с данными через API. В частности, вы можете использовать внешний API для получения и вывода данных и для CRUD манипуляций с ними.

При загрузке страницы данные поступают через API, и, несмотря на то, что API может быть медленным, сама страница загружается молниеносно благодаря использованию режима lazy load. Это позволяет отображать страницу сразу, а данные подгружаются уже по мере необходимости, что существенно улучшает пользовательский опыт.

Настроить такую работу можно с помощью свойства lazy, которое включается в конфиге. После этого страницы будут быстро отображаться, а API-запросы будут выполняться в фоне. Отлично подойдёт для работы с большими объемами данных и внешними сервисами.

Что касается конфигурации, то MoonShine предоставляет множество дефолтных страниц (например, дашборд, профиль, страницы с ошибками и логин). Эти страницы можно легко переопределить под собственные нужды. А команда moonshine:publish, позволяет быстро публиковать все необходимые страницы и формы. Всё сделано для того чтобы упростить настройку и уменьшить количество рутинной работы: не нужно делать всё с нуля, достаточно адаптировать готовые компоненты под ваши задачи.

С таким подходом работать с CRUD ресурсами становится не только удобно, но и быстро, при этом вы получаете полный контроль над внешним видом и функциональностью страниц.

JS

Документация

В MoonShine 3 раздел документации для работы с JavaScript стал значительно удобнее и функциональнее. Теперь, если вы работаете с фронтендом, вам не нужно разбираться, как писать классовые компоненты с нуля — все подробности объясняются прямо в документации. 

В частности, вы можете использовать Alpine.js для создания компонентов, что упрощает интеграцию с фронтендом. Компоненты инициализируются через директиву x-data, и вам нужно лишь указать название компонента. Это позволяет легко управлять состоянием и поведением компонентов, при этом все ассеты подключаются через Asset Manager.

В документация предоставлено полное описание всех событий, с которыми можно взаимодействовать через JavaScript, чтобы даже разработчик с небольшим опытом работы в JS смог правильно вызвать события и работать с ними.

SD UI

Документация

В MoonShine 3 появилась интересная возможность переключить систему в режим JSON-ответов, что открывает новые горизонты для интеграции с другими платформами. В этом режиме MoonShine будет отправлять данные в виде JSON, где каждый компонент будет представлен как объект с его состоянием, типом и прочими параметрами. Отлично подойдёт для интеграции MoonShine с мобильными приложениями или другими фронтенд-решениями.

Например, с помощью этого подхода можно создать приложение на Flutter, которое будет получать компоненты MoonShine в формате JSON. Это позволит вам изменять компоненты на лету, без необходимости пересобирать приложение. Такой подход также упрощает создание кастомных фронтендов, где вам не нужно компилировать каждую страницу, а достаточно работать с компонентами, полученными через API.

Для того чтобы использовать этот функционал, нужно отправить определенный заголовок и получить структуру, которая включает в себя все компоненты страницы, такие как Dashboard. Вы сможете увидеть, какие компоненты используются, их внутреннее состояние, а также отключить некоторые из них или изменить отображение только layout, если это необходимо.

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

API

Документация

Позволяет переключить админку в режим работы с JSON-ответами, что дает возможность интегрировать админ-панель с другими системами через API. В этом режиме все данные и взаимодействия происходят в формате JSON, что дает возможность работать с MoonShine как с API платформой.

Также добавлен пакет для генерации спецификаций OpenAPI. С его помощью вы можете легко генерировать документацию для вашего API. Процесс такой же, как и при создании ресурсов в MoonShine: вы создаёте нужные ресурсы, генерируете спецификацию и получаете документацию которая будет доступна через Swagger UI, и всё это достаточно просто настроить и использовать.

Аутентификация

Документация

Одним из интересных моментов является поддержка auth-пайплайнов (auth-pipelines). Это механизм, который используется для дополнительной обработки данных, например, после того как пользователь заполнил форму и отправил её. В процессе аутентификации или валидации данных могут быть выполнены дополнительные модификации или проверки, в зависимости от настроек.

В качестве заключения

Это только часть нововведений, которые появились в MoonShine v3. Как видите, в админке было внесено множество улучшений, а также добавлен новый функционал. Статья получилась довольно большой, при том, что я рассказал только про основные изменения. Наверное, чтобы расписать все нововведения надо будет оформить не одну такую статью. MoonShine развивается почти каждый день, и возможно в ней появились еще фичи, пока вы читали эту статью.

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

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

А если возникнут вопросы или предложения, всегда можно обсудить их в чате, где активно ведется работа над исправлением багов и улучшением функционала - https://t.me/moonshine_ru

Сейчас приступил к работе над большим видеогайдом по MoonShine v3, который поможет вам глубже разобраться в системе и максимально эффективно использовать ее возможности. Ролики добавляю в этот плейлист. Он будет регулярно наполняться, так что не забудьте подписаться на YouTube канал.

Ссылка на GitHub репозиторий, где можно ставить звёздочки:
https://github.com/moonshine-software/moonshine

А на этом всё! Всем добра! Используйте MoonShine в своих проектах и будьте счастливы!

Комментарии (11)


  1. Kudriavyi
    18.02.2025 07:15

    Я не разбираюсь в этой теме, но мне очень понравилось, что админ-панель назвали самогон.


  1. RobertGM
    18.02.2025 07:15

    только я не могу попасть на сайт проекта ?


    1. Cutcode Автор
      18.02.2025 07:15

      не открывается getmoonshine.app ?


      1. RobertGM
        18.02.2025 07:15

        все заработал )))


  1. nestran
    18.02.2025 07:15

    На протяжении многих лет использую на проектах filament. Было бы интересно увидеть сравнение фич


    1. Cutcode Автор
      18.02.2025 07:15

      Скоро сделаем сравнение. Сейчас думаем над деталями этого мероприятия. Если есть идеи, как сделать это максимально объективно - предлагайте!


  1. qeeveex
    18.02.2025 07:15

    Именно такие проекты дают надежду что PHP еще будет долго жить.


  1. wagoodoogoo
    18.02.2025 07:15

    Только что переключил проект с moonshine на filament из-за json поля типа key-value, которое не работает "из коробки" (


    1. Cutcode Автор
      18.02.2025 07:15

      Хорошо что перешли на filament! Хороший разработчик поддержит проект и напишет issue, как это делают в filament, а у нас вот так...

      P.s. Проверил Json keyValue и все работает, но это сейчас не имеет значения


    1. mihdan
      18.02.2025 07:15

      А что не работает, как воспроизвести? Не наблюдаю с этим проблем. Можно всегда написать в чат проекта или Данилу или поправить и сделать PR - вливайтесь в отечественный опенсорс


      1. Cutcode Автор
        18.02.2025 07:15

        я сразу проверил на всякий и проблем нет, по крайней мере в последней версии