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;
 | 
						|
  }
 | 
						|
}
 |