DynamicForm.class.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. <?php
  2. require_once(__DIR__.DIRECTORY_SEPARATOR.'DynamicField.class.php');
  3. /**
  4. * Define a dynamicform.
  5. * @author Charles DUBOIS
  6. * @category Plugin
  7. * @license copyright
  8. */
  9. class DynamicForm extends Entity{
  10. public $id;
  11. public $slug; //Slug (Texte)
  12. public $color; //Couleur (Couleur)
  13. public $icon; //Icônes (Icône)
  14. public $label; //Libellé (Texte)
  15. public $state; //State (Texte)
  16. public $firm; //Etablissement (Number)
  17. protected $TABLE_NAME = 'dynamicform_form';
  18. public $entityLabel = 'Formulaire';
  19. public $fields = array(
  20. 'id' => 'key',
  21. 'slug' => array('type'=>'string', 'label' => 'Slug'),
  22. 'color' => array('type'=>'string', 'label' => 'Couleur'),
  23. 'icon' => array('type'=>'string', 'label' => 'Icônes'),
  24. 'state' => array('type'=>'string', 'label' => 'Etat'),
  25. 'firm' => array('type'=>'int', 'label' => 'Etablissement','link'=>'class/Firm.class.php'),
  26. 'label' => array('type'=>'string', 'label' => 'Libellé')
  27. );
  28. //Colonnes indexées
  29. public $indexes = array('slug','firm');
  30. public static function remove($slug,$options = array()){
  31. Plugin::need('dynamicform/DynamicField,dynamicform/DynamicValue');
  32. $form = self::load(array('slug'=>$slug));
  33. if(!$form) return;
  34. $query = 'DELETE FROM {{table}} WHERE field IN(SELECT id FROM '.DynamicField::tableName().' fi WHERE fi.form=?) ';
  35. $data = array($form->id);
  36. if(empty($options['scope'])){
  37. $query .= ' AND scope=? ';
  38. $data[] = $options['scope'];
  39. }
  40. if(empty($options['uid'])){
  41. $query .= ' AND uid=? ';
  42. $data[] = $options['uid'];
  43. }
  44. DynamicValue::staticQuery($query,$data);
  45. }
  46. public static function check_required($slug,$options = array(),$newValues){
  47. Plugin::need('dynamicform/DynamicField');
  48. $types = FieldType::available();
  49. $query = "SELECT fi.id,fi.type,fi.label,fi.meta,fi.slug,fi.mandatory,fi.readonly
  50. FROM {{table}} fi
  51. LEFT JOIN ".self::tableName()." fo ON fi.form = fo.id
  52. WHERE fo.slug = ? ";
  53. $data = array($slug);
  54. $fields = DynamicField::staticQuery($query,$data,true,1);
  55. foreach($fields as $field)
  56. if(empty($newValues[$field->slug]) && $field->mandatory) throw new Exception('Le champ '.$field->label.' est obligatoire');
  57. //pour réutilisation de performance par la méthode record, souvant appellée juste après
  58. return $fields;
  59. }
  60. public static function record($slug,$options = array(),$newValues){
  61. Plugin::need('dynamicform/DynamicField');
  62. $types = FieldType::available();
  63. //si on a pas récupéré les fields dans un opération précédente (ex check_required), on les récupere ici
  64. if(!isset($options['fields'])){
  65. $query = "SELECT fi.id,fi.type,fi.label,fi.meta,fi.slug,fi.mandatory,fi.readonly
  66. FROM {{table}} fi
  67. LEFT JOIN ".self::tableName()." fo ON fi.form = fo.id
  68. WHERE fo.slug = ? ";
  69. $data = array($slug);
  70. $fields = DynamicField::staticQuery($query,$data,true,1);
  71. }else{
  72. $fields = $options['fields'];
  73. //gain de perf
  74. unset($options['fields']);
  75. }
  76. //on affiche chaque champ dynamique du formulaire
  77. foreach($fields as $field){
  78. if(empty($newValues[$field->slug]) && $field->mandatory) throw new Exception('Le champ '.$field->label.' est obligatoire');
  79. //on verifie qu'une valeur a été donnée
  80. if(!isset($newValues[$field->slug])) continue;
  81. //si le champ est en lecture seule on n'enregistre pas de valeur
  82. if($field->readonly) continue;
  83. DynamicField::record(array('field'=>$field,'value'=>$newValues[$field->slug],'options'=>$options,'types'=>$types));
  84. }
  85. }
  86. /**
  87. * Affiche un formulaire en fonction de son slug et de ses options de contexte
  88. * @param <String> slug du formulaire ciblé
  89. * @param <Array> options des filtres de sélection des champs
  90. * @return <Array> tableau des options de ciblage des champs à récupérer
  91. */
  92. public static function show($slug,$options = array()){
  93. global $myFirm;
  94. Plugin::need('dynamicform/DynamicField,dynamicform/DynamicValue');
  95. $options = array_merge(array(
  96. 'label' => 'block', // inline (input group) / block (label classique) / none (pas de label)
  97. 'legend' => 'block', // block (span text muted) / none (pas de label)
  98. 'input-class' => '', // classes additionnelles a placer sur les inputs
  99. 'input-group-class' => '', // classes additionnelles a placer sur les groupes d'inputs
  100. 'scope' => '', // ex : client
  101. 'uid' => '', // ex : 12 (Toxgen)
  102. 'firm' => $myFirm->id // ex : 1 (Sys1)
  103. ),$options);
  104. $options['format'] = 'table';
  105. $types = FieldType::available();
  106. //Initialisation des options de sélection des champs scope / firm etc
  107. $listOptions = self::list_options($slug, $options);
  108. //Si pas de slug on affiche rien
  109. if(empty($listOptions))return;
  110. //Récupération du tableau d'objets champs
  111. $fieldsObjects = self::get_fields($listOptions);
  112. //Mise au format table (mise en page ligne colonne) de la liste des champs
  113. $fieldsTable = self::fields_layout($fieldsObjects, $types, $options);
  114. //Mise au format liste (à plat) de la liste des champs
  115. $options['format'] = 'list';
  116. $fieldsList = self::fields_layout($fieldsObjects, $types, $options);
  117. $stream = '';
  118. $values = self::get_values($fieldsList,$options);
  119. if(isset($options['arrayOutput']) && $options['arrayOutput'])
  120. return self::get_values_as_array($values);
  121. //mise en page
  122. foreach($fieldsTable as $i => $columns) {
  123. $stream .= '<div class="row">';
  124. foreach ($columns as $u => $fields) {
  125. $stream .= '<div class="col-md column">';
  126. foreach($fields as $field){
  127. $value = isset($values[$field['id']]) ? $values[$field['id']] : '';
  128. $stream .= DynamicField::show(array('field'=>$field,'value'=>$value,'types'=>$types,'options'=>$options));
  129. }
  130. $stream .= '</div>';
  131. }
  132. $stream .= '</div>';
  133. }
  134. return $stream;
  135. }
  136. /**
  137. * Retourne un tableau de champs disponibles pour ce slug et cette firm
  138. * @param <String> slug du formulaire ciblé
  139. * @param <Array> options des filtres de sélection des champs
  140. * @return <Array> tableau des options de ciblage des champs à récupérer
  141. */
  142. public static function list($slug,$options = array()){
  143. $types = FieldType::available();
  144. $options = self::list_options($slug,$options);
  145. if(empty($options)) return array();
  146. $fields = self::get_fields($options);
  147. $rows = self::fields_layout($fields,$types,$options);
  148. return $rows;
  149. }
  150. /**
  151. * Initialise les options de filtrage pour récupération des champs
  152. * @param <String> slug du formulaire ciblé
  153. * @param <Array> options des filtres de sélection des champs
  154. * @return <Array> tableau des options de ciblage des champs à récupérer
  155. */
  156. public static function list_options($slug,$options = array()){
  157. global $myFirm;
  158. $options = array_merge(array(
  159. 'firm' => $myFirm->id, // ex : 1 (Sys1)
  160. 'format' => 'list' // table (mise en page ligne colonne) ou liste (a plat)
  161. ),$options);
  162. $forms = self::loadAll(array('slug'=>$slug,'firm:IN'=>array($options['firm'],'0')));
  163. if(!$forms || count($forms)==0) return array();
  164. $formIds = array();
  165. foreach ($forms as $form)
  166. $formIds[] = $form->id;
  167. $options['slug'] = $slug;
  168. $options['form'] = $formIds;
  169. $options['firm'] = array($options['firm'],'0');
  170. return $options;
  171. }
  172. /**
  173. * Retourne un tableau de champs (de type array) formaté en liste ou table selon l'option 'format'
  174. * @param <Array> tableau d'options/ filtres de sélection
  175. * @return <Array> tableau brut de champs (de type object) selon les options settées
  176. */
  177. public static function get_fields_layout($options = array()){
  178. $types = FieldType::available();
  179. $fields = self::get_fields($options);
  180. return self::fields_layout($fields,$types,$options);
  181. }
  182. /**
  183. * Retourne un tableau brut de champs (de type object) selon les options settées
  184. * @param <Array> tableau d'options/ filtres de sélection
  185. * @return <Array> tableau brut de champs (de type object) selon les options settées
  186. */
  187. public static function get_fields($options = array()){
  188. Plugin::need('dynamicform/DynamicField');
  189. $data = array();
  190. if(empty($options['format'])) $options['format'] = 'table';
  191. $query = 'SELECT fi.* FROM {{table}} fi LEFT JOIN '.self::tableName().' fo ON fi.form = fo.id WHERE 1 ';
  192. $query .= ' AND fo.state=? ';
  193. $data[] = self::ACTIVE;
  194. if(isset($options['slug'])){
  195. $query .= ' AND fo.slug=? ';
  196. $data[] = $options['slug'];
  197. }else if(isset($options['id'])){
  198. $query .= ' AND fo.id=? ';
  199. $data[] = $options['id'];
  200. }
  201. if(isset($options['form'])){
  202. $query .= ' AND fo.id IN ('.implode(',',array_fill(0,count($options['form']),'?')).')';
  203. foreach ($options['form'] as $form)
  204. $data[] = $form;
  205. }
  206. if(isset($options['firm'])){
  207. $query .= ' AND fo.firm IN ('.implode(',',array_fill(0,count($options['firm']),'?')).')';
  208. foreach ($options['firm'] as $firm)
  209. $data[] = $firm;
  210. }
  211. $query .= ' ORDER BY fi.row,fi.column,fi.sort';
  212. $dynamicfields = array();
  213. foreach(DynamicField::staticQuery($query,$data,true) as $dynamicField){
  214. if(!is_null($dynamicField->meta) && !empty($dynamicField->meta))
  215. $dynamicField->meta = base64_encode($dynamicField->meta);
  216. $dynamicfields[] = $dynamicField;
  217. }
  218. return $dynamicfields;
  219. }
  220. /**
  221. * Retourne un tableau de champs (de type array) formaté en liste ou table selon l'option 'format'
  222. * @param <Array> tableau brut d'objets champs dynamiques
  223. * @param <Array> tableau des fieldtypes disponibles
  224. * @param <Array> tableau des options de sélection et de format
  225. * @return <Array> tableau formaté (table ou liste) des champs dynamiques ciblés
  226. */
  227. public static function fields_layout($fields,$types,$options = array()){
  228. if(is_null($types)) $types = FieldType::available();
  229. if(empty($options['format'])) $options['format'] = 'table';
  230. $rows = array();
  231. foreach($fields as $field){
  232. $row = $field->toArray();
  233. $row['type'] = $types[$row['type']];
  234. if($options['format'] == 'table'){
  235. if(!isset($rows[$field->row])) $rows[$field->row] = array();
  236. if(!isset($rows[$field->row][$field->column])) $rows[$field->row][$field->column] = array();
  237. $rows[$field->row][$field->column][] = $row;
  238. }else{
  239. $rows[] = $row;
  240. }
  241. }
  242. return $rows;
  243. }
  244. /**
  245. * Retourne un tableau brut de valeurs pour un set de champs en fonction du scope / firm
  246. * @param <Array> tableau brut de champs dynamiques (sous forme d'arrays)
  247. * @param <Array> tableau des options de sélection et de format
  248. * @return <Array> tableau brut des valeurs des champs dynamiques ciblés avec comme clé l'identifiant du champ dynamique
  249. */
  250. public static function get_values($fields,$options){
  251. Plugin::need('dynamicform/DynamicValue');
  252. $values = array();
  253. $fieldIds = array();
  254. //Récuperation des id de fields
  255. foreach($fields as $field)
  256. $fieldIds[] = $field['id'];
  257. if(empty($fieldIds)) return $values;
  258. $valueFilters = array('field:IN'=>$fieldIds);
  259. if(!empty($options['firm'])) $valueFilters['firm:IN'] = array($options['firm'],0);
  260. if(!empty($options['scope'])) $valueFilters['scope'] = $options['scope'];
  261. $valueFilters['uid'] = $options['uid'];
  262. //récuperation des valeurs déja enregistrées pour ces fields
  263. foreach (DynamicValue::loadAll($valueFilters) as $key => $value)
  264. $values[$value->field] = $value->value;
  265. return $values;
  266. }
  267. /**
  268. * Retourne le tableau formaté des valeurs de champs pour export
  269. * @category manipulation de tableaux
  270. * @param <Array> tableau brut des valeurs des champs dynamiques
  271. * @return <Array> tableau des valeurs des champs dynamiques utilisables pour l'export
  272. */
  273. public static function get_values_as_array($values){
  274. $arrayOutput = array();
  275. foreach($values as $field){
  276. $field['value']= isset($values[$field['id']]) ? $values[$field['id']] : '';
  277. $arrayOutput[] = $field;
  278. }
  279. return $arrayOutput;
  280. }
  281. /**
  282. * Ajoute à la requete originelle les valeurs des champs dynamiques aliasisés par le slug du champ dynamique
  283. * @category manipulation de chaine pour création dynamiqe de requête de sélection en base
  284. * @param <Array> tableau des champs dynamiques potentiellement affichables
  285. * @param <String> requête originelle de sélection en base à modifier
  286. * @param <String> alias de la table mère présente dans le FROM de la requête originelle de sélection
  287. * @return <String> requête de sélection modifiée avec les valeurs des champs dynamiques
  288. */
  289. public static function query_column_add($fields,&$query,$alias = ''){
  290. global $_;
  291. Plugin::need('dynamicform/DynamicValue');
  292. $columns = array();
  293. //Tableau des champs dynamiques disponibles
  294. foreach($fields as $field)
  295. $columns[$field['id']] = $field['slug'];
  296. //Tableau des champs dynamiques sélectionnés
  297. $selected = isset($_['columns']) && isset($_['columns']['added']) ? $_['columns']['added'] : array();
  298. //On ne garde que les champs sélectionés et présents dans les champs disponibles ET les champs requetés en filtres
  299. if(isset($_['filters']['advanced'])){
  300. foreach($_['filters']['advanced'] as $field){
  301. $parts = explode('dynamicField_',$field['column']);
  302. //on ne traite que les field dynamiques
  303. if(count($parts)<=1) continue;
  304. //pour retrouver le slug du custom field (slug-fitlre) depuis le slug du filtre (dynamicField_{{id}})
  305. $dynamicSlug = $columns[str_replace('.value','',$parts[1])];
  306. $selected[] = $dynamicSlug;
  307. }
  308. }
  309. $columns = array_intersect($columns,$selected);
  310. //On crée la chaine de sélection des valeurs
  311. $selectFields = array();
  312. foreach($columns as $id=>$slug)
  313. $selectFields[] = 'dynamicField_'.$id.'.value AS "'.$slug.'"';
  314. //On récupère ce qu'il y a entre select et from et on le concatène à la chaine de sélection des valeurs créé
  315. $selectFields = empty($selectFields) ? ' ' : ','.implode(',',$selectFields);
  316. $query = preg_replace("/(?<=SELECT)(.*)(?=FROM)/i","$0".$selectFields.' ',$query);
  317. //on récupère ce qu'il y a entre select et where et on le concat avec les left joins pour chaque champs dynamiques à afficher
  318. $joinFields = array();
  319. foreach($columns as $id=>$slug)
  320. $joinFields[] = ' LEFT JOIN '.DynamicValue::tableName().' dynamicField_'.$id.' ON dynamicField_'.$id.'.uid = '.(empty($alias) ? 'id' : '`'.$alias.'`.id').' AND dynamicField_'.$id.'.field = '.$id;
  321. $joinFields = implode(' ',$joinFields);
  322. $query = preg_replace("/(?<=SELECT)(.*)(?=WHERE)/is","$0".$joinFields.' ',$query);
  323. }
  324. //Retourne un set de filtres de colonnes dynamiques en fonction de la liste des
  325. //champs custom fournie
  326. public static function get_dynamic_columns($fields){
  327. $columns = array();
  328. foreach ($fields as $field) {
  329. $columns[$field['slug']] =
  330. array(
  331. 'head' => '<th data-sortable="dynamicField_'.$field['id'].'.value" data-available="'.$field['slug'].'">'.$field['label'].'</th>',
  332. 'body' => '<td class="align-middle text-center">{{{'.$field['slug'].'}}}</td>',
  333. );
  334. }
  335. return $columns;
  336. }
  337. //Convertion des valeurs retours d'une db pour des colonnes dynamiques en fonction de leurs types
  338. public static function search_values(&$row,$options){
  339. if(!isset($options['force-raw'])) $options['force-raw'] = false;
  340. //Récupération des champs afin d'avoir les metas pour affichaqge fieldtypes exotique list, dictionnary...
  341. $meta = array();
  342. foreach(DynamicField::loadAll(array('slug:IN'=>array_keys($options['slugs']))) as $field)
  343. $meta[$field->slug] = $field->meta;
  344. //pour chaque champ, s'il fait partie des types qui possèdent une propriété onHtmlDisplay, on l'applique à la valeur du champ
  345. foreach($options['slugs'] as $slug=>$value){
  346. if(!isset($options['types'][$slug])) continue;
  347. $type = $options['types'][$slug];
  348. $displayOptions = array('meta'=>$meta[$slug],'type'=>$type);
  349. //tansmission des parametres de contexte
  350. foreach($options as $key=>$option){
  351. if(!is_string($option)) continue;
  352. $displayOptions[$key] = $option;
  353. }
  354. if(isset($row['id'])) $displayOptions['uid'] = $row['id'];
  355. if(property_exists($type,"onHtmlDisplay") && !$options['force-raw']){
  356. $method = $type->onHtmlDisplay;
  357. $displayOptions['decoration'] = 'true';
  358. $value = $method($value,$displayOptions);
  359. }elseif(property_exists($type,"onRawDisplay")){
  360. $method = $type->onRawDisplay;
  361. $value = $method($value,$displayOptions);
  362. }
  363. $row[$slug] = $value;
  364. }
  365. }
  366. //Récuperation des valeurs de champs dynamic pour un formulaire, un scope et un item donné sous la forme slug field => valeur
  367. public static function values($formSlug,$options){
  368. require_once(__DIR__.SLASH.'DynamicForm.class.php');
  369. require_once(__DIR__.SLASH.'DynamicField.class.php');
  370. require_once(__DIR__.SLASH.'DynamicValue.class.php');
  371. $query = 'SELECT dfi.slug,dva.value FROM {{table}} dva
  372. LEFT JOIN '.DynamicField::tableName().' dfi ON dfi.id = dva.field
  373. LEFT JOIN '.DynamicForm::tableName().' dfo ON dfo.id = dfi.form
  374. WHERE dfo.slug = ? AND dva.scope = ? AND dva.uid = ?';
  375. $data = array($formSlug,$options['scope'],$options['uid']);
  376. $values = array();
  377. foreach (DynamicValue::staticQuery($query,$data,true) as $key => $value)
  378. $values[$value->foreign('slug')] = $value->value;
  379. return $values;
  380. }
  381. //Retourne un set de filtres de recherche avancée en fonction de la liste des
  382. //champs custom fournie
  383. public static function get_filters($fields){
  384. $filters = array();
  385. foreach($fields as $field){
  386. $filterOptions = '';
  387. $filterType = $field['type']->slug;
  388. $meta = isset($field['meta']) ? json_decode(base64_decode($field['meta']),true) : (object) array();
  389. //le field doit comprendre le meta show-filter pour afficher le filtre (case a cocher dans la construction du formulaire)
  390. if(!is_array($meta) || empty($meta['show-filter'])) continue;
  391. $filterOptions = ' data-filter-type="'.$filterType.'" ';
  392. if(is_array($meta)){
  393. foreach($meta as $key=>$value)
  394. $filterOptions .= 'data-'.$key.'=\''.(is_string($value) ? $value : str_replace("'","\'",json_encode($value))).'\' ';
  395. }
  396. //si le type de champ ne propose pas de filtre on l'ignore
  397. if(!property_exists($field['type'],'filter')) continue;
  398. $filters[$field['slug']] = '<option value="dynamicField_'.$field['id'].'.value"'.$filterOptions.'>'.$field['label'].'</option>';
  399. }
  400. return $filters;
  401. }
  402. }
  403. ?>