Source of file TICWidgetType.php

Size: 10,776 Bytes - Last Modified: 2023-11-16T22:56:03+01:00

/home/websites/teicee/packagist/site/phpdoc/conf/../vendor/teicee/form-bundle/src/Base/TICWidgetType.php

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
<?php
namespace TIC\FormBundle\Base;

use Symfony\Component\Form\AbstractType;
#use Symfony\Component\Form\Extension\Core\Type\FormType;
#use Symfony\Component\Form\Util\StringUtil;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\Options;

/**
 * Classe parente pour tous les widgets fournis par le bundle.
 */
abstract class TICWidgetType extends AbstractType
{
	/**
	 * ATTENTION: une propriété doit être vue comme une constante de classe !
	 * Sa valeur sera commune aux différentes pseudo-instances des FormType.
	 * Ne pas s'en servir pour stocker des données dépendantes des options.
	 */
	protected $form_alias     = null;
	protected $form_parent    = null;
	protected $default_prefix = '<chevron-right>';
	protected $default_suffix = '<pencil>';
	protected $class_prefix   = 'input-group-text';
	protected $class_suffix   = 'input-group-text';
	protected $soft_required  = null;


	/**
	 * Initialisation du $form_alias.
	 */
	public function __construct()
	{
		if ($this->form_alias === null) {
			$this->form_alias = 'tic_widget';
			if (preg_match("/^TIC\\\\.*\\\\([^\\\\]+)Type\$/", static::class, $match)) {
#				$this->form_alias = "tic_" . strtolower($match[1]);
				$this->form_alias = "tic_" . strtolower(trim(preg_replace("/([A-Z])/", '_\1', $match[1]), '_'));
			}
		}
	}


	/**
	 * {@inheritdoc}
	 */
	public function getParent(): string
	{
		if ($this->form_parent !== null) return $this->form_parent;
		return parent::getParent();  // FormType::class;
	}


	/**
	 * {@inheritdoc}
	 */
	public function getBlockPrefix(): string
	{
		if ($this->form_alias !== null) return $this->form_alias;
		return parent::getBlockPrefix();  // StringUtil::fqcnToBlockPrefix(static::class) ?: '';
	}


