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