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