	/**
	 * {@inheritdoc}
	 */
	public function configureOptions(OptionsResolver $resolver): void
	{
		$resolver->setDefaults(array(
			'label'       => true,
			'label_attr'  => array(),
			'placeholder' => false,
			'pattern'     => null,      // regexp de validation HTML5
			'prefix'      => null,      // traduisible, html autorisé, raccourci <glyph-name>
			'suffix'      => null,      // traduisible, html autorisé, raccourci <glyph-name>
			'prefix_attr' => array(),
			'suffix_attr' => array(),
			'group_class' => null,      // classes à ajouter sur le conteneur du widget
			'row_class'   => null,      // classes à ajouter sur le conteneur principal du form type
			'grid_class'  => null,      // bootstrap grid pour le row_class : "12,6,4" ou "xs-12,sm-6,md-4"
			'inline'      => false,     // bootstrap form-inline sans cols pour label/widget
			'sizing'      => null,      // bootstrap form sizing : sm | md | lg (héritage si null)
			'required'    => true,
			'readonly'    => false,
			'strict'      => false,     // traitement normal sans tolérance sur des données sources de type invalide
		));
		
		$resolver->setAllowedTypes('label_attr',  array('array'));
		$resolver->setAllowedTypes('placeholder', array('null', 'string', 'bool'));
		$resolver->setAllowedTypes('pattern',     array('null', 'string', 'bool'));
		$resolver->setAllowedTypes('prefix',      array('null', 'string', 'bool'));
		$resolver->setAllowedTypes('suffix',      array('null', 'string', 'bool'));
		$resolver->setAllowedTypes('prefix_attr', array('array'));
		$resolver->setAllowedTypes('suffix_attr', array('array'));
		$resolver->setAllowedTypes('readonly',    array('bool'));
		$resolver->setAllowedTypes('group_class', array('null', 'string'));
		$resolver->setAllowedTypes('row_class',   array('null', 'string'));
		$resolver->setAllowedTypes('grid_class',  array('null', 'string'));
		$resolver->setAllowedTypes('inline',      array('bool'));
		$resolver->setAllowedTypes('strict',      array('bool'));
		$resolver->setAllowedValues('sizing',     array(null, 'sm', 'md', 'lg'));
		$resolver->setAllowedValues('required',   array(true, false, 'soft'));
		
		// gestion du "soft-required" : required passé à false mais label_attr avec classe 'required'
		$resolver->setNormalizer('required', function (Options $options, $required) {
			// reinit à false important car la propriété est partagée par tous les widgets d'un même FormType
			$this->soft_required = false;
			if ($required === 'soft') { $this->soft_required = true; $required = false; }
			return $required;
		});
		$resolver->setNormalizer('label_attr', function (Options $options, $label_attr) {
			if ($options['required'] === false && $this->soft_required) {
				$label_attr['class'] = isset($label_attr['class']) ? $label_attr['class'] . ' required' : 'required';
			}
			return $label_attr;
		});
		
		// gestion de l'option 'column' en ajoutant les classes nécessaires à 'row_class'
		$resolver->setNormalizer('row_class', function (Options $options, $row_class) {
			if ($options['grid_class'] !== null) {
				$row_class = empty($row_class) ? 'form-group-col' : $row_class.' form-group-col';
				$formats = array('xs','sm','md','lg');
				foreach (explode(',', $options['grid_class']) as $idx => $col) {
					if ($col === '') continue;
					if (ctype_digit($col)) $col = $formats[$idx] . '-' . $col;
					$row_class.= ' col-' . $col;
				}
			}
			return $row_class;
		});
	}


	/**
	 * {@inheritdoc}
	 */
	public function buildView(FormView $view, FormInterface $form, array $options): void
	{
		// input_group avec prefix/suffix ?
		$this->setViewInputGroup($view, $options);
		
		// héritage de l'option sizing (utile avec tic_collection)
		while ($options['sizing'] === null) {
			$parent = isset($parent) ? $parent->getParent() : $form->getParent();
			if (! isset($parent)) break;
			$options['sizing'] = $parent->getConfig()->getOption('sizing');
		}
		$view->vars['sizing'] = ($options['sizing'] === null) ? 'md' : $options['sizing'];
		
		// class form-inline sur le conteneur et affichage des enfants avec form_row sans cols
		$view->vars['inline'] = ($options['inline'] && $options['compound']) ? true : false;
		
		// ajout des options placeholder et pattern dans les attributs du champs
		if (! empty($options['placeholder'])) $view->vars['attr']['placeholder'] = $options['placeholder'];
		if (! empty($options['pattern']))     $view->vars['attr']['pattern']     = $options['pattern'];
		
		// définition des classes à appliquer sur les conteneurs du widget
		// Note: dépend des variables 'inline', 'input_group' et 'sizing'
		$this->setViewGroupClass($view, $options);
		
		// spécification de la taille sur le widget lui-même (ajout de classe bootstrap)
		// Note: dépend de la variable 'sizing'
		$this->setViewInputSizing($view, $options);
		
		// options readonly propre au widget
		$this->setViewReadOnly($view, $options);
	}


