Макеты экрана



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

Разделение логики и презентации является один из принципов разработки с ORCHID. Одним из элементов презентации являются «Layouts» (макеты) которые могут отображаться в различных вариациях, если попытаться обьяснить коротко, то получиться, что это view на стеройдах.

Подход через макеты

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

<div class="d-sm-flex flex-row flex-wrap text-center text-sm-left align-items-center">
    <span class="thumb-sm avatar m-r-xs">
        <img src="/avatar/maria.jpg" class="bg-light" alt="Maria">
    </span>
    <div class="ml-sm-3 ml-md-0 ml-xl-3 mt-2 mt-sm-0 mt-md-2 mt-xl-0">
        <h6 class="mb-0">Maria</h6>
        <p class="text-muted mb-1">maria@exaple.com</p>
    </div>
</div>

Простое отображение блока с профилем может фигурировать на десятках страниц и если они скопированы, то поддерживать их внешний вид может потребовать много времени, по этому прорабатываются различные варианты повторного использования. Это называется компонентным подходом, вне зависимости от способа доставки и уровня ответственности, практикуется как в Blade так и в React/Vue/Angular.

Именно из таких компонентов и состоят слои платформы, явным отличием является только, что необходимо оперировать именно классами, создавая которые вы явно опеределяете, что принятый параметр avatar будет вставлен в тег <img>, без необходимости каждый раз править исходный код.

Таблица

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

php artisan orchid:table PatientListLayout

Пример:

namespace App\Layouts\Clinic\Patient;

use Orchid\Screen\TD;
use Orchid\Screen\Layouts\Table;

use Orchid\Platform\Http\Filters\SearchFilter;
use App\Http\Filters\LastNamePatient;

class PatientListLayout extends Table
{

    /**
     * @var string
     */
    public $data = 'patients';

    /**
     * @return array
     */
    public function fields() : array
    {
        return [
            TD::set('last_name','Last name')
                ->align('center')
                ->width('100px')
                ->render(function ($patient) {
                    return '<a href="' . route('platform.clinic.patient.edit',
                            $patient->id) . '">' . $patient->last_name . '</a>';
                }),

            TD::name('first_name')
                ->title('First Name')
                ->sort()
                ->link('platform.clinic.patient.edit', 'id'),

            TD::set('phone','Phone')
                ->loadModalAsync('oneAsyncModal', 'savePhone', 'id', 'phone'),

            TD::set('email','Email'),

            TD::set('created_at','Date of publication'),

        ];
    }
}

Доступные методы

  • Метод align() горизонтальное выравнивание текста, принимает значения: 'left’, 'center’, 'right’

  • Метод link($route,$key) добавляет в ячейку ссылку, например на редактирование данной записи.

  • Метод linkPost($text) добавляет в ячейку ссылку на текущий пост (используется в списке постов).

  • Метод locale() отображение данных столбца согласно текущему языку локали.

  • Метод loadModalAsync($modal, $method, $options, $text) добавляет модальное окно к каждой ячейке столбца. Где атрибуты $modal – название модального окна, $method – метод (функция) который отправляет данные через POST запрос, $options дополнителиные атрибуты, например id или slug, $text – отображаемое значение ячейки.

  • Метод name($key) устанавливает имя ключа из массива значения которого отображать в таблице.

  • Метод set($key, $name) основной метод устанавливает имя ключа из массива и отображаемое название. Заменяет методы name() и title().

  • Метод render(function ($item) { return $item->id}) возможность генерации ячейки согласно функции. В $item передаются данные текущей строки.

  • Метод sort() добавляет в заголовок возможность сортировки по данному столбцу.

  • Метод title($name) устанавливает заголовок столбца.

  • Метод width() явно задает ширину столбца width('100px')

Строки

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

Для создания исполните команду:

php artisan orchid:rows PatientFirstRows

Пример:

namespace App\Layouts\Clinic\Patient;

use App\Http\Widgets\AppointmentTypes;
use Orchid\Screen\Field;
use Orchid\Platform\Layouts\Rows;

class Appointment extends Rows
{

    /**
     * @return array
     *
     * @throws \Orchid\Press\EntityTypeException
     */
    public function fields(): array
    {
        return [

            DateTimer::make()
                ->name('appointment_time')
                ->required()
                ->title('Time'),

            Relationship::make()
                ->name('appointment_type')
                ->required()
                ->title('Appointment type')
                ->handler(AppointmentTypes::class),

            TextArea::make()
                ->name('doctor_notes')
                ->rows(10)
                ->required()
                ->title('Doctor notes')
                ->help('What did the patient complain about?'),

        ];
    }
}

Строки поддерживают короткую запись без создания отдельного класса, например, когда требуется показать одно – два поля

/**
 * Views.
 *
 * @return array
 * @throws \Throwable
 */
public function layout(): array
{
    return [
        Layout::rows([
           Input::make('example')
                ->type('text')
                ->title('Example')
        ]),
    ];
}

Графики

Макет графиков удобный способ графически отображать динамику значений, но он требует некоторой обработки данных, пример данных из query

/**
 * Query data
 *
 * @param Patient $patient
 *
 * @return array
 */
