150 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			150 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace nur\b\values;
 | |
| 
 | |
| use nur\A;
 | |
| 
 | |
| /**
 | |
|  * Class Breaker: aide pour construire des listes multi-niveaux.
 | |
|  *
 | |
|  * Dans une liste, certaines valeurs sont des clés. L'idée est de pouvoir
 | |
|  * visuellement représenter les valeurs associées à ces clés. La méthode
 | |
|  * should_break_on() indique quand la clé change, ce qui indique qu'il faut
 | |
|  * passer à la section suivante.
 | |
|  *
 | |
|  * Par exemple, avec la liste suivante:
 | |
|  *   clé valeur
 | |
|  *   A   1.1
 | |
|  *   A   1.2
 | |
|  *   A   1.3
 | |
|  *   B   2.1
 | |
|  *   B   2.2
 | |
|  *   C   3.1
 | |
|  * should_break_on($row["clé"]) retourne true pour les valeurs 1.1, 2.1 et 3.1
 | |
|  * et donc on peut facilement construire cette liste pour affichage:
 | |
|  *   clé valeur
 | |
|  *   A   1.1
 | |
|  *       1.2
 | |
|  *       1.3
 | |
|  *   B   2.1
 | |
|  *       2.2
 | |
|  *   C   3.1
 | |
|  */
 | |
| class Breaker {
 | |
|   function __construct() {
 | |
|     $this->reset();
 | |
|   }
 | |
| 
 | |
|   private $values;
 | |
| 
 | |
|   private $changedKeys;
 | |
| 
 | |
|   private function computeShouldBreak(array $values): bool {
 | |
|     $should_break = false;
 | |
|     foreach ($values as $key => $value) {
 | |
|       if (!$should_break) {
 | |
|         if ($value === A::get($this->values, $key)) {
 | |
|           $this->changedKeys[$key] = false;
 | |
|           continue;
 | |
|         } else {
 | |
|           $should_break = true;
 | |
|         }
 | |
|       }
 | |
|       $this->changedKeys[$key] = $should_break;
 | |
|     }
 | |
|     $this->values = $values;
 | |
|     return $should_break;
 | |
|   }
 | |
| 
 | |
|   function reset() {
 | |
|     $this->values = [];
 | |
|     $this->changedKeys = [];
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Retourner true si $values est différent par rapport au précédent appel
 | |
|    *
 | |
|    * $value peut contenir des instances de Breaker à condition que leur
 | |
|    * méthodes {@link shouldBreakOn()} aient déjà été appelées.
 | |
|    */
 | |
|   function shouldBreakOn(...$values): bool {
 | |
|     # résoudre les éventuelles instances de Breaker
 | |
|     $actual_values = [];
 | |
|     foreach ($values as $value) {
 | |
|       if ($value instanceof Breaker) {
 | |
|         $actual_values = array_merge($actual_values, $value->values);
 | |
|       } else {
 | |
|         $actual_values[] = $value;
 | |
|       }
 | |
|     }
 | |
|     return $this->computeShouldBreak($actual_values);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Retourner true si les valeurs de $array correspondant aux clés $keys sont
 | |
|    * différentes par rapport au précédent appel
 | |
|    *
 | |
|    * $keys peut contenir des instances de Breaker à condition que leur méthodes
 | |
|    * {@link shouldBreakOnKeys()} aient déjà été appelées.
 | |
|    */
 | |
|   function shouldBreakOnKeys($array, ...$keys): bool {
 | |
|     # résoudre les éventuelles instances de Breaker
 | |
|     $values = [];
 | |
|     foreach ($keys as $key) {
 | |
|       if ($key instanceof Breaker) {
 | |
|         $values[] = $key;
 | |
|       } else {
 | |
|         $values[$key] = A::get($array, $key);
 | |
|       }
 | |
|     }
 | |
|     $actual_values = [];
 | |
|     foreach ($values as $key => $value) {
 | |
|       if ($value instanceof Breaker) {
 | |
|         $actual_values = array_merge($actual_values, $value->values);
 | |
|       } else {
 | |
|         $actual_values[$key] = $value;
 | |
|       }
 | |
|     }
 | |
|     return $this->computeShouldBreak($actual_values);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Tester si la clé spécifiée a été changée.
 | |
|    *
 | |
|    * Il faut d'abord avoir appelé l'une des méthodes {@link shouldBreakOn()}
 | |
|    * ou {@link shouldBreakOnKeys()}
 | |
|    */
 | |
|   function isChanged($key=null): bool {
 | |
|     if ($key !== null) return $this->changedKeys[$key];
 | |
|     foreach ($this->changedKeys as $changed) {
 | |
|       if ($changed) return true;
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Appeler {@link shouldBreakOn()}(...$values) puis retourner un tableau avec
 | |
|    * les valeurs non modifiées à false.
 | |
|    */
 | |
|   function getChanged(...$values): array {
 | |
|     $this->shouldBreakOn(...$values);
 | |
|     $values = [];
 | |
|     foreach ($this->values as $key => $value) {
 | |
|       $values[$key] = $this->changedKeys[$key]? $value: false;
 | |
|     }
 | |
|     return $values;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Appeler {@link shouldBreakOnKeys()}($array, ...$keys) puis retourner un
 | |
|    * tableau avec les valeurs non modifiées à false.
 | |
|    */
 | |
|   function getChangedKeys($array, ...$keys): array {
 | |
|     $this->shouldBreakOnKeys($array, ...$keys);
 | |
|     $values = [];
 | |
|     foreach ($this->values as $key => $value) {
 | |
|       $values[$key] = $this->changedKeys[$key]? $value: false;
 | |
|     }
 | |
|     return $values;
 | |
|   }
 | |
| }
 |