---
title: 'Сортировка по json-полю в таблицах Laravel Orchid'
url: 'https://graker.ru/news/2025/11/22/laravel-orchid-sort-table-by-json-field'
language: ru
type: post
author: 'Роман Грачев'
published: '2025-11-22T01:30:00+03:00'
updated: '2026-11-07T18:53:04+03:00'
categories:
  - Программирование
  - Laravel
translations:
  en: 'https://graker.ru/en/news/2025/11/22/laravel-orchid-sort-table-by-json-field.md'
---

# Сортировка по json-полю в таблицах Laravel Orchid

Спешно прикручиваю к своей туристической CRMке нормальную админку (которая для админов). Раньше была самописная с фронтом на Vue, из-за этого добавление любой новой страницы или функции требовало слишком много времени. Собрался с мыслями, решил наконец заменить её на [Laravel Orchid](https://orchid.software/). Дело пошло значительно быстрее, и в целом могу констатировать очевидное: как бы ни было прикольно играться с фронтом на Vue, какая бы небольшая ни была админка — лучше конечно сразу брать готовую панельку. А с фронтом играться в пользовательской части.

В процессе переноса столкнулся с неожиданной трудностью. В Orchid есть вывод моделей в таблицы, который довольно легко и удобно собирается, при этом столбцы таблицы можно сделать фильтруемыми и сортируемыми — тоже легко. Вот, например, объявили колонку «Название» с сортировкой и фильтром (по фрагменту текста):

```php
    TD::make('name', 'Название')
        ->sort()
        ->filter(Input::make()),
```

И всё отлично, но в одной из таких таблиц у меня выводятся теги (а именно — страны). Теги сделаны на библиотеке [spatie/laravel-tags](https://github.com/spatie/laravel-tags), и в них названия (name) — это поля json, так сделано для поддержки локализации. То есть внутри поля названия будет `{"ru": "Россия", "en": "Russia"}` и т.д.

У меня язык только русский, но эта структура плотно и давно используется, отказываться от неё сейчас не хочется. Но, разумеется, без проблем с ней не обошлось. Дело в том, что фильтр для поиска тегов по названию работает сразу, из коробки (что логично). А вот с сортировкой получается ерунда, без подстановки `->ru` она не работает. Если же использовать `TD::name('name->ru'...)` (и отрендерить значение поля вручную) — получается наоборот. Сортировка работает, а фильтр нет.

Но мы-то хотим, чтобы работало всё. Далее два решения: костыль и более элегантный вариант.

## Костыль

Первым делом поковырялся в коде класса, который размечает колонки таблицы, и нашел код сортировки. Написал свой наследный класс, где в сортировку принудительно добавляется `->ru`, а также передал в шаблон построения заголовка отдельную переменную `sort_column` — чтобы шаблон знал, когда надо рисовать иконку сортировки по данному столбцу.

```php
<?php

namespace App\Orchid\Screen;

use Illuminate\View\View;
use Orchid\Screen\TD;

class JSONSortedTD extends TD
{
    public function buildSortUrl(): string
    {
        $query = request()->collect()->put(
            'sort', 
            revert_sort($this->column . '->ru') // <- добавили суффикс
        )->toArray();
        return url()->current() . '?' . http_build_query($query);
    }

    public function buildTh()
    {
        return view('platform::partials.layouts.th', [
            'width'        => is_numeric($this->width) ? $this->width . 'px' : $this->width,
            'align'        => $this->align,
            'sort'         => $this->sort,
            'sortUrl'      => $this->buildSortUrl(),
            'column'       => $this->column,
            'sort_column'  => $this->column . '->ru', // <- добавили суффикс    
            'title'        => $this->title,
            'filter'       => $this->buildFilter(),
            'filterString' => $this->buildFilterString(),
            'slug'         => $this->sluggable(),
            'popover'      => $this->popover,
        ]);
    }

}
```

Переопределил шаблон Orchid `platform/partials/layouts/th.blade.php`, чтобы использовать новую переменную с суффиксом, если она задана.

```markup
@if(is_sort($sort_column ?? $column))
    @php $sortIcon = get_sort($sort_column ?? $column) === 'desc' ? 'bs.sort-down' : 'bs.sort-up' @endphp
    <x-orchid-icon :path="$sortIcon"/>
@endif
```

И вместо TD в табличном лейауте указал наш новый класс:

```php
JSONSortedTD::make('name', 'Название')
    ->sort()
    ->filter(Input::make()),
```

Такое себе решение, но работает.

## Решение с помощью фильтра

Поспрашивал в [канале Orchid](https://t.me/orchid_russian_community), присоветовали вместо костыля выше написать фильтр (см. [Eloquent Filters](https://orchid.software/en/docs/filters/)). Попробовал, и действительно получилось гораздо лучше.

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

```php
<?php

namespace App\Orchid\Filters;

use Illuminate\Database\Eloquent\Builder;
use Orchid\Filters\Filter;
use Orchid\Screen\Field;

class JsonSortFilter extends Filter
{
    public function name(): string
    {
        return '';
    }

    public function parameters(): ?array
    {
        // реагируем на sort в query
        return ['sort'];
    }

    public function run(Builder $builder): Builder
    {
        $sort = $this->request->get('sort');
        // если в sort — наш параметр, используем его для сортировки запроса
        if ($sort == 'name') {
            return $builder->orderBy('name->ru', 'asc');
        } else if ($sort == '-name') {
            return $builder->orderBy('name->ru', 'desc');
        }
        // а если что-то другое, то ничего не делаем
        return $builder;
    }

    public function display(): iterable
    {
        // никаких визуальных полей нашему фильтру не нужно
        return [];
    }
}
```

И теперь в `query()` экрана со списком тегов можно добавить новый фильтр:

```php
    $tags = DirectionTag::filters([JsonSortFilter::class])->defaultSort('name->ru')->paginate(40);
```

Прописывать в `TD::...` дополнительно ничего не надо. Уже работает. Но вызов `->sort()` в объявлении колонки надо оставить, чтобы сортировка происходила по клику на название колонки, и чтобы иконка сортировки появлялась.

Результат с включенным фильтром и сортировкой:

![json_filtered.png](https://graker.ru/sites/default/files/styles/max_1300x1300/public/uploads/2025-11/json_filtered.png.webp?itok=D9mnUIkm)

P.S. У меня везде добавляется явно `->ru`, поскольку язык только русский. Но параметризовать это, конечно же, не проблема.
