212 lines
4.1 KiB
PHP
212 lines
4.1 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace Kirby\Form\Field;
|
||
|
|
|
||
|
|
use Kirby\Data\Data;
|
||
|
|
use Kirby\Exception\InvalidArgumentException;
|
||
|
|
use Kirby\Form\FieldClass;
|
||
|
|
use Kirby\Form\Form;
|
||
|
|
use Kirby\Form\Mixin\EmptyState;
|
||
|
|
use Kirby\Form\Mixin\Max;
|
||
|
|
use Kirby\Form\Mixin\Min;
|
||
|
|
use Kirby\Toolkit\A;
|
||
|
|
use Kirby\Toolkit\Str;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Main class file of the entries field
|
||
|
|
*
|
||
|
|
* @package Kirby Field
|
||
|
|
* @author Ahmet Bora <ahmet@getkirby.com>
|
||
|
|
* @link https://getkirby.com
|
||
|
|
* @copyright Bastian Allgeier
|
||
|
|
* @license https://getkirby.com/license
|
||
|
|
* @since 5.0.0
|
||
|
|
*/
|
||
|
|
class EntriesField extends FieldClass
|
||
|
|
{
|
||
|
|
use EmptyState;
|
||
|
|
use Max;
|
||
|
|
use Min;
|
||
|
|
|
||
|
|
protected array $field;
|
||
|
|
protected Form $form;
|
||
|
|
protected bool $sortable = true;
|
||
|
|
|
||
|
|
public function __construct(array $params = [])
|
||
|
|
{
|
||
|
|
parent::__construct($params);
|
||
|
|
|
||
|
|
$this->setEmpty($params['empty'] ?? null);
|
||
|
|
$this->setField($params['field'] ?? null);
|
||
|
|
$this->setMax($params['max'] ?? null);
|
||
|
|
$this->setMin($params['min'] ?? null);
|
||
|
|
$this->setSortable($params['sortable'] ?? true);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function field(): array
|
||
|
|
{
|
||
|
|
return $this->field;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function fieldProps(): array
|
||
|
|
{
|
||
|
|
return $this->form()->fields()->first()->toArray();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @psalm-suppress MethodSignatureMismatch
|
||
|
|
* @todo Remove psalm suppress after https://github.com/vimeo/psalm/issues/8673 is fixed
|
||
|
|
*/
|
||
|
|
public function fill(mixed $value): static
|
||
|
|
{
|
||
|
|
$this->value = Data::decode($value ?? '', 'yaml');
|
||
|
|
return $this;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function form(): Form
|
||
|
|
{
|
||
|
|
return $this->form ??= new Form(
|
||
|
|
fields: [$this->field()],
|
||
|
|
model: $this->model
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function props(): array
|
||
|
|
{
|
||
|
|
return [
|
||
|
|
...parent::props(),
|
||
|
|
'empty' => $this->empty(),
|
||
|
|
'field' => $this->fieldProps(),
|
||
|
|
'max' => $this->max(),
|
||
|
|
'min' => $this->min(),
|
||
|
|
'sortable' => $this->sortable(),
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function setField(array|string|null $attrs = null): void
|
||
|
|
{
|
||
|
|
if (is_string($attrs) === true) {
|
||
|
|
$attrs = ['type' => $attrs];
|
||
|
|
}
|
||
|
|
|
||
|
|
$attrs ??= ['type' => 'text'];
|
||
|
|
|
||
|
|
if (in_array($attrs['type'], $this->supports()) === false) {
|
||
|
|
throw new InvalidArgumentException(
|
||
|
|
key: 'entries.supports',
|
||
|
|
data: ['type' => $attrs['type']]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// remove the unsupported props from the entry field
|
||
|
|
unset($attrs['counter'], $attrs['label']);
|
||
|
|
|
||
|
|
$this->field = $attrs;
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function setSortable(bool|null $sortable = true): void
|
||
|
|
{
|
||
|
|
$this->sortable = $sortable;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function sortable(): bool
|
||
|
|
{
|
||
|
|
return $this->sortable;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function supports(): array
|
||
|
|
{
|
||
|
|
return [
|
||
|
|
'color',
|
||
|
|
'date',
|
||
|
|
'email',
|
||
|
|
'number',
|
||
|
|
'select',
|
||
|
|
'slug',
|
||
|
|
'tel',
|
||
|
|
'text',
|
||
|
|
'time',
|
||
|
|
'url'
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
public function toFormValue(): mixed
|
||
|
|
{
|
||
|
|
$form = $this->form();
|
||
|
|
$value = parent::toFormValue() ?? [];
|
||
|
|
|
||
|
|
return A::map(
|
||
|
|
$value,
|
||
|
|
fn ($value) => $form
|
||
|
|
->reset()
|
||
|
|
->fill(input: [$value])
|
||
|
|
->fields()
|
||
|
|
->first()
|
||
|
|
->toFormValue()
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function toStoredValue(): mixed
|
||
|
|
{
|
||
|
|
$form = $this->form();
|
||
|
|
$value = parent::toStoredValue();
|
||
|
|
|
||
|
|
return A::map(
|
||
|
|
$value,
|
||
|
|
fn ($value) => $form
|
||
|
|
->reset()
|
||
|
|
->submit(input: [$value])
|
||
|
|
->fields()
|
||
|
|
->first()
|
||
|
|
->toStoredValue()
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function validations(): array
|
||
|
|
{
|
||
|
|
return [
|
||
|
|
'entries' => function ($value) {
|
||
|
|
if ($this->min && count($value) < $this->min) {
|
||
|
|
throw new InvalidArgumentException(
|
||
|
|
key: match ($this->min) {
|
||
|
|
1 => 'entries.min.singular',
|
||
|
|
default => 'entries.min.plural'
|
||
|
|
},
|
||
|
|
data: ['min' => $this->min]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->max && count($value) > $this->max) {
|
||
|
|
throw new InvalidArgumentException(
|
||
|
|
key: match ($this->max) {
|
||
|
|
1 => 'entries.max.singular',
|
||
|
|
default => 'entries.max.plural'
|
||
|
|
},
|
||
|
|
data: ['max' => $this->max]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
$form = $this->form();
|
||
|
|
|
||
|
|
foreach ($value as $index => $val) {
|
||
|
|
$form->reset()->submit(input: [$val]);
|
||
|
|
|
||
|
|
foreach ($form->fields() as $field) {
|
||
|
|
$errors = $field->errors();
|
||
|
|
|
||
|
|
if ($errors !== []) {
|
||
|
|
throw new InvalidArgumentException(
|
||
|
|
key: 'entries.validation',
|
||
|
|
data: [
|
||
|
|
'field' => $this->label() ?? Str::ucfirst($this->name()),
|
||
|
|
'index' => $index + 1
|
||
|
|
]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
];
|
||
|
|
}
|
||
|
|
}
|