> $options Choice list. * @param array $settings Type specific settings. * @param bool $is_required Whether the field is mandatory. * @param int $menu_order Position within the form. */ public function __construct( private int $id = 0, private int $form_id = 0, private string $field_key = '', private string $type = FieldTypes::TEXT, private string $label = '', private string $placeholder = '', private string $description = '', private string $default_value = '', private array $options = array(), private array $settings = array(), private bool $is_required = false, private int $menu_order = 0 ) {} /** * Builds an entity from a database row. * * @param object|array $row Row returned by wpdb. */ public static function from_row( object|array $row ): Field { $row = (array) $row; return new self( id: (int) ( $row['id'] ?? 0 ), form_id: (int) ( $row['form_id'] ?? 0 ), field_key: (string) ( $row['field_key'] ?? '' ), type: (string) ( $row['type'] ?? FieldTypes::TEXT ), label: (string) ( $row['label'] ?? '' ), placeholder: (string) ( $row['placeholder'] ?? '' ), description: (string) ( $row['description'] ?? '' ), default_value: (string) ( $row['default_value'] ?? '' ), options: self::decode( $row['options'] ?? '' ), settings: self::decode( $row['settings'] ?? '' ), is_required: ! empty( $row['is_required'] ), menu_order: (int) ( $row['menu_order'] ?? 0 ) ); } /** * Decodes a JSON column. * * @param mixed $value Raw column value. * * @return array The decoded array, empty when unusable. */ private static function decode( mixed $value ): array { if ( is_array( $value ) ) { return $value; } if ( ! is_string( $value ) || '' === $value ) { return array(); } $decoded = json_decode( $value, true ); return is_array( $decoded ) ? $decoded : array(); } /** * Returns the row ID. * * @return int The field ID, or 0 when unsaved. */ public function id(): int { return $this->id; } /** * Returns the owning form ID. */ public function form_id(): int { return $this->form_id; } /** * Returns the machine name. */ public function field_key(): string { return $this->field_key; } /** * Returns the field type. */ public function type(): string { return $this->type; } /** * Returns the translated type label. */ public function type_label(): string { return FieldTypes::label( $this->type ); } /** * Returns the visible label. */ public function label(): string { return $this->label; } /** * Returns the placeholder. */ public function placeholder(): string { return $this->placeholder; } /** * Returns the help text. */ public function description(): string { return $this->description; } /** * Returns the prefilled value. */ public function default_value(): string { return $this->default_value; } /** * Returns the choice list. * * @return array> Options with label and value keys. */ public function options(): array { return $this->options; } /** * Returns the choice list as editable text, one option per line. */ public function options_as_text(): string { $lines = array(); foreach ( $this->options as $option ) { $label = (string) ( $option['label'] ?? '' ); $value = (string) ( $option['value'] ?? '' ); /* * A label containing the separator is escaped on the way out and * unescaped on the way back in. Written plainly, a label of `A|B` with a * matching value round-tripped as label `A`, value `B` — the option was * silently rewritten by the act of opening the form and saving it. */ $label = str_replace( array( '\\', '|' ), array( '\\\\', '\\|' ), $label ); $lines[] = $label === $value ? $label : $label . '|' . $value; } return implode( "\n", $lines ); } /** * Returns the type specific settings. * * @return array The settings payload. */ public function settings(): array { return $this->settings; } /** * Returns a single setting. * * @param string $key Setting name. * @param mixed $fallback Optional. Value returned when the setting is absent. Default ''. * * @return mixed The stored value. */ public function setting( string $key, mixed $fallback = '' ): mixed { return $this->settings[ $key ] ?? $fallback; } /** * Determines whether the field is mandatory. * * @return bool True when required. */ public function is_required(): bool { return $this->is_required; } /** * Returns the conditional logic attached to the field. * * @return LogicRule The rule, inactive when none is configured. */ public function logic(): LogicRule { return LogicRule::from_array( $this->setting( 'logic', array() ) ); } /** * Returns a copy with a different required flag. * * Used by conditional logic, which can make an optional field mandatory for * one submission without changing what the form stores. * * @param bool $required Whether the field must be filled in. */ public function with_required( bool $required ): Field { $clone = clone $this; $clone->is_required = $required; return $clone; } /** * Returns the position within the form. */ public function menu_order(): int { return $this->menu_order; } /** * Returns the entity as an array. * * @return array The entity data. */ public function to_array(): array { return array( 'id' => $this->id, 'form_id' => $this->form_id, 'field_key' => $this->field_key, 'type' => $this->type, 'label' => $this->label, 'placeholder' => $this->placeholder, 'description' => $this->description, 'default_value' => $this->default_value, 'options' => $this->options, 'settings' => $this->settings, 'is_required' => $this->is_required, 'menu_order' => $this->menu_order, ); } }