	/**
	 *
	 */
	protected function generateContent($value, $default)
	{
		if ($value === true ) $value = $default;
		if ($value === null ) return;
		if ($value === false) return;
		$value = preg_replace('/<fa([srlb]?)\-([\w\-]+)>/', '<i class="fa${1} fa-${2}" aria-hidden="true"></i>', $value);
		$value = preg_replace('/<bi\-([\w\-]+)>/',          '<i class="bi bi-${1}" aria-hidden="true"></i>', $value);
		$value = preg_replace('/<gi\-([\w\-]+)>/',          '<span class="glyphicon glyphicon-${1}" aria-hidden="true"></span>', $value);
#		$value = preg_replace(   '/^<([\w\-]+)>$/',         '<span class="glyphicon glyphicon-${1}" aria-hidden="true"></span>', $value);
		$value = preg_replace(   '/^<([\w\-]+)>$/',         '<i class="bi bi-${1}" aria-hidden="true"></i>', $value);
		$value = preg_replace('/^\[([^\|\]]+)\|([\w\-\s]+)(\|([^\|\]]*))?\]$/', '<button class="btn btn-${2}" type="button" title="${4}">${1}</button>', $value);
		return $value;
	}

	/**
	 * Définition des éventuels contenus préfixes & suffixes du widget.
	 */
	protected function setViewInputGroup(FormView $view, $options = array()): void
	{
		if (! isset($view->vars['input_group'])) $view->vars['input_group'] = false;
		
		// définition du contenu et des attributs du préfixe du champs
		$view->vars['prefix'] = $this->generateContent($options['prefix'], $this->default_prefix);
		if ($view->vars['prefix'] !== null) {
			$view->vars['prefix_attr'] = $options['prefix_attr'] + array('class' => '');
			$class = strpos($view->vars['prefix'], 'btn btn') ? 'input-group-btn' : $this->class_prefix;
			$view->vars['prefix_attr']['class'] = trim($view->vars['prefix_attr']['class'].' '.$class);
			$view->vars['input_group'] = true;
		}
		
		// définition du contenu et des attributs du suffixe du champs
		$view->vars['suffix'] = $this->generateContent($options['suffix'], $this->default_suffix);
		if ($view->vars['suffix'] !== null) {
			$view->vars['suffix_attr'] = $options['suffix_attr'] + array('class' => '');
			$class = strpos($view->vars['suffix'], 'btn btn') ? 'input-group-btn' : $this->class_suffix;
			$view->vars['suffix_attr']['class'] = trim($view->vars['suffix_attr']['class'].' '.$class);
			$view->vars['input_group'] = true;
		}
	}

	/**
	 * Définition des classes à appliquer sur les conteneurs du widget (variables 'row_class' et 'group_class').
	 * Note: dépend des variables 'inline', 'input_group' et 'sizing'
	 */
	protected function setViewGroupClass(FormView $view, $options = array()): void
	{
		$view->vars['row_class'] = $options['row_class'];
		$view->vars['group_class'] = strtr($this->form_alias, '_', '-') . '-widget';
		
		if (isset($options['group_class'])) {
			$view->vars['group_class'].= ' ' . $options['group_class'];
		}
		if ($view->vars['inline']) {
			$view->vars['group_class'].= ' form-inline';
		}
		if ($view->vars['input_group']) {
			$view->vars['group_class'].= ' input-group';
			if ($view->vars['sizing'] !== null) $view->vars['group_class'].= ' input-group-' . $view->vars['sizing'];
		}
	}

	/**
	 * Définition de la classe à appliquer pour la taille du widget (classe bootstrap).
	 * Note: dépend de la variable 'sizing'
	 */
	protected function setViewInputSizing(FormView $view, $options = array()): void
	{
		if ($view->vars['sizing'] !== null) {
			$class = isset($view->vars['attr']['class']) ? $view->vars['attr']['class'] . ' ' : '';
			$view->vars['attr']['class'] = $class . 'input-' . $view->vars['sizing'];
		}
	}

	/**
	 * Définition de l'état "lecture seule" du widget (variable et attribut HTML).
	 * Note: compatibilité ave l'option 'read_only' (deprecated)
	 */
	protected function setViewReadOnly(FormView $view, $options = array()): void
	{
		$view->vars['readonly'] = $options['readonly']
		 || (array_key_exists('read_only', $options) && $options['read_only'])
		 || (array_key_exists('readonly', $options['attr']) && $options['attr']['readonly'])
		;
		if ($view->vars['readonly']) $view->vars['attr']['readonly'] = true;
	}

}