Разработка кастомной страницы «Настройки» с Admiral
Время прочтения — 10 минут


В Admiral создавать кастомные страницы с формой не сложнее, чем описывать CRUD.
Для начала давайте добавим новый пункт в меню, например settings (Настройки). Такая возможность особенно полезна, когда нужно реализовать уникальный раздел, не привязанный напрямую к стандартным сущностям: например, страницу с настройками профиля, управления доступом или параметрами приложения.
$menu->addItem(MenuItem::make(MenuKey::SETTINGS, 'Настройки', 'FiSettings', '/settings'));
Далее в админке в директории создаем директорию components, если она еще не создана. В нее добавляем директорию Settings с файлом Settings.tsx.

Теперь базово опишем наш файл и ознакомимся с ним.
import React, {useCallback} from 'react'
import {Form, Page} from '@devfamily/admiral'
import api from '../../config/api'
const settingsUri = 'settings'
export default function Settings() {
const fetchInitialData = useCallback(() => {
return api.get(settingsUri)
}, [])
const _onSubmit = useCallback(async (values) => {
return api.post(settingsUri, values)
}, [])
return (
<Page title="Настройки">
<Form submitData={_onSubmit} fetchInitialData={fetchInitialData}>
// Здесь будут наши инпуты
<Form.Footer>
<Form.Submit>Сохранить</Form.Submit>
</Form.Footer>
</Form>
</Page>
)
}
Сразу посмотрим, что возвращает наш компонент. Он возвращает компонент Page с компонентом формы Form из пакета devfamily/admiral.
- В Page мы заполнили свойство title;
- В Form мы описали свойство submitData, которое вызывает пробрасываемую в него функцию, для отправки данных на сервер, по нажатию на кнопку «Сохранить», и свойство fetchInitialData, которое получает данные с сервера и заполняет наши инпуты.
Давайте посмотрим как наша страница выглядит в браузере, но для начала зарегистрируем нашу страницу. Переходим в директорию Pages и создаем директорию settings с файлом index.tsx. В нем опишем следующее:
import Settings from '@/src/components/Settings/Settings'
export default Settings
Переходим в браузер и видим нашу страницу в таком виде:

Теперь можно перейти к описанию инпутов формы.
Опишем 2 инпута:
- Простое текстовое поле
<TextInput name='min_version' label='Минимальная версия приложения'/>
- Выбор валюты с options, которые будем возвращать с сервера
<SelectInput name=’default_currency’ label='Валюта по умолчанию'/>
Итоговый компонент с двумя инпутами будет выглядеть так:
import React, {useCallback} from 'react'
import {Form, Page, SelectInput, TextInput} from '@devfamily/admiral'
import api from '../../config/api'
const settingsUri = 'settings'
export default function Settings() {
const fetchInitialData = useCallback(() => {
return api.get(settingsUri)
}, [])
const _onSubmit = useCallback(async (values) => {
return api.post(settingsUri, values)
}, [])
return (
<Page title="Настройки">
<Form submitData={_onSubmit} fetchInitialData={fetchInitialData}>
<TextInput name='min_version' label='Минимальная версия приложения'/>
<SelectInput name='default_currency' label='Валюта по умолчанию'/>
<Form.Footer>
<Form.Submit>Сохранить</Form.Submit>
</Form.Footer>
</Form>
</Page>
)
}
Нам осталось описать функции отправки API запросов на сервер, это api.get() и api.post(). Для этого нам нужно в директории src/config создать файл api.ts со следующим содержимым:
import {GetFormDataResult, UpdateResult} from '@devfamily/admiral'
import _ from './request'
import {Any} from '@react-spring/types'
const apiUrl = import.meta.env.VITE_API_URL || '/api'
type apiType = {
get: (uri: string) => Promise<GetFormDataResult>
post: (uri: string, data: Any) => Promise<UpdateResult>
}
const api: apiType = {
get: (uri) => {
const url = `${apiUrl}/${uri}`
return _.get(url)({})
},
post: (uri, data) => {
const url = `${apiUrl}/${uri}`
return _.post(url)({ data })
},
}
export default api
Здесь мы описали два метода отправки запросов на сервер в которых к нашему пробрасываемому uri (settings) добавляется адрес сервера, указанный в .env.
Запросы отправляются через уже существующие методы get() и post() из файла /src/config/request.ts. Адрес сервера в нашем случае: VITE_API_URL=http://localhost:802/admin
Теперь можем перейти к описанию роутинга и контроллера. Для хранения настроек создадим простую таблицу в базе данных. Миграция выглядит так:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('settings', function (Blueprint $table) {
$table->string('key')->unique();
$table->text('value')->nullable();
$table->timestamps();
});
DB::table('settings')->insert([
[
'key' => 'min_version',
'value' => '3.0.0',
],
[
'key' => 'default_currency',
'value' => 'RUB',
],
]);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('settings');
}
};
Создадим модель.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Setting extends Model
{
protected $fillable = [
'key',
'value',
];
}
Далее приступим к созданию контроллера, в котором будут 2 метода:
- Для возврата текущих настроек.
- Для сохранения новых значений настроек.
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Setting;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class SettingsController extends Controller
{
public function get(): JsonResponse
{
$settings = Setting::query()
->whereIn('key', ['min_version', 'default_currency'])
->get()
->keyBy('key');
return response()->json([
'data' => [
'min_version' => $settings->get('min_version')->value,
'default_currency' => $settings->get('default_currency')->value,
],
'values' => [
'default_currency' => [
[
'label' => 'Доллар',
'value' => 'USD',
],
[
'label' => 'Евро',
'value' => 'EUR',
],
[
'label' => 'Российский рубль',
'value' => 'RUB',
],
],
]
]);
}
public function store(Request $request): JsonResponse
{
Setting::query()->where('key', 'min_version')->update(['value' => $request->input('min_version')]);
Setting::query()->where('key', 'default_currency')->update(['value' => $request->input('default_currency')]);
return response()->json();
}
}
В методе get() в ключе data возвращаются текущие данные настроек. Ключи должны совпадать и атрибутами name в инпутах формы.
В ключе values передаются необходимые данные для SelectInput, в данном случае мы описали 3 опшина с валютами.
Зарегистрируем наши роутеры. В файле /routes/admin.php допишем:
Route::prefix('settings')->as('settings.')->group(function () {
Route::get('',[SettingsController::class,'get'])->name('get');
Route::post('',[SettingsController::class,'store'])->name('store');
});
Если мы перейдем в браузер и обновим страницу, то увидим как наши инпуты наполнились дефолтными значениями.

Так же, по нажатию на кнопку «Сохранить» данные отправляются на сервер.

Таким же образом можно пользоваться всеми остальными инпутами из пакета devfamily/admiral.