public function query($patient = null) : array
{
    $charts = [
        [
            'title'  => "Some Data",
            'values' => [25, 40, 30, 35, 8, 52, 17, -4],
        ],
        [
            'title'  => "Another Set",
            'values' => [25, 50, -10, 15, 18, 32, 27, 14],
        ],
        [
            'title'  => "Yet Another",
            'values' => [15, 20, -3, -15, 58, 12, -17, 37],
        ],
    ];

    return [
        'charts' => $charts,
    ];
}

Для создания исполните команду:

php artisan orchid:chart ChartsLayout

Пример макета:

namespace App\Layouts\Clinic\Patient;

use Orchid\Platform\Layouts\Chart;

class ChartsLayout extends Chart
{

    /**
     * @var string
     */
    public $title = 'DemoCharts';

    /**
     * @var int
     */
    public $height = 150;

    /**
     * Available options:
     * 'bar', 'line', 
     * 'pie', 'percentage'
     *
     * @var string
     */
    public $type = 'scatter';

    /**
     * @var array
     */
    public $labels = [
        "12am-3am",
        "3am-6am",
        "6am-9am",
        "9am-12pm",
        "12pm-3pm",
        "3pm-6pm",
        "6pm-9pm",
        "9pm-12am",
    ];

    /**
     * @var string
     */
    public $data = 'charts';
}

Набор фильтров

Для группировки фильтров, их спроса и применения, существует отдельный слой Selection, в котором они указываются.

Для создания исполните команду:

php artisan orchid:selection MySelection

Пример класса:

namespace App\Orchid\Layouts;

use Orchid\Platform\Filters\Filter;
use Orchid\Press\Http\Filters\CreatedFilter;
use Orchid\Press\Http\Filters\SearchFilter;
use Orchid\Screen\Layouts\Selection;

class MySelection extends Selection
{
    /**
     * @return Filter[]
     */
    public function filters(): array
    {
        return [
          SearchFilter::class,
          CreatedFilter::class
        ];
    }
}

Табы

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

/**
 * Views.
 *
 * @return array
 * @throws \Throwable
 */
public function layout(): array
{
    return [
        Layout::tabs([
            'Example Tab Table' => TableExample::class,
            'Example Tab Rows'  => RowExample::class,
        ]),
    ];
}

Название вкладок будет соответствовать ключам массива

Столбцы

Аналогично табам:

/**
 * Views.
 *
 * @return array
 * @throws \Throwable
 */
public function layout(): array
{
    return [
        Layout::columns([
           TableExample::class,
           RowExample::class,
        ]),
    ];
}

Раскрывающийся список

/**
 * Views.
 *
 * @return array
 * @throws \Throwable
 */
public function layout(): array
{
    return [
        Layout::collapse([
            Input::make('name')
                ->type('text')
                ->title('Name Articles')
        ])->label('More'),
    ];
}

Аккордеон

/**
 * Views.
 *
 * @return array
 */
public function layout(): array
{
    return [
        Layout::accordion([
            'Personal Information' => [
                Layout::rows([
                    Input::make('user.name')
                        ->type('text')
                        ->required()
                        ->title('Name')
                        ->placeholder('Name'),

                    Input::make('user.email')
                        ->type('email')
                        ->required()
                        ->title('Email')
                        ->placeholder('Email'),
                ]),
            ],
            'Billing Address'      => [
                Layout::rows([
                    Input::make('address')
                        ->type('text')
                        ->required()
                        ->title('Адрес доставки')
                        ->placeholder('Ул. Ленина дом 14 оф.162'),
                ]),
            ],
        ]),
    ];
}

Пользовательский шаблон

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

/**
 * Views.
 *
 * @return array
 */
public function layout(): array
{
    return [
        Layout::view('myTemplate'),
    ];
}

Все данные из метода query будут переданы в ваш шаблон.

Обертка

Промежуточным звеном между «Пользовательским шаблоном» и стандартными слоями может служить «Обёртка», с помощью которой доступно указывать где именно должны отображаться другие слои.

/**
 * Views.
 *
 * @return array
 */
public function layout(): array
{
    return [
        Layout::wrapper('myTemplate', [
            'foo' => [
                RowLayout::class,
                RowLayout::class,
            ],
            'bar' => RowLayout::class,
        ]),
    ];
}

В шаблон myTemplate будут переданы переменные foo и bar, которые содержат уже собранные View, которые можно выводить:

<div class="row">
    <div class="col-7 border-right">
        @foreach($foo as $row)
            {!! $row !!}
        @endforeach
    </div>
    <div class="col-5 no-gutter">
        {!! $bar !!}
    </div>
</div>

Переменные из query так же доступны к шаблоне.

Расширение слоёв

Класс Layouts является группирующим нескольких различных, для того, что бы добавить в него новую возможность достаточно указать её в сервис провайдере как:

use Orchid\Screen\Layouts\Base;
use Orchid\Screen\Repository;
use Orchid\Screen\Layout;

Layout::macro('hello', function (string $name) {
    return new class($name) extends Base
    {
        /**
         * @ string
         */
        public $name;

        /**
         * Hello constructor.
         *
         * @param string $name
         */
        public function __construct(string $name)
        {
            $this->name = $name;
        }

        /**
         * @param Repository $repository
         *
         * @return mixed
         */
        public function build(Repository $repository)
        {
            return view('hello')->with('name', $this->name);
        }

    };
});

Тогда в экране вызов будет выглядеть как:

/**
 * Views.
 *
 * @return array
 */
public function layout(): array
{
    return [
        Layout::hello('Alexandr Chernyaev')
    ];
}