geoproject-app/public/kirby/src/Form/Form.php
2025-11-24 14:01:48 +01:00

400 lines
7.5 KiB
PHP

<?php
namespace Kirby\Form;
use Kirby\Cms\File;
use Kirby\Cms\Language;
use Kirby\Cms\ModelWithContent;
use Kirby\Data\Data;
use Kirby\Toolkit\A;
/**
* The main form class, that is being
* used to create a list of form fields
* and handles global form validation
* and submission
*
* @package Kirby Form
* @author Bastian Allgeier <bastian@getkirby.com>
* @link https://getkirby.com
* @copyright Bastian Allgeier
* @license https://opensource.org/licenses/MIT
*/
class Form
{
/**
* Fields in the form
*/
protected Fields $fields;
/**
* Form constructor
*/
public function __construct(
array $props = [],
array $fields = [],
ModelWithContent|null $model = null,
Language|string|null $language = null
) {
if ($props !== []) {
$this->legacyConstruct(...$props);
return;
}
$this->fields = new Fields(
fields: $fields,
model: $model,
language: $language
);
}
/**
* Returns the data required to write to the content file
* Doesn't include default and null values
*
* @deprecated 5.0.0 Use `::toStoredValues()` instead
*/
public function content(): array
{
return $this->data(false, false);
}
/**
* Returns data for all fields in the form
*
* @deprecated 5.0.0 Use `::toStoredValues()` instead
*/
public function data($defaults = false, bool $includeNulls = true): array
{
$data = [];
$language = $this->fields->language();
foreach ($this->fields as $field) {
if ($field->isStorable($language) === false) {
if ($includeNulls === true) {
$data[$field->name()] = null;
}
continue;
}
if ($defaults === true && $field->isEmpty() === true) {
$field->fill($field->default());
}
$data[$field->name()] = $field->toStoredValue();
}
foreach ($this->fields->passthrough() as $key => $value) {
if (isset($data[$key]) === false) {
$data[$key] = $value;
}
}
return $data;
}
/**
* Returns an array with the default value of each field
*
* @since 5.0.0
*/
public function defaults(): array
{
return $this->fields->defaults();
}
/**
* An array of all found errors
*/
public function errors(): array
{
return $this->fields->errors();
}
/**
* Get the field object by name
* and handle nested fields correctly
*
* @throws \Kirby\Exception\NotFoundException
*/
public function field(string $name): Field|FieldClass
{
return $this->fields->field($name);
}
/**
* Returns form fields
*/
public function fields(): Fields
{
return $this->fields;
}
/**
* Sets the value for each field with a matching key in the input array
*
* @since 5.0.0
*/
public function fill(
array $input,
bool $passthrough = true
): static {
$this->fields->fill(
input: $input,
passthrough: $passthrough
);
return $this;
}
/**
* Creates a new Form instance for the given model with the fields
* from the blueprint and the values from the content
*/
public static function for(
ModelWithContent $model,
array $props = [],
Language|string|null $language = null,
): static {
if ($props !== []) {
return static::legacyFor(
$model,
...$props
);
}
$form = new static(
fields: $model->blueprint()->fields(),
model: $model,
language: $language
);
// fill the form with the latest content of the model
$form->fill(input: $model->content($form->language())->toArray());
return $form;
}
/**
* Checks if the form is invalid
*/
public function isInvalid(): bool
{
return $this->isValid() === false;
}
/**
* Checks if the form is valid
*/
public function isValid(): bool
{
return $this->fields->errors() === [];
}
/**
* Returns the language of the form
*
* @since 5.0.0
*/
public function language(): Language
{
return $this->fields->language();
}
/**
* Legacy constructor to support the old props array
*
* @deprecated 5.0.0 Use the new constructor with named parameters instead
*/
protected function legacyConstruct(
array $fields = [],
ModelWithContent|null $model = null,
Language|string|null $language = null,
array $values = [],
array $input = [],
bool $strict = false
): void {
$this->__construct(
fields: $fields,
model: $model,
language: $language
);
$this->fill(
input: $values,
passthrough: $strict === false
);
$this->submit(
input: $input,
passthrough: $strict === false
);
}
/**
* Legacy for method to support the old props array
*
* @deprecated 5.0.0 Use `::for()` with named parameters instead
*/
protected static function legacyFor(
ModelWithContent $model,
Language|string|null $language = null,
bool $strict = false,
array|null $input = [],
array|null $values = [],
bool $ignoreDisabled = false
): static {
$form = static::for(
model: $model,
language: $language,
);
$form->fill(
input: $values ?? [],
passthrough: $strict === false
);
$form->submit(
input: $input ?? [],
passthrough: $strict === false
);
return $form;
}
/**
* Adds values to the passthrough array
* which will be added to the form data
* if the field does not exist
*
* @since 5.0.0
*/
public function passthrough(
array|null $values = null
): static|array {
if ($values === null) {
return $this->fields->passthrough();
}
$this->fields->passthrough(
values: $values
);
return $this;
}
/**
* Resets the value of each field
*
* @since 5.0.0
*/
public function reset(): static
{
$this->fields->reset();
return $this;
}
/**
* Converts the data of fields to strings
*
* @deprecated 5.0.0 Use `::toStoredValues()` instead
*/
public function strings($defaults = false): array
{
return A::map(
$this->data($defaults),
fn ($value) => match (true) {
is_array($value) => Data::encode($value, 'yaml'),
default => $value
}
);
}
/**
* Sets the value for each field with a matching key in the input array
* but only if the field is not disabled
*
* @since 5.0.0
* @param bool $passthrough If true, values for undefined fields will be submitted
* @param bool $force If true, values for fields that cannot be submitted (e.g. disabled or untranslatable fields) will be submitted
*/
public function submit(
array $input,
bool $passthrough = true,
bool $force = false
): static {
$this->fields->submit(
input: $input,
passthrough: $passthrough,
force: $force
);
return $this;
}
/**
* Converts the form to a plain array
*/
public function toArray(): array
{
$array = [
'errors' => $this->fields->errors(),
'fields' => $this->fields->toArray(),
'invalid' => $this->isInvalid()
];
return $array;
}
/**
* Returns an array with the form value of each field
* (e.g. used as data for Panel Vue components)
*
* @since 5.0.0
*/
public function toFormValues(): array
{
return $this->fields->toFormValues();
}
/**
* Returns an array with the props of each field
* for the frontend
*
* @since 5.0.0
*/
public function toProps(): array
{
return $this->fields->toProps();
}
/**
* Returns an array with the stored value of each field
* (e.g. used for saving to content storage)
*
* @since 5.0.0
*/
public function toStoredValues(): array
{
return $this->fields->toStoredValues();
}
/**
* Validates the form and throws an exception if there are any errors
*
* @throws \Kirby\Exception\InvalidArgumentException
*/
public function validate(): void
{
$this->fields->validate();
}
/**
* Returns form values
*
* @deprecated 5.0.0 Use `::toFormValues()` instead
*/
public function values(): array
{
return $this->fields->toFormValues();
}
}