1<?php
2
3
4namespace ComboStrap;
5
6use http\Exception\RuntimeException;
7
8/**
9 * Class FormMeta
10 * @package ComboStrap
11 *
12 * Represents form metadata sends via an ajax request
13 *
14 * It makes sure that the data send
15 * is coherent
16 */
17class FormMeta
18{
19
20    const FORM_TYPES = [self::FORM_NAV_TABS_TYPE, self::FORM_LIST_GROUP_TYPE];
21    const FORM_NAV_TABS_TYPE = "nav-tabs";
22    const FORM_LIST_GROUP_TYPE = "list-group";
23    const FIELDS_ATTRIBUTE = "fields";
24
25    private $name;
26
27    /**
28     * @var FormMetaField[]
29     */
30    private $fields;
31    /**
32     * @var FormMetaTab[]|string[]
33     */
34    private $tabs;
35    /**
36     * @var string
37     */
38    private $type;
39
40    /**
41     * @throws ExceptionComboRuntime
42     */
43    public function __construct($name)
44    {
45        Html::validNameGuard($name);
46        $this->name = $name;
47    }
48
49
50    public static function create($name): FormMeta
51    {
52        return new FormMeta($name);
53    }
54
55    public function addField(FormMetaField $formField): FormMeta
56    {
57        $this->fields[] = $formField;
58        $tab = $formField->getTab();
59        if (!empty($tab) && !isset($this->tabs[$tab])) {
60            $this->tabs[$tab] = $tab;
61        }
62        return $this;
63    }
64
65    public function addTab(FormMetaTab $tab): FormMeta
66    {
67        $this->tabs[$tab->getName()] = $tab;
68        return $this;
69    }
70
71    public function toAssociativeArray(): array
72    {
73
74        $fieldsArray = [];
75        foreach ($this->fields as $element) {
76            /**
77             * The order is kept even if we add a key
78             */
79            $fieldsArray[$element->getName()] = $element->toAssociativeArray();
80        }
81        $tabs = [];
82        foreach ($this->tabs as $element) {
83            if (!($element instanceof FormMetaTab)) {
84                $tab = FormMetaTab::create($element);
85            } else {
86                $tab = $element;
87            }
88            /**
89             * The order is kept even if we add a key
90             */
91            $tabs[$tab->getName()] = $tab->toAssociativeArray();
92        }
93        return [
94            self::FIELDS_ATTRIBUTE => $fieldsArray,
95            "tabs" => $tabs
96        ];
97    }
98
99    /**
100     * ie nav-tabs versus list-group:
101     * https://getbootstrap.com/docs/5.0/components/list-group/#javascript-behavior
102     *
103     * @param string $type
104     * @return FormMeta
105     */
106    public function setType(string $type): FormMeta
107    {
108        if (!in_array($type, self::FORM_TYPES)) {
109            LogUtility::msg("The form type ($type) is unknown");
110            return $this;
111        }
112        $this->type = $type;
113        return $this;
114    }
115
116    /**
117     * The data as if it was send by a HTML form to the
118     * post endpoint
119     *
120     * It transforms the fields to an associative array
121     * that should be send with a post request
122     *
123     * ie Equivalent to the javascript api formdata output
124     * (Used in test to simulate a post)
125     *
126     * TODO: Not sure but with the {@link MetadataStore}, this should move to the class
127     *   where the target/read store is a {@link MetadataFormDataStore} ?
128     */
129    public function toFormData(): array
130    {
131        $data = [];
132        $this->toFormDataRecurse($data, $this->fields);
133        return $data;
134    }
135
136    /**
137     * @param $data
138     * @param FormMetaField[] $fields
139     */
140    private function toFormDataRecurse(&$data, array $fields)
141    {
142
143        foreach ($fields as $field) {
144
145            if ($field->isMutable()) {
146
147                $value = $field->getValue();
148                if ($field->getType() === DataType::BOOLEAN_TYPE_VALUE) {
149                    if ($value === $field->getDefaultValue() || $value === null) {
150                        continue;
151                    }
152                }
153                if ($value === null) {
154                    // A form would return empty string
155                    $value = "";
156                }
157                if (is_array($value)) {
158                    $temp = [];
159                    foreach ($value as $subValue) {
160                        if ($subValue === null) {
161                            $temp[] = "";
162                        } else {
163                            if (is_array($subValue) && $field->isMultiple()) {
164                                $temp[] = implode(",", $subValue);
165                            } else {
166                                $temp[] = $subValue;
167                            }
168                        }
169                    }
170                    $value = $temp;
171                }
172                $data[$field->getName()] = $value;
173
174            }
175            $formMetaChildren = $field->getChildren();
176            if ($formMetaChildren != null) {
177                $this->toFormDataRecurse($data, $formMetaChildren);
178            }
179        }
180
181    }
182
183    /**
184     * @param Metadata $metadata
185     * @return FormMeta
186     * @throws ExceptionCombo
187     */
188    public function addFormFieldFromMetadata(Metadata $metadata): FormMeta
189    {
190        $field = FormMetaField::createFromMetadata($metadata);
191        $this->addField($field);
192        return $this;
193    }
194
195
196}
197