/home/bdqbpbxa/api-uniferx.goodface.com.ua/vendor/laravel/nova/src/Fields/MultiSelect.php
<?php

namespace Laravel\Nova\Fields;

use Illuminate\Support\Arr;
use Laravel\Nova\Contracts\FilterableField;
use Laravel\Nova\Fields\Filters\MultiSelectFilter;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Util;

/**
 * @phpstan-type TOptionLabel \Stringable|string|array{label: string, group?: string}
 * @phpstan-type TOptionValue string|int
 * @phpstan-type TOption iterable<TOptionValue, TOptionLabel>
 */
class MultiSelect extends Field implements FilterableField
{
    use FieldFilterable, SupportsDependentFields;

    /**
     * The field's component.
     *
     * @var string
     */
    public $component = 'multi-select-field';

    /**
     * The field's options callback.
     *
     * @var array<string|int, array<string, mixed>|string>|\Closure|callable|\Illuminate\Support\Collection|null
     *
     * @phpstan-var TOption|(callable(): (TOption))|(\Closure(): (TOption))|null
     */
    public $optionsCallback;

    /**
     * Set display using label for the field.
     *
     * @var bool
     */
    public $displayUsingLabel = false;

    /**
     * Set the options for the select menu.
     *
     * @param  array<string|int, array<string, mixed>|string>|\Closure|callable|\Illuminate\Support\Collection  $options
     * @return $this
     *
     * @phpstan-param TOption|(callable(): (TOption))|(\Closure(): (TOption)) $options
     */
    public function options($options)
    {
        $this->optionsCallback = $options;

        return $this;
    }

    /**
     * Display values using their corresponding specified labels.
     *
     * @return $this
     */
    public function displayUsingLabels()
    {
        $this->displayUsingLabel = true;

        return $this;
    }

    /**
     * Hydrate the given attribute on the model based on the incoming request.
     *
     * @param  \Laravel\Nova\Http\Requests\NovaRequest  $request
     * @param  string  $requestAttribute
     * @param  \Illuminate\Database\Eloquent\Model|\Laravel\Nova\Support\Fluent  $model
     * @param  string  $attribute
     * @return mixed
     */
    protected function fillAttributeFromRequest(NovaRequest $request, $requestAttribute, $model, $attribute)
    {
        if ($request->exists($requestAttribute)) {
            $value = $request[$requestAttribute];

            $model->{$attribute} = $this->isValidNullValue($value) ? null : json_decode($value, true);
        }
    }

    /**
     * Make the field filter.
     *
     * @param  \Laravel\Nova\Http\Requests\NovaRequest  $request
     * @return \Laravel\Nova\Fields\Filters\Filter
     */
    protected function makeFilter(NovaRequest $request)
    {
        return new MultiSelectFilter($this);
    }

    /**
     * Define the default filterable callback.
     *
     * @return callable(\Laravel\Nova\Http\Requests\NovaRequest, \Illuminate\Database\Eloquent\Builder, mixed, string):\Illuminate\Database\Eloquent\Builder
     */
    protected function defaultFilterableCallback()
    {
        return function (NovaRequest $request, $query, $value, $attribute) {
            return $query->whereJsonContains($attribute, $value);
        };
    }

    /**
     * Prepare the field for JSON serialization.
     *
     * @return array
     */
    public function serializeForFilter()
    {
        return transform($this->jsonSerialize(), function ($field) {
            return Arr::only($field, [
                'uniqueKey',
                'name',
                'attribute',
                'options',
            ]);
        });
    }

    /**
     * Serialize options for the field.
     *
     * @return array<int, array<string, mixed>>
     *
     * @phpstan-return array<int, array{group: string, label: string, value: TOptionValue}>
     */
    protected function serializeOptions()
    {
        /** @var TOption $options */
        $options = value($this->optionsCallback);

        if (is_callable($options)) {
            $options = $options();
        }

        return collect($options ?? [])->map(function ($label, $value) {
            $value = Util::safeInt($value);

            return is_array($label) ? $label + ['value' => $value] : ['label' => $label, 'value' => $value];
        })->values()->all();
    }

    /**
     * Prepare the field for JSON serialization.
     *
     * @return array<string, mixed>
     */
    public function jsonSerialize(): array
    {
        $this->withMeta([
            'options' => $options = $this->serializeOptions(),
        ]);

        if ($this->displayUsingLabel === true) {
            $this->displayUsing(function ($value) use ($options) {
                return collect($options)
                        ->where('value', $value)
                        ->first()['label'] ?? $value;
            });
        }

        return parent::jsonSerialize();
    }
}