Orchid — административная панель для Laravel, построенная на классах-экранах вместо генерации CRUD по модели. Подход отличается от привычного по другим панелям, и это стоит понять до начала: здесь вы описываете экран целиком — какие данные загрузить, какие блоки показать, какие действия доступны, — а не настраиваете готовый ресурс.
Разберём основы и, отдельно, для каких задач этот инструмент подходит, а для каких — нет.
Что такое Orchid
Orchid Platform — это пакет для Laravel, который превращает создание админ-панелей из рутины в удовольствие. Вместо написания CRUD-контроллеров, views и форм вы описываете экраны декларативно.
Преимущества:
- Готовый UI на основе Bootstrap 5
- Декларативные экраны (Screens)
- Встроенные компоненты: таблицы, формы, фильтры
- Гибкая система прав
- Уведомления, виджеты, графики
Orchid vs Nova/Filament: Orchid бесплатный и open-source. Nova платная ($199). Filament тоже бесплатный, но Orchid более гибкий для кастомной логики.
Установка
# Новый проект Laravel
composer create-project laravel/laravel admin-panel
cd admin-panel
# Установка Orchid
composer require orchid/platform
# Публикация ресурсов
php artisan orchid:install
# Создание админа
php artisan orchid:admin admin admin@example.com password# .env
APP_URL=http://localhost:8000php artisan serve
# Админка: http://localhost:8000/adminСтруктура проекта
После установки появляются:
app/
├── Orchid/
│ ├── Layouts/ # Компоненты таблиц и форм
│ ├── Screens/ # Экраны (страницы админки)
│ ├── Presenters/ # Форматирование данных
│ └── PlatformProvider.php # Регистрация менюПервый Screen
Создадим экран списка статей:
php artisan orchid:screen PostListScreen// app/Orchid/Screens/PostListScreen.php
namespace App\Orchid\Screens;
use App\Models\Post;
use Orchid\Screen\Screen;
use Orchid\Screen\Actions\Link;
use Orchid\Support\Facades\Layout;
use Orchid\Screen\TD;
class PostListScreen extends Screen
{
/**
* Название экрана
*/
public ?string $name = 'Статьи';
/**
* Описание
*/
public ?string $description = 'Управление статьями блога';
/**
* Получение данных для экрана
*/
public function query(): iterable
{
return [
'posts' => Post::filters()->defaultSort('created_at', 'desc')->paginate(),
];
}
/**
* Кнопки в header
*/
public function commandBar(): iterable
{
return [
Link::make('Создать статью')
->icon('plus')
->route('platform.post.edit'),
];
}
/**
* Layouts — что отображаем
*/
public function layout(): iterable
{
return [
Layout::table('posts', [
TD::make('id', 'ID')
->sort()
->width('80px'),
TD::make('title', 'Заголовок')
->sort()
->filter()
->render(fn(Post $post) => Link::make($post->title)
->route('platform.post.edit', $post)),
TD::make('status', 'Статус')
->sort()
->render(fn(Post $post) => $post->is_published
? '<span class="badge bg-success">Опубликована</span>'
: '<span class="badge bg-secondary">Черновик</span>'),
TD::make('created_at', 'Создана')
->sort()
->render(fn(Post $post) => $post->created_at->format('d.m.Y H:i')),
]),
];
}
}Экран редактирования
php artisan orchid:screen PostEditScreen// app/Orchid/Screens/PostEditScreen.php
namespace App\Orchid\Screens;
use App\Models\Post;
use Orchid\Screen\Screen;
use Orchid\Screen\Actions\Button;
use Orchid\Support\Facades\Layout;
use Orchid\Screen\Fields\Input;
use Orchid\Screen\Fields\TextArea;
use Orchid\Screen\Fields\Switcher;
use Orchid\Screen\Fields\Quill;
use Orchid\Screen\Fields\Upload;
use Orchid\Support\Facades\Toast;
use Illuminate\Http\Request;
class PostEditScreen extends Screen
{
public ?Post $post = null;
public function query(Post $post): iterable
{
return [
'post' => $post,
];
}
public function name(): ?string
{
return $this->post->exists
? 'Редактирование: ' . $this->post->title
: 'Создание статьи';
}
public function commandBar(): iterable
{
return [
Button::make('Сохранить')
->icon('save')
->method('save'),
Button::make('Удалить')
->icon('trash')
->method('remove')
->canSee($this->post->exists)
->confirm('Вы уверены, что хотите удалить статью?'),
];
}
public function layout(): iterable
{
return [
Layout::rows([
Input::make('post.title')
->title('Заголовок')
->required()
->placeholder('Введите заголовок'),
Input::make('post.slug')
->title('URL (slug)')
->placeholder('avtomaticheski-iz-zagolovka'),
TextArea::make('post.excerpt')
->title('Краткое описание')
->rows(3),
Quill::make('post.content')
->title('Содержание'),
Upload::make('post.image')
->title('Изображение')
->maxFiles(1)
->acceptedFiles('image/*'),
Switcher::make('post.is_published')
->title('Опубликовать')
->sendTrueOrFalse(),
]),
];
}
public function save(Post $post, Request $request): void
{
$data = $request->validate([
'post.title' => 'required|string|max:255',
'post.slug' => 'nullable|string|max:255',
'post.excerpt' => 'nullable|string',
'post.content' => 'nullable|string',
'post.is_published' => 'boolean',
]);
$post->fill($data['post']);
if (empty($post->slug)) {
$post->slug = \Str::slug($post->title);
}
$post->save();
Toast::success('Статья сохранена');
}
public function remove(Post $post): void
{
$post->delete();
Toast::info('Статья удалена');
}
}Регистрация роутов
// routes/platform.php
use App\Orchid\Screens\PostListScreen;
use App\Orchid\Screens\PostEditScreen;
Route::screen('posts', PostListScreen::class)
->name('platform.post.list');
Route::screen('posts/{post?}', PostEditScreen::class)
->name('platform.post.edit');Меню
// app/Orchid/PlatformProvider.php
namespace App\Orchid;
use Orchid\Platform\Dashboard;
use Orchid\Platform\OrchidServiceProvider;
use Orchid\Screen\Actions\Menu;
class PlatformProvider extends OrchidServiceProvider
{
public function menu(): array
{
return [
Menu::make('Главная')
->icon('home')
->route('platform.main'),
Menu::make('Статьи')
->icon('docs')
->route('platform.post.list')
->badge(fn() => Post::where('is_published', false)->count()),
Menu::make('Пользователи')
->icon('people')
->route('platform.systems.users')
->permission('platform.systems.users'),
Menu::make('Настройки')
->icon('settings')
->list([
Menu::make('Роли')
->icon('shield')
->route('platform.systems.roles'),
]),
];
}
}Модель с Orchid traits
// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Orchid\Filters\Filterable;
use Orchid\Screen\AsSource;
use Orchid\Attachment\Attachable;
class Post extends Model
{
use AsSource, Filterable, Attachable;
protected $fillable = [
'title',
'slug',
'excerpt',
'content',
'is_published',
];
protected $casts = [
'is_published' => 'boolean',
];
protected $allowedSorts = [
'id',
'title',
'created_at',
];
protected $allowedFilters = [
'title',
];
}Не забудьте миграцию!
php artisan make:migration create_posts_table
php artisan migrateРезультат
За 30 минут мы получили:
- Список статей с сортировкой и пагинацией
- Форму создания и редактирования
- WYSIWYG-редактор
- Загрузку изображений
- Удаление с подтверждением
- Toast-уведомления
- Меню со счётчиком
Итоги
| Компонент | Назначение |
|---|---|
| Screen | Страница админки |
| Layout | Структура экрана |
| TD | Колонка таблицы |
| Fields | Поля форм |
| Actions | Кнопки и ссылки |
| Toast | Уведомления |
Когда Orchid — правильный выбор
Стоит сказать прямо, потому что выбор админки определяет работу на годы вперёд.
Подходит для внутренних инструментов: панели управления контентом, административной части сервиса, интерфейсов для сотрудников. Экраны описываются кодом, значит живут в репозитории, проходят ревью и разворачиваются вместе с приложением — для командной работы это существенное преимущество перед настройкой через интерфейс.
Не подходит в качестве личного кабинета для клиентов. Админка — это админка: её внешний вид, поведение и логика прав рассчитаны на сотрудников. Попытка сделать из неё пользовательский интерфейс заканчивается борьбой с фреймворком.
Подумайте дважды, если проекту нужен сильно нестандартный интерфейс. Orchid хорош, пока вы работаете в его модели — экраны, слои, поля. Как только требуется что-то принципиально другое, объём кастомизации быстро превышает стоимость обычных контроллеров с Blade-шаблонами.
И оцените связанность. Экраны, поля, права и меню — всё это код, зависящий от конкретного пакета. Смена админки через два года будет означать переписывание всего этого слоя. Это нормальная плата за скорость разработки, но принимать её лучше осознанно.
Следующие шаги
- Продвинутые layouts: вкладки, аккордеоны, модальные окна
- Фильтры и сортировка — вместе с индексами под них
- Собственные поля — когда встроенных действительно не хватает
- Права и роли — и понимание, что скрытая кнопка не равна закрытому действию
Комментарии
Система комментариев скоро будет подключена. А пока вы можете написать мне в Telegram или на email.