Групування декількох boolean атрибутів у Laravel Nova


Групування декількох boolean атрибутів у Laravel Nova

Laravel Nova встановлюється з великим та дивовижним списком полів. Ці поля за замовчуванням досить розумні, і вони підходять майже для будь-якої ситуації. Але що, якщо у вас ситуація, коли інтерфейс відповідає вашим потребам, але поля обробляють дані по-різному. Ви змушені створювати власне поле? Ну, можливо, ні.


Налаштуйте власне поле

Поля Laravel Nova мають безліч методів, що дозволяють налаштувати їх поведінку. Як ви можете прочитати в документах, ви можете налаштувати спосіб гідратації відповідної моделі, приєднавши зворотний виклик fillUsing (). І ви можете налаштувати спосіб розв’язання поля, приєднавши зворотний виклик resolUsing (). Тож давайте використаємо ці функції та створимо своє власне «спеціальне» поле.

Групування кількох boolean символів у групу BooleanGroup 

Уявіть, що у вас є модель повідомлення, яка представляє повідомлення, яке може відображатися у декількох областях, таких як: веб-сайт, програма та RSS. Ви хочете легко запитувати ці повідомлення за їх обсягом, тому ви додали 3 логічні поля: scope_website, scope_app і scope_rss.

Так, ви могли б скласти категорії та створити зведену таблицю; але у вас були лише ці сфери, і тоді ви згадали, що це був приклад ..

 

<?php

namespace App\Nova\Resources;

use App\Nova\Resource;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\BooleanGroup;

class MessageResource extends Resource
{
    // ...

    public function fields(Request $request): array
    {
        return [
            BooleanGroup::make('Scopes')->options($options = [
                'scope_website' => 'Website',
                'scope_app' => 'Application',
                'scope_rss' => 'RSS Feed',
            ])
        ];
    }
}

 

Зміна атрибутів моделі 
 

Замість того, щоб додавати 3 різні поля до Ресурсу Nova, ми хочемо додати BooleanGroup цих областей. За замовчуванням поле BooleanGroup зберігатиме свої параметри у вигляді BLOB-масиву на одному атрибуті. Щоб змінити цю поведінку, ми збираємося налаштувати гідратацію нашого поля, викликавши метод fillUsing за допомогою цієї функції зворотного виклику: 

use App\Model\Message;
use Laravel\Nova\Http\Requests\NovaRequest;

// this function goes inside ->fillUsing(...)
function (NovaRequest $request, Message $model, string $attribute, string $requestAttribute) {
    // Make sure the `scopes` value exists on the request.
    if (!$request->exists($requestAttribute)) {
        return;
    }
    // Decode the values because it is send as a JSON blob.
    $values = json_decode($request[$requestAttribute], true);
    // Hydrate the model.
    foreach ($values as $key => $value) {
        $model->{$key} = $value;
    }
}

Функція зворотного виклику для fillUsing () отримує 4 параметри: 
 

Переконавшись, що ми опублікували значення, ми можемо зіставити всі ці параметри з їх атрибутом моделі. Ефективно називаючи щось на зразок:

$model->scope_website = 1; // or 0

 

Встановлення значення для поля 

Тепер, коли ми фактично зберігаємо логічні значення за правильними атрибутами, настав час поглянути на розв’язання поля в Nova. Хоча для моделі можуть бути встановлені сфери дії, усі прапорці будуть вимкнені. Це тому, що поле все ще намагається отримати їх значення з $ model-> scopes; який не існує. Тож дозволяє це виправити, додавши дозвіл зворотного виклику за допомогою

Ви могли помітити, що ми визначили наші параметри поля всередині змінної $ options. Це було навмисно, бо нам потрібні ключі від цих варіантів. На цей час метод callUlbackback не має доступу до самого поля для отримання цих опцій. Це наш обхідний шлях.


// this function goes inside ->resolveUsing(...)
function ($value, Message $model, string $attribute) use ($options) {
    $keys = array_keys($options);
    $values =  array_map(function($value, $key) use ($model) {
        return $model->{$key};
    }, $options, $keys);

    return array_combine($keys, $values);
}

 Функція зворотного виклику для резолюції Using () отримує 3 параметри: 
 

 
Єдине, що потрібно зробити для нашого зворотного виклику, це отримати значення для кожного логічного значення з модель і повернути ці значення у вигляді масиву. Цей код трохи безлад. Побачити, що відбувається, непросто, і нам потрібно використовувати значення в межах декількох областей функцій. Давайте трохи очистимо це, використовуючи скорочені функції і деякі laravel збирають магію:

fn($value, Message $model) => collect($options)->map(fn($value, $key) => $model->{$key})

Ось, хороший однокласник, який вирішує поле з правильних атрибутів моделі. Коли ви оновлюєте свою сторінку Nova, прапорці повинні правильно вказувати їх статус. 

Бонус: зробіть це поле обов’язковим 

Для розваги, припустимо, вам потрібно вибрати принаймні один обсяг . Вашим першим інстинктом може бути просто встановити -> required () на полі, але це насправді не працює, хоча це дасть гарну зірочку * на формі. На щастя, ми також можемо додати власне правило перевірки, викликавши в полі метод rules (). 

 $field->rules('required', function (string $attribute, $value, callable $fail) {
    if (!array_filter(json_decode($value, true) ?? [])) {
       return $fail(sprintf('The "%s" field must have at least one option selected.', $attribute));
    }
})

Встановлення необхідного правила `required` також додасть зірочку `*` до форми. Зворотний дзвінок швидко перевіряє, чи було повернуто якесь значення як істинне. Якщо ні; ми називаємо виклик `$fail` і вказуємо причину невдалої перевірки.
 
І все; власне поле, фактично не будуючи власне поле. 

Джерело